support legacy ota

pull/140/head
cfig 1 year ago
parent 374aab8f28
commit 657b2d40ce
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

13
.gitignore vendored

@ -1,8 +1,19 @@
.idea
.gradle
build/
local.properties
.classpath
.project
.settings
__pycache__
aosp/bouncycastle/bcpkix/bin/
aosp/bouncycastle/bcprov/bin/
aosp/bouncycastle/bcpkix/build/
aosp/bouncycastle/bcprov/build/
aosp/apksigner/build/
aosp/boot_signer/build/
aosp/libavb1.1/build/
aosp/libavb1.2/build/
helper/build/
lazybox/build/
avbImpl/build/
bbootimg/build/

@ -0,0 +1,38 @@
plugins {
java
application
}
version = "1.0"
repositories {
mavenCentral()
}
application {
mainClass.set("com.android.signapk.SignApk")
}
dependencies {
implementation(project(":aosp:bouncycastle:bcpkix"))
implementation(project(":aosp:bouncycastle:bcprov"))
}
tasks {
jar {
manifest {
attributes["Implementation-Title"] = "AOSP ApkSigner"
attributes["Main-Class"] = "com.android.signapk.SignApk"
}
from(configurations.runtimeClasspath.get().map({ if (it.isDirectory) it else zipTree(it) }))
excludes.addAll(mutableSetOf("META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA"))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn(":aosp:bouncycastle:bcpkix:jar")
}
test {
testLogging {
showExceptions = true
showStackTraces = true
}
}
}

@ -0,0 +1,948 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.signapk;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.security.DigestOutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* HISTORICAL NOTE:
*
* Prior to the keylimepie release, SignApk ignored the signature
* algorithm specified in the certificate and always used SHA1withRSA.
*
* Starting with JB-MR2, the platform supports SHA256withRSA, so we use
* the signature algorithm in the certificate to select which to use
* (SHA256withRSA or SHA1withRSA). Also in JB-MR2, EC keys are supported.
*
* Because there are old keys still in use whose certificate actually
* says "MD5withRSA", we treat these as though they say "SHA1withRSA"
* for compatibility with older releases. This can be changed by
* altering the getAlgorithm() function below.
*/
/**
* Command line tool to sign JAR files (including APKs and OTA updates) in a way
* compatible with the mincrypt verifier, using EC or RSA keys and SHA1 or
* SHA-256 (see historical note).
*/
class SignApk {
private static final String CERT_SF_NAME = "META-INF/CERT.SF";
private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
private static final String CERT_SF_MULTI_NAME = "META-INF/CERT%d.SF";
private static final String CERT_SIG_MULTI_NAME = "META-INF/CERT%d.%s";
private static final String OTACERT_NAME = "META-INF/com/android/otacert";
private static Provider sBouncyCastleProvider;
// bitmasks for which hash algorithms we need the manifest to include.
private static final int USE_SHA1 = 1;
private static final int USE_SHA256 = 2;
/**
* Return one of USE_SHA1 or USE_SHA256 according to the signature
* algorithm specified in the cert.
*/
private static int getDigestAlgorithm(X509Certificate cert) {
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
if ("SHA1WITHRSA".equals(sigAlg) ||
"MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above.
return USE_SHA1;
} else if (sigAlg.startsWith("SHA256WITH")) {
return USE_SHA256;
} else {
throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
"\" in cert [" + cert.getSubjectDN());
}
}
/** Returns the expected signature algorithm for this key type. */
private static String getSignatureAlgorithm(X509Certificate cert) {
String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
if ("RSA".equalsIgnoreCase(keyType)) {
if (getDigestAlgorithm(cert) == USE_SHA256) {
return "SHA256withRSA";
} else {
return "SHA1withRSA";
}
} else if ("EC".equalsIgnoreCase(keyType)) {
return "SHA256withECDSA";
} else {
throw new IllegalArgumentException("unsupported key type: " + keyType);
}
}
// Files matching this pattern are not copied to the output.
private static Pattern stripPattern =
Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
private static X509Certificate readPublicKey(File file)
throws IOException, GeneralSecurityException {
FileInputStream input = new FileInputStream(file);
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(input);
} finally {
input.close();
}
}
/**
* Reads the password from stdin and returns it as a string.
*
* @param keyFile The file containing the private key. Used to prompt the user.
*/
private static String readPassword(File keyFile) {
// TODO: use Console.readPassword() when it's available.
System.out.print("Enter password for " + keyFile + " (password will not be hidden): ");
System.out.flush();
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
try {
return stdin.readLine();
} catch (IOException ex) {
return null;
}
}
/**
* Decrypt an encrypted PKCS#8 format private key.
*
* Based on ghstark's post on Aug 6, 2006 at
* http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949
*
* @param encryptedPrivateKey The raw data of the private key
* @param keyFile The file containing the private key
*/
private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)
throws GeneralSecurityException {
EncryptedPrivateKeyInfo epkInfo;
try {
epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey);
} catch (IOException ex) {
// Probably not an encrypted key.
return null;
}
char[] password = readPassword(keyFile).toCharArray();
SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName());
Key key = skFactory.generateSecret(new PBEKeySpec(password));
Cipher cipher = Cipher.getInstance(epkInfo.getAlgName());
cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters());
try {
return epkInfo.getKeySpec(cipher);
} catch (InvalidKeySpecException ex) {
System.err.println("signapk: Password for " + keyFile + " may be bad.");
throw ex;
}
}
/** Read a PKCS#8 format private key. */
private static PrivateKey readPrivateKey(File file)
throws IOException, GeneralSecurityException {
DataInputStream input = new DataInputStream(new FileInputStream(file));
try {
byte[] bytes = new byte[(int) file.length()];
input.read(bytes);
/* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
PKCS8EncodedKeySpec spec = decryptPrivateKey(bytes, file);
if (spec == null) {
spec = new PKCS8EncodedKeySpec(bytes);
}
/*
* Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
* OID and use that to construct a KeyFactory.
*/
ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
return KeyFactory.getInstance(algOid).generatePrivate(spec);
} finally {
input.close();
}
}
/**
* Add the hash(es) of every file to the manifest, creating it if
* necessary.
*/
private static Manifest addDigestsToManifest(JarFile jar, int hashes)
throws IOException, GeneralSecurityException {
Manifest input = jar.getManifest();
Manifest output = new Manifest();
Attributes main = output.getMainAttributes();
if (input != null) {
main.putAll(input.getMainAttributes());
} else {
main.putValue("Manifest-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
}
MessageDigest md_sha1 = null;
MessageDigest md_sha256 = null;
if ((hashes & USE_SHA1) != 0) {
md_sha1 = MessageDigest.getInstance("SHA1");
}
if ((hashes & USE_SHA256) != 0) {
md_sha256 = MessageDigest.getInstance("SHA256");
}
byte[] buffer = new byte[4096];
int num;
// We sort the input entries by name, and add them to the
// output manifest in sorted order. We expect that the output
// map will be deterministic.
TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
JarEntry entry = e.nextElement();
byName.put(entry.getName(), entry);
}
for (JarEntry entry: byName.values()) {
String name = entry.getName();
if (!entry.isDirectory() &&
(stripPattern == null || !stripPattern.matcher(name).matches())) {
InputStream data = jar.getInputStream(entry);
while ((num = data.read(buffer)) > 0) {
if (md_sha1 != null) md_sha1.update(buffer, 0, num);
if (md_sha256 != null) md_sha256.update(buffer, 0, num);
}
Attributes attr = null;
if (input != null) attr = input.getAttributes(name);
attr = attr != null ? new Attributes(attr) : new Attributes();
if (md_sha1 != null) {
attr.putValue("SHA1-Digest",
new String(Base64.encode(md_sha1.digest()), "ASCII"));
}
if (md_sha256 != null) {
attr.putValue("SHA-256-Digest",
new String(Base64.encode(md_sha256.digest()), "ASCII"));
}
output.getEntries().put(name, attr);
}
}
return output;
}
/**
* Add a copy of the public key to the archive; this should
* exactly match one of the files in
* /system/etc/security/otacerts.zip on the device. (The same
* cert can be extracted from the CERT.RSA file but this is much
* easier to get at.)
*/
private static void addOtacert(JarOutputStream outputJar,
File publicKeyFile,
long timestamp,
Manifest manifest,
int hash)
throws IOException, GeneralSecurityException {
MessageDigest md = MessageDigest.getInstance(hash == USE_SHA1 ? "SHA1" : "SHA256");
JarEntry je = new JarEntry(OTACERT_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
FileInputStream input = new FileInputStream(publicKeyFile);
byte[] b = new byte[4096];
int read;
while ((read = input.read(b)) != -1) {
outputJar.write(b, 0, read);
md.update(b, 0, read);
}
input.close();
Attributes attr = new Attributes();
attr.putValue(hash == USE_SHA1 ? "SHA1-Digest" : "SHA-256-Digest",
new String(Base64.encode(md.digest()), "ASCII"));
manifest.getEntries().put(OTACERT_NAME, attr);
}
/** Write to another stream and track how many bytes have been
* written.
*/
private static class CountOutputStream extends FilterOutputStream {
private int mCount;
public CountOutputStream(OutputStream out) {
super(out);
mCount = 0;
}
@Override
public void write(int b) throws IOException {
super.write(b);
mCount++;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
mCount += len;
}
public int size() {
return mCount;
}
}
/** Write a .SF file with a digest of the specified manifest. */
private static void writeSignatureFile(Manifest manifest, OutputStream out,
int hash)
throws IOException, GeneralSecurityException {
Manifest sf = new Manifest();
Attributes main = sf.getMainAttributes();
main.putValue("Signature-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
MessageDigest md = MessageDigest.getInstance(
hash == USE_SHA256 ? "SHA256" : "SHA1");
PrintStream print = new PrintStream(
new DigestOutputStream(new ByteArrayOutputStream(), md),
true, "UTF-8");
// Digest of the entire manifest
manifest.write(print);
print.flush();
main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest",
new String(Base64.encode(md.digest()), "ASCII"));
Map<String, Attributes> entries = manifest.getEntries();
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
// Digest of the manifest stanza for this entry.
print.print("Name: " + entry.getKey() + "\r\n");
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
}
print.print("\r\n");
print.flush();
Attributes sfAttr = new Attributes();
sfAttr.putValue(hash == USE_SHA256 ? "SHA-256-Digest" : "SHA1-Digest-Manifest",
new String(Base64.encode(md.digest()), "ASCII"));
sf.getEntries().put(entry.getKey(), sfAttr);
}
CountOutputStream cout = new CountOutputStream(out);
sf.write(cout);
// A bug in the java.util.jar implementation of Android platforms
// up to version 1.6 will cause a spurious IOException to be thrown
// if the length of the signature file is a multiple of 1024 bytes.
// As a workaround, add an extra CRLF in this case.
if ((cout.size() % 1024) == 0) {
cout.write('\r');
cout.write('\n');
}
}
/** Sign data and write the digital signature to 'out'. */
private static void writeSignatureBlock(
CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
OutputStream out)
throws IOException,
CertificateEncodingException,
OperatorCreationException,
CMSException {
ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(1);
certList.add(publicKey);
JcaCertStore certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey))
.setProvider(sBouncyCastleProvider)
.build(privateKey);
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder()
.setProvider(sBouncyCastleProvider)
.build())
.setDirectSignature(true)
.build(signer, publicKey));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(data, false);
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
DEROutputStream dos = new DEROutputStream(out);
dos.writeObject(asn1.readObject());
}
/**
* Copy all the files in a manifest from input to output. We set
* the modification times in the output to a fixed time, so as to
* reduce variation in the output file and make incremental OTAs
* more efficient.
*/
private static void copyFiles(Manifest manifest, JarFile in, JarOutputStream out,
long timestamp, int alignment) throws IOException {
byte[] buffer = new byte[4096];
int num;
Map<String, Attributes> entries = manifest.getEntries();
ArrayList<String> names = new ArrayList<String>(entries.keySet());
Collections.sort(names);
boolean firstEntry = true;
long offset = 0L;
// We do the copy in two passes -- first copying all the
// entries that are STORED, then copying all the entries that
// have any other compression flag (which in practice means
// DEFLATED). This groups all the stored entries together at
// the start of the file and makes it easier to do alignment
// on them (since only stored entries are aligned).
for (String name : names) {
JarEntry inEntry = in.getJarEntry(name);
JarEntry outEntry = null;
if (inEntry.getMethod() != JarEntry.STORED) continue;
// Preserve the STORED method of the input entry.
outEntry = new JarEntry(inEntry);
outEntry.setTime(timestamp);
// 'offset' is the offset into the file at which we expect
// the file data to begin. This is the value we need to
// make a multiple of 'alignement'.
offset += JarFile.LOCHDR + outEntry.getName().length();
if (firstEntry) {
// The first entry in a jar file has an extra field of
// four bytes that you can't get rid of; any extra
// data you specify in the JarEntry is appended to
// these forced four bytes. This is JAR_MAGIC in
// JarOutputStream; the bytes are 0xfeca0000.
offset += 4;
firstEntry = false;
}
if (alignment > 0 && (offset % alignment != 0)) {
// Set the "extra data" of the entry to between 1 and
// alignment-1 bytes, to make the file data begin at
// an aligned offset.
int needed = alignment - (int)(offset % alignment);
outEntry.setExtra(new byte[needed]);
offset += needed;
}
out.putNextEntry(outEntry);
InputStream data = in.getInputStream(inEntry);
while ((num = data.read(buffer)) > 0) {
out.write(buffer, 0, num);
offset += num;
}
out.flush();
}
// Copy all the non-STORED entries. We don't attempt to
// maintain the 'offset' variable past this point; we don't do
// alignment on these entries.
for (String name : names) {
JarEntry inEntry = in.getJarEntry(name);
JarEntry outEntry = null;
if (inEntry.getMethod() == JarEntry.STORED) continue;
// Create a new entry so that the compressed len is recomputed.
outEntry = new JarEntry(name);
outEntry.setTime(timestamp);
out.putNextEntry(outEntry);
InputStream data = in.getInputStream(inEntry);
while ((num = data.read(buffer)) > 0) {
out.write(buffer, 0, num);
}
out.flush();
}
}
private static class WholeFileSignerOutputStream extends FilterOutputStream {
private boolean closing = false;
private ByteArrayOutputStream footer = new ByteArrayOutputStream();
private OutputStream tee;
public WholeFileSignerOutputStream(OutputStream out, OutputStream tee) {
super(out);
this.tee = tee;
}
public void notifyClosing() {
closing = true;
}
public void finish() throws IOException {
closing = false;
byte[] data = footer.toByteArray();
if (data.length < 2)
throw new IOException("Less than two bytes written to footer");
write(data, 0, data.length - 2);
}
public byte[] getTail() {
return footer.toByteArray();
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (closing) {
// if the jar is about to close, save the footer that will be written
footer.write(b, off, len);
}
else {
// write to both output streams. out is the CMSTypedData signer and tee is the file.
out.write(b, off, len);
tee.write(b, off, len);
}
}
@Override
public void write(int b) throws IOException {
if (closing) {
// if the jar is about to close, save the footer that will be written
footer.write(b);
}
else {
// write to both output streams. out is the CMSTypedData signer and tee is the file.
out.write(b);
tee.write(b);
}
}
}
private static class CMSSigner implements CMSTypedData {
private JarFile inputJar;
private File publicKeyFile;
private X509Certificate publicKey;
private PrivateKey privateKey;
private String outputFile;
private OutputStream outputStream;
private final ASN1ObjectIdentifier type;
private WholeFileSignerOutputStream signer;
public CMSSigner(JarFile inputJar, File publicKeyFile,
X509Certificate publicKey, PrivateKey privateKey,
OutputStream outputStream) {
this.inputJar = inputJar;
this.publicKeyFile = publicKeyFile;
this.publicKey = publicKey;
this.privateKey = privateKey;
this.outputStream = outputStream;
this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
}
public Object getContent() {
throw new UnsupportedOperationException();
}
public ASN1ObjectIdentifier getContentType() {
return type;
}
public void write(OutputStream out) throws IOException {
try {
signer = new WholeFileSignerOutputStream(out, outputStream);
JarOutputStream outputJar = new JarOutputStream(signer);
int hash = getDigestAlgorithm(publicKey);
// Assume the certificate is valid for at least an hour.
long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
Manifest manifest = addDigestsToManifest(inputJar, hash);
copyFiles(manifest, inputJar, outputJar, timestamp, 0);
addOtacert(outputJar, publicKeyFile, timestamp, manifest, hash);
signFile(manifest, inputJar,
new X509Certificate[]{ publicKey },
new PrivateKey[]{ privateKey },
outputJar);
signer.notifyClosing();
outputJar.close();
signer.finish();
}
catch (Exception e) {
throw new IOException(e);
}
}
public void writeSignatureBlock(ByteArrayOutputStream temp)
throws IOException,
CertificateEncodingException,
OperatorCreationException,
CMSException {
SignApk.writeSignatureBlock(this, publicKey, privateKey, temp);
}
public WholeFileSignerOutputStream getSigner() {
return signer;
}
}
private static void signWholeFile(JarFile inputJar, File publicKeyFile,
X509Certificate publicKey, PrivateKey privateKey,
OutputStream outputStream) throws Exception {
CMSSigner cmsOut = new CMSSigner(inputJar, publicKeyFile,
publicKey, privateKey, outputStream);
ByteArrayOutputStream temp = new ByteArrayOutputStream();
// put a readable message and a null char at the start of the
// archive comment, so that tools that display the comment
// (hopefully) show something sensible.
// TODO: anything more useful we can put in this message?
byte[] message = "signed by SignApk".getBytes("UTF-8");
temp.write(message);
temp.write(0);
cmsOut.writeSignatureBlock(temp);
byte[] zipData = cmsOut.getSigner().getTail();
// For a zip with no archive comment, the
// end-of-central-directory record will be 22 bytes long, so
// we expect to find the EOCD marker 22 bytes from the end.
if (zipData[zipData.length-22] != 0x50 ||
zipData[zipData.length-21] != 0x4b ||
zipData[zipData.length-20] != 0x05 ||
zipData[zipData.length-19] != 0x06) {
throw new IllegalArgumentException("zip data already has an archive comment");
}
int total_size = temp.size() + 6;
if (total_size > 0xffff) {
throw new IllegalArgumentException("signature is too big for ZIP file comment");
}
// signature starts this many bytes from the end of the file
int signature_start = total_size - message.length - 1;
temp.write(signature_start & 0xff);
temp.write((signature_start >> 8) & 0xff);
// Why the 0xff bytes? In a zip file with no archive comment,
// bytes [-6:-2] of the file are the little-endian offset from
// the start of the file to the central directory. So for the
// two high bytes to be 0xff 0xff, the archive would have to
// be nearly 4GB in size. So it's unlikely that a real
// commentless archive would have 0xffs here, and lets us tell
// an old signed archive from a new one.
temp.write(0xff);
temp.write(0xff);
temp.write(total_size & 0xff);
temp.write((total_size >> 8) & 0xff);
temp.flush();
// Signature verification checks that the EOCD header is the
// last such sequence in the file (to avoid minzip finding a
// fake EOCD appended after the signature in its scan). The
// odds of producing this sequence by chance are very low, but
// let's catch it here if it does.
byte[] b = temp.toByteArray();
for (int i = 0; i < b.length-3; ++i) {
if (b[i] == 0x50 && b[i+1] == 0x4b && b[i+2] == 0x05 && b[i+3] == 0x06) {
throw new IllegalArgumentException("found spurious EOCD header at " + i);
}
}
outputStream.write(total_size & 0xff);
outputStream.write((total_size >> 8) & 0xff);
temp.writeTo(outputStream);
}
private static void signFile(Manifest manifest, JarFile inputJar,
X509Certificate[] publicKey, PrivateKey[] privateKey,
JarOutputStream outputJar)
throws Exception {
// Assume the certificate is valid for at least an hour.
long timestamp = publicKey[0].getNotBefore().getTime() + 3600L * 1000;
// MANIFEST.MF
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
manifest.write(outputJar);
int numKeys = publicKey.length;
for (int k = 0; k < numKeys; ++k) {
// CERT.SF / CERT#.SF
je = new JarEntry(numKeys == 1 ? CERT_SF_NAME :
(String.format(CERT_SF_MULTI_NAME, k)));
je.setTime(timestamp);
outputJar.putNextEntry(je);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeSignatureFile(manifest, baos, getDigestAlgorithm(publicKey[k]));
byte[] signedData = baos.toByteArray();
outputJar.write(signedData);
// CERT.{EC,RSA} / CERT#.{EC,RSA}
final String keyType = publicKey[k].getPublicKey().getAlgorithm();
je = new JarEntry(numKeys == 1 ?
(String.format(CERT_SIG_NAME, keyType)) :
(String.format(CERT_SIG_MULTI_NAME, k, keyType)));
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureBlock(new CMSProcessableByteArray(signedData),
publicKey[k], privateKey[k], outputJar);
}
}
/**
* Tries to load a JSE Provider by class name. This is for custom PrivateKey
* types that might be stored in PKCS#11-like storage.
*/
private static void loadProviderIfNecessary(String providerClassName) {
if (providerClassName == null) {
return;
}
final Class<?> klass;
try {
final ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
if (sysLoader != null) {
klass = sysLoader.loadClass(providerClassName);
} else {
klass = Class.forName(providerClassName);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.exit(1);
return;
}
Constructor<?> constructor = null;
for (Constructor<?> c : klass.getConstructors()) {
if (c.getParameterTypes().length == 0) {
constructor = c;
break;
}
}
if (constructor == null) {
System.err.println("No zero-arg constructor found for " + providerClassName);
System.exit(1);
return;
}
final Object o;
try {
o = constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
return;
}
if (!(o instanceof Provider)) {
System.err.println("Not a Provider class: " + providerClassName);
System.exit(1);
}
Security.insertProviderAt((Provider) o, 1);
}
private static void usage() {
System.err.println("Usage: signapk [-w] " +
"[-a <alignment>] " +
"[-providerClass <className>] " +
"publickey.x509[.pem] privatekey.pk8 " +
"[publickey2.x509[.pem] privatekey2.pk8 ...] " +
"input.jar output.jar");
System.exit(2);
}
public static void main(String[] args) {
if (args.length < 4) usage();
sBouncyCastleProvider = new BouncyCastleProvider();
Security.addProvider(sBouncyCastleProvider);
boolean signWholeFile = false;
String providerClass = null;
String providerArg = null;
int alignment = 4;
int argstart = 0;
while (argstart < args.length && args[argstart].startsWith("-")) {
if ("-w".equals(args[argstart])) {
signWholeFile = true;
++argstart;
} else if ("-providerClass".equals(args[argstart])) {
if (argstart + 1 >= args.length) {
usage();
}
providerClass = args[++argstart];
++argstart;
} else if ("-a".equals(args[argstart])) {
alignment = Integer.parseInt(args[++argstart]);
++argstart;
} else {
usage();
}
}
if ((args.length - argstart) % 2 == 1) usage();
int numKeys = ((args.length - argstart) / 2) - 1;
if (signWholeFile && numKeys > 1) {
System.err.println("Only one key may be used with -w.");
System.exit(2);
}
loadProviderIfNecessary(providerClass);
String inputFilename = args[args.length-2];
String outputFilename = args[args.length-1];
JarFile inputJar = null;
FileOutputStream outputFile = null;
int hashes = 0;
try {
File firstPublicKeyFile = new File(args[argstart+0]);
X509Certificate[] publicKey = new X509Certificate[numKeys];
try {
for (int i = 0; i < numKeys; ++i) {
int argNum = argstart + i*2;
publicKey[i] = readPublicKey(new File(args[argNum]));
hashes |= getDigestAlgorithm(publicKey[i]);
}
} catch (IllegalArgumentException e) {
System.err.println(e);
System.exit(1);
}
// Set the ZIP file timestamp to the starting valid time
// of the 0th certificate plus one hour (to match what
// we've historically done).
long timestamp = publicKey[0].getNotBefore().getTime() + 3600L * 1000;
PrivateKey[] privateKey = new PrivateKey[numKeys];
for (int i = 0; i < numKeys; ++i) {
int argNum = argstart + i*2 + 1;
privateKey[i] = readPrivateKey(new File(args[argNum]));
}
inputJar = new JarFile(new File(inputFilename), false); // Don't verify.
outputFile = new FileOutputStream(outputFilename);
if (signWholeFile) {
SignApk.signWholeFile(inputJar, firstPublicKeyFile,
publicKey[0], privateKey[0], outputFile);
} else {
JarOutputStream outputJar = new JarOutputStream(outputFile);
// For signing .apks, use the maximum compression to make
// them as small as possible (since they live forever on
// the system partition). For OTA packages, use the
// default compression level, which is much much faster
// and produces output that is only a tiny bit larger
// (~0.1% on full OTA packages I tested).
outputJar.setLevel(9);
Manifest manifest = addDigestsToManifest(inputJar, hashes);
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
signFile(manifest, inputJar, publicKey, privateKey, outputJar);
outputJar.close();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
try {
if (inputJar != null) inputJar.close();
if (outputFile != null) outputFile.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
}

@ -0,0 +1,39 @@
// Copyright 2023 yuyezhong@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
plugins {
`java-library`
}
repositories {
mavenCentral()
}
dependencies {
implementation(project(":aosp:bouncycastle:bcprov"))
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
tasks {
jar {
from(configurations.runtimeClasspath.get().map({ if (it.isDirectory) it else zipTree(it) }))
excludes.addAll(mutableSetOf("META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA"))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn(":aosp:bouncycastle:bcprov:jar")
}
}

@ -0,0 +1,357 @@
package org.bouncycastle.cert;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.Holder;
import org.bouncycastle.asn1.x509.IssuerSerial;
import org.bouncycastle.asn1.x509.ObjectDigestInfo;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Selector;
/**
* The Holder object.
*
* <pre>
* Holder ::= SEQUENCE {
* baseCertificateID [0] IssuerSerial OPTIONAL,
* -- the issuer and serial number of
* -- the holder's Public Key Certificate
* entityName [1] GeneralNames OPTIONAL,
* -- the name of the claimant or role
* objectDigestInfo [2] ObjectDigestInfo OPTIONAL
* -- used to directly authenticate the holder,
* -- for example, an executable
* }
* </pre>
* <p>
* <b>Note:</b> If objectDigestInfo comparisons are to be carried out the static
* method setDigestCalculatorProvider <b>must</b> be called once to configure the class
* to do the necessary calculations.
* </p>
*/
public class AttributeCertificateHolder
implements Selector
{
private static DigestCalculatorProvider digestCalculatorProvider;
final Holder holder;
AttributeCertificateHolder(ASN1Sequence seq)
{
holder = Holder.getInstance(seq);
}
public AttributeCertificateHolder(X500Name issuerName,
BigInteger serialNumber)
{
holder = new Holder(new IssuerSerial(
new GeneralNames(new GeneralName(issuerName)),
new ASN1Integer(serialNumber)));
}
public AttributeCertificateHolder(X509CertificateHolder cert)
{
holder = new Holder(new IssuerSerial(generateGeneralNames(cert.getIssuer()),
new ASN1Integer(cert.getSerialNumber())));
}
public AttributeCertificateHolder(X500Name principal)
{
holder = new Holder(generateGeneralNames(principal));
}
/**
* Constructs a holder for v2 attribute certificates with a hash value for
* some type of object.
* <p>
* <code>digestedObjectType</code> can be one of the following:
* <ul>
* <li>0 - publicKey - A hash of the public key of the holder must be
* passed.
* <li>1 - publicKeyCert - A hash of the public key certificate of the
* holder must be passed.
* <li>2 - otherObjectDigest - A hash of some other object type must be
* passed. <code>otherObjectTypeID</code> must not be empty.
* </ul>
* <p>
* This cannot be used if a v1 attribute certificate is used.
*
* @param digestedObjectType The digest object type.
* @param digestAlgorithm The algorithm identifier for the hash.
* @param otherObjectTypeID The object type ID if
* <code>digestedObjectType</code> is
* <code>otherObjectDigest</code>.
* @param objectDigest The hash value.
*/
public AttributeCertificateHolder(int digestedObjectType,
ASN1ObjectIdentifier digestAlgorithm, ASN1ObjectIdentifier otherObjectTypeID, byte[] objectDigest)
{
holder = new Holder(new ObjectDigestInfo(digestedObjectType,
otherObjectTypeID, new AlgorithmIdentifier(digestAlgorithm), Arrays
.clone(objectDigest)));
}
/**
* Returns the digest object type if an object digest info is used.
* <p>
* <ul>
* <li>0 - publicKey - A hash of the public key of the holder must be
* passed.
* <li>1 - publicKeyCert - A hash of the public key certificate of the
* holder must be passed.
* <li>2 - otherObjectDigest - A hash of some other object type must be
* passed. <code>otherObjectTypeID</code> must not be empty.
* </ul>
*
* @return The digest object type or -1 if no object digest info is set.
*/
public int getDigestedObjectType()
{
if (holder.getObjectDigestInfo() != null)
{
return holder.getObjectDigestInfo().getDigestedObjectType()
.getValue().intValue();
}
return -1;
}
/**
* Returns algorithm identifier for the digest used if ObjectDigestInfo is present.
*
* @return digest AlgorithmIdentifier or <code>null</code> if ObjectDigestInfo is absent.
*/
public AlgorithmIdentifier getDigestAlgorithm()
{
if (holder.getObjectDigestInfo() != null)
{
return holder.getObjectDigestInfo().getDigestAlgorithm();
}
return null;
}
/**
* Returns the hash if an object digest info is used.
*
* @return The hash or <code>null</code> if ObjectDigestInfo is absent.
*/
public byte[] getObjectDigest()
{
if (holder.getObjectDigestInfo() != null)
{
return holder.getObjectDigestInfo().getObjectDigest().getBytes();
}
return null;
}
/**
* Returns the digest algorithm ID if an object digest info is used.
*
* @return The digest algorithm ID or <code>null</code> if no object
* digest info is set.
*/
public ASN1ObjectIdentifier getOtherObjectTypeID()
{
if (holder.getObjectDigestInfo() != null)
{
new ASN1ObjectIdentifier(holder.getObjectDigestInfo().getOtherObjectTypeID().getId());
}
return null;
}
private GeneralNames generateGeneralNames(X500Name principal)
{
return new GeneralNames(new GeneralName(principal));
}
private boolean matchesDN(X500Name subject, GeneralNames targets)
{
GeneralName[] names = targets.getNames();
for (int i = 0; i != names.length; i++)
{
GeneralName gn = names[i];
if (gn.getTagNo() == GeneralName.directoryName)
{
if (X500Name.getInstance(gn.getName()).equals(subject))
{
return true;
}
}
}
return false;
}
private X500Name[] getPrincipals(GeneralName[] names)
{
List l = new ArrayList(names.length);
for (int i = 0; i != names.length; i++)
{
if (names[i].getTagNo() == GeneralName.directoryName)
{
l.add(X500Name.getInstance(names[i].getName()));
}
}
return (X500Name[])l.toArray(new X500Name[l.size()]);
}
/**
* Return any principal objects inside the attribute certificate holder
* entity names field.
*
* @return an array of Principal objects (usually X500Principal), null if no
* entity names field is set.
*/
public X500Name[] getEntityNames()
{
if (holder.getEntityName() != null)
{
return getPrincipals(holder.getEntityName().getNames());
}
return null;
}
/**
* Return the principals associated with the issuer attached to this holder
*
* @return an array of principals, null if no BaseCertificateID is set.
*/
public X500Name[] getIssuer()
{
if (holder.getBaseCertificateID() != null)
{
return getPrincipals(holder.getBaseCertificateID().getIssuer().getNames());
}
return null;
}
/**
* Return the serial number associated with the issuer attached to this
* holder.
*
* @return the certificate serial number, null if no BaseCertificateID is
* set.
*/
public BigInteger getSerialNumber()
{
if (holder.getBaseCertificateID() != null)
{
return holder.getBaseCertificateID().getSerial().getValue();
}
return null;
}
public Object clone()
{
return new AttributeCertificateHolder((ASN1Sequence)holder.toASN1Primitive());
}
public boolean match(Object obj)
{
if (!(obj instanceof X509CertificateHolder))
{
return false;
}
X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
if (holder.getBaseCertificateID() != null)
{
return holder.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
&& matchesDN(x509Cert.getIssuer(), holder.getBaseCertificateID().getIssuer());
}
if (holder.getEntityName() != null)
{
if (matchesDN(x509Cert.getSubject(),
holder.getEntityName()))
{
return true;
}
}
if (holder.getObjectDigestInfo() != null)
{
try
{
DigestCalculator digCalc = digestCalculatorProvider.get(holder.getObjectDigestInfo().getDigestAlgorithm());
OutputStream digOut = digCalc.getOutputStream();
switch (getDigestedObjectType())
{
case ObjectDigestInfo.publicKey:
// TODO: DSA Dss-parms
digOut.write(x509Cert.getSubjectPublicKeyInfo().getEncoded());
break;
case ObjectDigestInfo.publicKeyCert:
digOut.write(x509Cert.getEncoded());
break;
}
digOut.close();
if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
{
return false;
}
}
catch (Exception e)
{
return false;
}
}
return false;
}
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (!(obj instanceof AttributeCertificateHolder))
{
return false;
}
AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
return this.holder.equals(other.holder);
}
public int hashCode()
{
return this.holder.hashCode();
}
/**
* Set a digest calculator provider to be used if matches are attempted using
* ObjectDigestInfo,
*
* @param digCalcProvider a provider of digest calculators.
*/
public static void setDigestCalculatorProvider(DigestCalculatorProvider digCalcProvider)
{
digestCalculatorProvider = digCalcProvider;
}
}

@ -0,0 +1,147 @@
package org.bouncycastle.cert;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AttCertIssuer;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.V2Form;
import org.bouncycastle.util.Selector;
/**
* Carrying class for an attribute certificate issuer.
*/
public class AttributeCertificateIssuer
implements Selector
{
final ASN1Encodable form;
/**
* Set the issuer directly with the ASN.1 structure.
*
* @param issuer The issuer
*/
public AttributeCertificateIssuer(AttCertIssuer issuer)
{
form = issuer.getIssuer();
}
public AttributeCertificateIssuer(X500Name principal)
{
form = new V2Form(new GeneralNames(new GeneralName(principal)));
}
public X500Name[] getNames()
{
GeneralNames name;
if (form instanceof V2Form)
{
name = ((V2Form)form).getIssuerName();
}
else
{
name = (GeneralNames)form;
}
GeneralName[] names = name.getNames();
List l = new ArrayList(names.length);
for (int i = 0; i != names.length; i++)
{
if (names[i].getTagNo() == GeneralName.directoryName)
{
l.add(X500Name.getInstance(names[i].getName()));
}
}
return (X500Name[])l.toArray(new X500Name[l.size()]);
}
private boolean matchesDN(X500Name subject, GeneralNames targets)
{
GeneralName[] names = targets.getNames();
for (int i = 0; i != names.length; i++)
{
GeneralName gn = names[i];
if (gn.getTagNo() == GeneralName.directoryName)
{
if (X500Name.getInstance(gn.getName()).equals(subject))
{
return true;
}
}
}
return false;
}
public Object clone()
{
return new AttributeCertificateIssuer(AttCertIssuer.getInstance(form));
}
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (!(obj instanceof AttributeCertificateIssuer))
{
return false;
}
AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj;
return this.form.equals(other.form);
}
public int hashCode()
{
return this.form.hashCode();
}
public boolean match(Object obj)
{
if (!(obj instanceof X509CertificateHolder))
{
return false;
}
X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
if (form instanceof V2Form)
{
V2Form issuer = (V2Form)form;
if (issuer.getBaseCertificateID() != null)
{
return issuer.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
&& matchesDN(x509Cert.getIssuer(), issuer.getBaseCertificateID().getIssuer());
}
GeneralNames name = issuer.getIssuerName();
if (matchesDN(x509Cert.getSubject(), name))
{
return true;
}
}
else
{
GeneralNames name = (GeneralNames)form;
if (matchesDN(x509Cert.getSubject(), name))
{
return true;
}
}
return false;
}
}

@ -0,0 +1,27 @@
package org.bouncycastle.cert;
/**
* General checked Exception thrown in the cert package and its sub-packages.
*/
public class CertException
extends Exception
{
private Throwable cause;
public CertException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public CertException(String msg)
{
super(msg);
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,29 @@
package org.bouncycastle.cert;
import java.io.IOException;
/**
* General IOException thrown in the cert package and its sub-packages.
*/
public class CertIOException
extends IOException
{
private Throwable cause;
public CertIOException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public CertIOException(String msg)
{
super(msg);
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,244 @@
package org.bouncycastle.cert;
import java.io.IOException;
import java.io.OutputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.TBSCertList;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.operator.ContentSigner;
class CertUtils
{
private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
private static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
static X509CertificateHolder generateFullCert(ContentSigner signer, TBSCertificate tbsCert)
{
try
{
return new X509CertificateHolder(generateStructure(tbsCert, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCert)));
}
catch (IOException e)
{
throw new IllegalStateException("cannot produce certificate signature");
}
}
static X509AttributeCertificateHolder generateFullAttrCert(ContentSigner signer, AttributeCertificateInfo attrInfo)
{
try
{
return new X509AttributeCertificateHolder(generateAttrStructure(attrInfo, signer.getAlgorithmIdentifier(), generateSig(signer, attrInfo)));
}
catch (IOException e)
{
throw new IllegalStateException("cannot produce attribute certificate signature");
}
}
static X509CRLHolder generateFullCRL(ContentSigner signer, TBSCertList tbsCertList)
{
try
{
return new X509CRLHolder(generateCRLStructure(tbsCertList, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCertList)));
}
catch (IOException e)
{
throw new IllegalStateException("cannot produce certificate signature");
}
}
private static byte[] generateSig(ContentSigner signer, ASN1Encodable tbsObj)
throws IOException
{
OutputStream sOut = signer.getOutputStream();
DEROutputStream dOut = new DEROutputStream(sOut);
dOut.writeObject(tbsObj);
sOut.close();
return signer.getSignature();
}
private static Certificate generateStructure(TBSCertificate tbsCert, AlgorithmIdentifier sigAlgId, byte[] signature)
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(tbsCert);
v.add(sigAlgId);
v.add(new DERBitString(signature));
return Certificate.getInstance(new DERSequence(v));
}
private static AttributeCertificate generateAttrStructure(AttributeCertificateInfo attrInfo, AlgorithmIdentifier sigAlgId, byte[] signature)
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attrInfo);
v.add(sigAlgId);
v.add(new DERBitString(signature));
return AttributeCertificate.getInstance(new DERSequence(v));
}
private static CertificateList generateCRLStructure(TBSCertList tbsCertList, AlgorithmIdentifier sigAlgId, byte[] signature)
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(tbsCertList);
v.add(sigAlgId);
v.add(new DERBitString(signature));
return CertificateList.getInstance(new DERSequence(v));
}
static Set getCriticalExtensionOIDs(Extensions extensions)
{
if (extensions == null)
{
return EMPTY_SET;
}
return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs())));
}
static Set getNonCriticalExtensionOIDs(Extensions extensions)
{
if (extensions == null)
{
return EMPTY_SET;
}
// TODO: should probably produce a set that imposes correct ordering
return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
}
static List getExtensionOIDs(Extensions extensions)
{
if (extensions == null)
{
return EMPTY_LIST;
}
return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs()));
}
static void addExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value)
throws CertIOException
{
try
{
extGenerator.addExtension(oid, isCritical, value);
}
catch (IOException e)
{
throw new CertIOException("cannot encode extension: " + e.getMessage(), e);
}
}
static DERBitString booleanToBitString(boolean[] id)
{
byte[] bytes = new byte[(id.length + 7) / 8];
for (int i = 0; i != id.length; i++)
{
bytes[i / 8] |= (id[i]) ? (1 << ((7 - (i % 8)))) : 0;
}
int pad = id.length % 8;
if (pad == 0)
{
return new DERBitString(bytes);
}
else
{
return new DERBitString(bytes, 8 - pad);
}
}
static boolean[] bitStringToBoolean(DERBitString bitString)
{
if (bitString != null)
{
byte[] bytes = bitString.getBytes();
boolean[] boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
for (int i = 0; i != boolId.length; i++)
{
boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
}
return boolId;
}
return null;
}
static Date recoverDate(ASN1GeneralizedTime time)
{
try
{
return time.getDate();
}
catch (ParseException e)
{
throw new IllegalStateException("unable to recover date: " + e.getMessage());
}
}
static boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
{
if (!id1.getAlgorithm().equals(id2.getAlgorithm()))
{
return false;
}
if (id1.getParameters() == null)
{
if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
{
return false;
}
return true;
}
if (id2.getParameters() == null)
{
if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
{
return false;
}
return true;
}
return id1.getParameters().equals(id2.getParameters());
}
}

@ -0,0 +1,366 @@
package org.bouncycastle.cert;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.AttCertValidityPeriod;
import org.bouncycastle.asn1.x509.Attribute;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
/**
* Holding class for an X.509 AttributeCertificate structure.
*/
public class X509AttributeCertificateHolder
{
private static Attribute[] EMPTY_ARRAY = new Attribute[0];
private AttributeCertificate attrCert;
private Extensions extensions;
private static AttributeCertificate parseBytes(byte[] certEncoding)
throws IOException
{
try
{
return AttributeCertificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
}
catch (ClassCastException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
catch (IllegalArgumentException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
}
/**
* Create a X509AttributeCertificateHolder from the passed in bytes.
*
* @param certEncoding BER/DER encoding of the certificate.
* @throws IOException in the event of corrupted data, or an incorrect structure.
*/
public X509AttributeCertificateHolder(byte[] certEncoding)
throws IOException
{
this(parseBytes(certEncoding));
}
/**
* Create a X509AttributeCertificateHolder from the passed in ASN.1 structure.
*
* @param attrCert an ASN.1 AttributeCertificate structure.
*/
public X509AttributeCertificateHolder(AttributeCertificate attrCert)
{
this.attrCert = attrCert;
this.extensions = attrCert.getAcinfo().getExtensions();
}
/**
* Return the ASN.1 encoding of this holder's attribute certificate.
*
* @return a DER encoded byte array.
* @throws IOException if an encoding cannot be generated.
*/
public byte[] getEncoded()
throws IOException
{
return attrCert.getEncoded();
}
public int getVersion()
{
return attrCert.getAcinfo().getVersion().getValue().intValue() + 1;
}
/**
* Return the serial number of this attribute certificate.
*
* @return the serial number.
*/
public BigInteger getSerialNumber()
{
return attrCert.getAcinfo().getSerialNumber().getValue();
}
/**
* Return the holder details for this attribute certificate.
*
* @return this attribute certificate's holder structure.
*/
public AttributeCertificateHolder getHolder()
{
return new AttributeCertificateHolder((ASN1Sequence)attrCert.getAcinfo().getHolder().toASN1Primitive());
}
/**
* Return the issuer details for this attribute certificate.
*
* @return this attribute certificate's issuer structure,
*/
public AttributeCertificateIssuer getIssuer()
{
return new AttributeCertificateIssuer(attrCert.getAcinfo().getIssuer());
}
/**
* Return the date before which this attribute certificate is not valid.
*
* @return the start date for the attribute certificate's validity period.
*/
public Date getNotBefore()
{
return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotBeforeTime());
}
/**
* Return the date after which this attribute certificate is not valid.
*
* @return the final date for the attribute certificate's validity period.
*/
public Date getNotAfter()
{
return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotAfterTime());
}
/**
* Return the attributes, if any associated with this request.
*
* @return an array of Attribute, zero length if none present.
*/
public Attribute[] getAttributes()
{
ASN1Sequence seq = attrCert.getAcinfo().getAttributes();
Attribute[] attrs = new Attribute[seq.size()];
for (int i = 0; i != seq.size(); i++)
{
attrs[i] = Attribute.getInstance(seq.getObjectAt(i));
}
return attrs;
}
/**
* Return an array of attributes matching the passed in type OID.
*
* @param type the type of the attribute being looked for.
* @return an array of Attribute of the requested type, zero length if none present.
*/
public Attribute[] getAttributes(ASN1ObjectIdentifier type)
{
ASN1Sequence seq = attrCert.getAcinfo().getAttributes();
List list = new ArrayList();
for (int i = 0; i != seq.size(); i++)
{
Attribute attr = Attribute.getInstance(seq.getObjectAt(i));
if (attr.getAttrType().equals(type))
{
list.add(attr);
}
}
if (list.size() == 0)
{
return EMPTY_ARRAY;
}
return (Attribute[])list.toArray(new Attribute[list.size()]);
}
/**
* Return whether or not the holder's attribute certificate contains extensions.
*
* @return true if extension are present, false otherwise.
*/
public boolean hasExtensions()
{
return extensions != null;
}
/**
* Look up the extension associated with the passed in OID.
*
* @param oid the OID of the extension of interest.
*
* @return the extension if present, null otherwise.
*/
public Extension getExtension(ASN1ObjectIdentifier oid)
{
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
/**
* Return the extensions block associated with this certificate if there is one.
*
* @return the extensions block, null otherwise.
*/
public Extensions getExtensions()
{
return extensions;
}
/**
* Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
* extensions contained in this holder's attribute certificate.
*
* @return a list of extension OIDs.
*/
public List getExtensionOIDs()
{
return CertUtils.getExtensionOIDs(extensions);
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* critical extensions contained in this holder's attribute certificate.
*
* @return a set of critical extension OIDs.
*/
public Set getCriticalExtensionOIDs()
{
return CertUtils.getCriticalExtensionOIDs(extensions);
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* non-critical extensions contained in this holder's attribute certificate.
*
* @return a set of non-critical extension OIDs.
*/
public Set getNonCriticalExtensionOIDs()
{
return CertUtils.getNonCriticalExtensionOIDs(extensions);
}
public boolean[] getIssuerUniqueID()
{
return CertUtils.bitStringToBoolean(attrCert.getAcinfo().getIssuerUniqueID());
}
/**
* Return the details of the signature algorithm used to create this attribute certificate.
*
* @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
*/
public AlgorithmIdentifier getSignatureAlgorithm()
{
return attrCert.getSignatureAlgorithm();
}
/**
* Return the bytes making up the signature associated with this attribute certificate.
*
* @return the attribute certificate signature bytes.
*/
public byte[] getSignature()
{
return attrCert.getSignatureValue().getBytes();
}
/**
* Return the underlying ASN.1 structure for the attribute certificate in this holder.
*
* @return a AttributeCertificate object.
*/
public AttributeCertificate toASN1Structure()
{
return attrCert;
}
/**
* Return whether or not this attribute certificate is valid on a particular date.
*
* @param date the date of interest.
* @return true if the attribute certificate is valid, false otherwise.
*/
public boolean isValidOn(Date date)
{
AttCertValidityPeriod certValidityPeriod = attrCert.getAcinfo().getAttrCertValidityPeriod();
return !date.before(CertUtils.recoverDate(certValidityPeriod.getNotBeforeTime())) && !date.after(CertUtils.recoverDate(certValidityPeriod.getNotAfterTime()));
}
/**
* Validate the signature on the attribute certificate in this holder.
*
* @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
* @return true if the signature is valid, false otherwise.
* @throws CertException if the signature cannot be processed or is inappropriate.
*/
public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
throws CertException
{
AttributeCertificateInfo acinfo = attrCert.getAcinfo();
if (!CertUtils.isAlgIdEqual(acinfo.getSignature(), attrCert.getSignatureAlgorithm()))
{
throw new CertException("signature invalid - algorithm identifier mismatch");
}
ContentVerifier verifier;
try
{
verifier = verifierProvider.get((acinfo.getSignature()));
OutputStream sOut = verifier.getOutputStream();
DEROutputStream dOut = new DEROutputStream(sOut);
dOut.writeObject(acinfo);
sOut.close();
}
catch (Exception e)
{
throw new CertException("unable to process signature: " + e.getMessage(), e);
}
return verifier.verify(attrCert.getSignatureValue().getBytes());
}
public boolean equals(
Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof X509AttributeCertificateHolder))
{
return false;
}
X509AttributeCertificateHolder other = (X509AttributeCertificateHolder)o;
return this.attrCert.equals(other.attrCert);
}
public int hashCode()
{
return this.attrCert.hashCode();
}
}

@ -0,0 +1,144 @@
package org.bouncycastle.cert;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.TBSCertList;
/**
* Holding class for an X.509 CRL Entry structure.
*/
public class X509CRLEntryHolder
{
private TBSCertList.CRLEntry entry;
private GeneralNames ca;
X509CRLEntryHolder(TBSCertList.CRLEntry entry, boolean isIndirect, GeneralNames previousCA)
{
this.entry = entry;
this.ca = previousCA;
if (isIndirect && entry.hasExtensions())
{
Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
if (currentCaName != null)
{
ca = GeneralNames.getInstance(currentCaName.getParsedValue());
}
}
}
/**
* Return the serial number of the certificate associated with this CRLEntry.
*
* @return the revoked certificate's serial number.
*/
public BigInteger getSerialNumber()
{
return entry.getUserCertificate().getValue();
}
/**
* Return the date on which the certificate associated with this CRLEntry was revoked.
*
* @return the revocation date for the revoked certificate.
*/
public Date getRevocationDate()
{
return entry.getRevocationDate().getDate();
}
/**
* Return whether or not the holder's CRL entry contains extensions.
*
* @return true if extension are present, false otherwise.
*/
public boolean hasExtensions()
{
return entry.hasExtensions();
}
/**
* Return the available names for the certificate issuer for the certificate referred to by this CRL entry.
* <p>
* Note: this will be the issuer of the CRL unless it has been specified that the CRL is indirect
* in the IssuingDistributionPoint extension and either a previous entry, or the current one,
* has specified a different CA via the certificateIssuer extension.
* </p>
*
* @return the revoked certificate's issuer.
*/
public GeneralNames getCertificateIssuer()
{
return this.ca;
}
/**
* Look up the extension associated with the passed in OID.
*
* @param oid the OID of the extension of interest.
*
* @return the extension if present, null otherwise.
*/
public Extension getExtension(ASN1ObjectIdentifier oid)
{
Extensions extensions = entry.getExtensions();
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
/**
* Return the extensions block associated with this CRL entry if there is one.
*
* @return the extensions block, null otherwise.
*/
public Extensions getExtensions()
{
return entry.getExtensions();
}
/**
* Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
* extensions contained in this holder's CRL entry.
*
* @return a list of extension OIDs.
*/
public List getExtensionOIDs()
{
return CertUtils.getExtensionOIDs(entry.getExtensions());
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* critical extensions contained in this holder's CRL entry.
*
* @return a set of critical extension OIDs.
*/
public Set getCriticalExtensionOIDs()
{
return CertUtils.getCriticalExtensionOIDs(entry.getExtensions());
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* non-critical extensions contained in this holder's CRL entry.
*
* @return a set of non-critical extension OIDs.
*/
public Set getNonCriticalExtensionOIDs()
{
return CertUtils.getNonCriticalExtensionOIDs(entry.getExtensions());
}
}

@ -0,0 +1,317 @@
package org.bouncycastle.cert;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
import org.bouncycastle.asn1.x509.TBSCertList;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
/**
* Holding class for an X.509 CRL structure.
*/
public class X509CRLHolder
{
private CertificateList x509CRL;
private boolean isIndirect;
private Extensions extensions;
private GeneralNames issuerName;
private static CertificateList parseStream(InputStream stream)
throws IOException
{
try
{
return CertificateList.getInstance(new ASN1InputStream(stream, true).readObject());
}
catch (ClassCastException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
catch (IllegalArgumentException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
}
private static boolean isIndirectCRL(Extensions extensions)
{
if (extensions == null)
{
return false;
}
Extension ext = extensions.getExtension(Extension.issuingDistributionPoint);
return ext != null && IssuingDistributionPoint.getInstance(ext.getParsedValue()).isIndirectCRL();
}
/**
* Create a X509CRLHolder from the passed in bytes.
*
* @param crlEncoding BER/DER encoding of the CRL
* @throws IOException in the event of corrupted data, or an incorrect structure.
*/
public X509CRLHolder(byte[] crlEncoding)
throws IOException
{
this(parseStream(new ByteArrayInputStream(crlEncoding)));
}
/**
* Create a X509CRLHolder from the passed in InputStream.
*
* @param crlStream BER/DER encoded InputStream of the CRL
* @throws IOException in the event of corrupted data, or an incorrect structure.
*/
public X509CRLHolder(InputStream crlStream)
throws IOException
{
this(parseStream(crlStream));
}
/**
* Create a X509CRLHolder from the passed in ASN.1 structure.
*
* @param x509CRL an ASN.1 CertificateList structure.
*/
public X509CRLHolder(CertificateList x509CRL)
{
this.x509CRL = x509CRL;
this.extensions = x509CRL.getTBSCertList().getExtensions();
this.isIndirect = isIndirectCRL(extensions);
this.issuerName = new GeneralNames(new GeneralName(x509CRL.getIssuer()));
}
/**
* Return the ASN.1 encoding of this holder's CRL.
*
* @return a DER encoded byte array.
* @throws IOException if an encoding cannot be generated.
*/
public byte[] getEncoded()
throws IOException
{
return x509CRL.getEncoded();
}
/**
* Return the issuer of this holder's CRL.
*
* @return the CRL issuer.
*/
public X500Name getIssuer()
{
return X500Name.getInstance(x509CRL.getIssuer());
}
public X509CRLEntryHolder getRevokedCertificate(BigInteger serialNumber)
{
GeneralNames currentCA = issuerName;
for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
{
TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
if (entry.getUserCertificate().getValue().equals(serialNumber))
{
return new X509CRLEntryHolder(entry, isIndirect, currentCA);
}
if (isIndirect && entry.hasExtensions())
{
Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
if (currentCaName != null)
{
currentCA = GeneralNames.getInstance(currentCaName.getParsedValue());
}
}
}
return null;
}
/**
* Return a collection of X509CRLEntryHolder objects, giving the details of the
* revoked certificates that appear on this CRL.
*
* @return the revoked certificates as a collection of X509CRLEntryHolder objects.
*/
public Collection getRevokedCertificates()
{
TBSCertList.CRLEntry[] entries = x509CRL.getRevokedCertificates();
List l = new ArrayList(entries.length);
GeneralNames currentCA = issuerName;
for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
{
TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
X509CRLEntryHolder crlEntry = new X509CRLEntryHolder(entry, isIndirect, currentCA);
l.add(crlEntry);
currentCA = crlEntry.getCertificateIssuer();
}
return l;
}
/**
* Return whether or not the holder's CRL contains extensions.
*
* @return true if extension are present, false otherwise.
*/
public boolean hasExtensions()
{
return extensions != null;
}
/**
* Look up the extension associated with the passed in OID.
*
* @param oid the OID of the extension of interest.
*
* @return the extension if present, null otherwise.
*/
public Extension getExtension(ASN1ObjectIdentifier oid)
{
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
/**
* Return the extensions block associated with this CRL if there is one.
*
* @return the extensions block, null otherwise.
*/
public Extensions getExtensions()
{
return extensions;
}
/**
* Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
* extensions contained in this holder's CRL.
*
* @return a list of extension OIDs.
*/
public List getExtensionOIDs()
{
return CertUtils.getExtensionOIDs(extensions);
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* critical extensions contained in this holder's CRL.
*
* @return a set of critical extension OIDs.
*/
public Set getCriticalExtensionOIDs()
{
return CertUtils.getCriticalExtensionOIDs(extensions);
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* non-critical extensions contained in this holder's CRL.
*
* @return a set of non-critical extension OIDs.
*/
public Set getNonCriticalExtensionOIDs()
{
return CertUtils.getNonCriticalExtensionOIDs(extensions);
}
/**
* Return the underlying ASN.1 structure for the CRL in this holder.
*
* @return a CertificateList object.
*/
public CertificateList toASN1Structure()
{
return x509CRL;
}
/**
* Validate the signature on the CRL.
*
* @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
* @return true if the signature is valid, false otherwise.
* @throws CertException if the signature cannot be processed or is inappropriate.
*/
public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
throws CertException
{
TBSCertList tbsCRL = x509CRL.getTBSCertList();
if (!CertUtils.isAlgIdEqual(tbsCRL.getSignature(), x509CRL.getSignatureAlgorithm()))
{
throw new CertException("signature invalid - algorithm identifier mismatch");
}
ContentVerifier verifier;
try
{
verifier = verifierProvider.get((tbsCRL.getSignature()));
OutputStream sOut = verifier.getOutputStream();
DEROutputStream dOut = new DEROutputStream(sOut);
dOut.writeObject(tbsCRL);
sOut.close();
}
catch (Exception e)
{
throw new CertException("unable to process signature: " + e.getMessage(), e);
}
return verifier.verify(x509CRL.getSignature().getBytes());
}
public boolean equals(
Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof X509CRLHolder))
{
return false;
}
X509CRLHolder other = (X509CRLHolder)o;
return this.x509CRL.equals(other.x509CRL);
}
public int hashCode()
{
return this.x509CRL.hashCode();
}
}

@ -0,0 +1,327 @@
package org.bouncycastle.cert;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
/**
* Holding class for an X.509 Certificate structure.
*/
public class X509CertificateHolder
{
private Certificate x509Certificate;
private Extensions extensions;
private static Certificate parseBytes(byte[] certEncoding)
throws IOException
{
try
{
return Certificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
}
catch (ClassCastException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
catch (IllegalArgumentException e)
{
throw new CertIOException("malformed data: " + e.getMessage(), e);
}
}
/**
* Create a X509CertificateHolder from the passed in bytes.
*
* @param certEncoding BER/DER encoding of the certificate.
* @throws IOException in the event of corrupted data, or an incorrect structure.
*/
public X509CertificateHolder(byte[] certEncoding)
throws IOException
{
this(parseBytes(certEncoding));
}
/**
* Create a X509CertificateHolder from the passed in ASN.1 structure.
*
* @param x509Certificate an ASN.1 Certificate structure.
*/
public X509CertificateHolder(Certificate x509Certificate)
{
this.x509Certificate = x509Certificate;
this.extensions = x509Certificate.getTBSCertificate().getExtensions();
}
public int getVersionNumber()
{
return x509Certificate.getVersionNumber();
}
/**
* @deprecated use getVersionNumber
*/
public int getVersion()
{
return x509Certificate.getVersionNumber();
}
/**
* Return whether or not the holder's certificate contains extensions.
*
* @return true if extension are present, false otherwise.
*/
public boolean hasExtensions()
{
return extensions != null;
}
/**
* Look up the extension associated with the passed in OID.
*
* @param oid the OID of the extension of interest.
*
* @return the extension if present, null otherwise.
*/
public Extension getExtension(ASN1ObjectIdentifier oid)
{
if (extensions != null)
{
return extensions.getExtension(oid);
}
return null;
}
/**
* Return the extensions block associated with this certificate if there is one.
*
* @return the extensions block, null otherwise.
*/
public Extensions getExtensions()
{
return extensions;
}
/**
* Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
* extensions contained in this holder's certificate.
*
* @return a list of extension OIDs.
*/
public List getExtensionOIDs()
{
return CertUtils.getExtensionOIDs(extensions);
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* critical extensions contained in this holder's certificate.
*
* @return a set of critical extension OIDs.
*/
public Set getCriticalExtensionOIDs()
{
return CertUtils.getCriticalExtensionOIDs(extensions);
}
/**
* Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
* non-critical extensions contained in this holder's certificate.
*
* @return a set of non-critical extension OIDs.
*/
public Set getNonCriticalExtensionOIDs()
{
return CertUtils.getNonCriticalExtensionOIDs(extensions);
}
/**
* Return the serial number of this attribute certificate.
*
* @return the serial number.
*/
public BigInteger getSerialNumber()
{
return x509Certificate.getSerialNumber().getValue();
}
/**
* Return the issuer of this certificate.
*
* @return the certificate issuer.
*/
public X500Name getIssuer()
{
return X500Name.getInstance(x509Certificate.getIssuer());
}
/**
* Return the subject this certificate is for.
*
* @return the subject for the certificate.
*/
public X500Name getSubject()
{
return X500Name.getInstance(x509Certificate.getSubject());
}
/**
* Return the date before which this certificate is not valid.
*
* @return the start time for the certificate's validity period.
*/
public Date getNotBefore()
{
return x509Certificate.getStartDate().getDate();
}
/**
* Return the date after which this certificate is not valid.
*
* @return the final time for the certificate's validity period.
*/
public Date getNotAfter()
{
return x509Certificate.getEndDate().getDate();
}
/**
* Return the SubjectPublicKeyInfo describing the public key this certificate is carrying.
*
* @return the public key ASN.1 structure contained in the certificate.
*/
public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
{
return x509Certificate.getSubjectPublicKeyInfo();
}
/**
* Return the underlying ASN.1 structure for the certificate in this holder.
*
* @return a X509CertificateStructure object.
*/
public Certificate toASN1Structure()
{
return x509Certificate;
}
/**
* Return the details of the signature algorithm used to create this attribute certificate.
*
* @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
*/
public AlgorithmIdentifier getSignatureAlgorithm()
{
return x509Certificate.getSignatureAlgorithm();
}
/**
* Return the bytes making up the signature associated with this attribute certificate.
*
* @return the attribute certificate signature bytes.
*/
public byte[] getSignature()
{
return x509Certificate.getSignature().getBytes();
}
/**
* Return whether or not this certificate is valid on a particular date.
*
* @param date the date of interest.
* @return true if the certificate is valid, false otherwise.
*/
public boolean isValidOn(Date date)
{
return !date.before(x509Certificate.getStartDate().getDate()) && !date.after(x509Certificate.getEndDate().getDate());
}
/**
* Validate the signature on the certificate in this holder.
*
* @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
* @return true if the signature is valid, false otherwise.
* @throws CertException if the signature cannot be processed or is inappropriate.
*/
public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
throws CertException
{
TBSCertificate tbsCert = x509Certificate.getTBSCertificate();
if (!CertUtils.isAlgIdEqual(tbsCert.getSignature(), x509Certificate.getSignatureAlgorithm()))
{
throw new CertException("signature invalid - algorithm identifier mismatch");
}
ContentVerifier verifier;
try
{
verifier = verifierProvider.get((tbsCert.getSignature()));
OutputStream sOut = verifier.getOutputStream();
DEROutputStream dOut = new DEROutputStream(sOut);
dOut.writeObject(tbsCert);
sOut.close();
}
catch (Exception e)
{
throw new CertException("unable to process signature: " + e.getMessage(), e);
}
return verifier.verify(x509Certificate.getSignature().getBytes());
}
public boolean equals(
Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof X509CertificateHolder))
{
return false;
}
X509CertificateHolder other = (X509CertificateHolder)o;
return this.x509Certificate.equals(other.x509Certificate);
}
public int hashCode()
{
return this.x509Certificate.hashCode();
}
/**
* Return the ASN.1 encoding of this holder's certificate.
*
* @return a DER encoded byte array.
* @throws IOException if an encoding cannot be generated.
*/
public byte[] getEncoded()
throws IOException
{
return x509Certificate.getEncoded();
}
}

@ -0,0 +1,64 @@
package org.bouncycastle.cert.jcajce;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.util.CollectionStore;
/**
* Class for storing Certificates for later lookup.
* <p>
* The class will convert X509Certificate objects into X509CertificateHolder objects.
* </p>
*/
public class JcaCertStore
extends CollectionStore
{
/**
* Basic constructor.
*
* @param collection - initial contents for the store, this is copied.
*/
public JcaCertStore(Collection collection)
throws CertificateEncodingException
{
super(convertCerts(collection));
}
private static Collection convertCerts(Collection collection)
throws CertificateEncodingException
{
List list = new ArrayList(collection.size());
for (Iterator it = collection.iterator(); it.hasNext();)
{
Object o = it.next();
if (o instanceof X509Certificate)
{
X509Certificate cert = (X509Certificate)o;
try
{
list.add(new X509CertificateHolder(cert.getEncoded()));
}
catch (IOException e)
{
throw new CertificateEncodingException("unable to read encoding: " + e.getMessage());
}
}
else
{
list.add((X509CertificateHolder)o);
}
}
return list;
}
}

@ -0,0 +1,26 @@
package org.bouncycastle.cert.jcajce;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.cert.X509CertificateHolder;
/**
* JCA helper class for converting an X509Certificate into a X509CertificateHolder object.
*/
public class JcaX509CertificateHolder
extends X509CertificateHolder
{
/**
* Base constructor.
*
* @param cert certificate to be used a the source for the holder creation.
* @throws CertificateEncodingException if there is a problem extracting the certificate information.
*/
public JcaX509CertificateHolder(X509Certificate cert)
throws CertificateEncodingException
{
super(Certificate.getInstance(cert.getEncoded()));
}
}

@ -0,0 +1,33 @@
package org.bouncycastle.cert.selector;
import java.io.IOException;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
class MSOutlookKeyIdCalculator
{
static byte[] calculateKeyId(SubjectPublicKeyInfo info)
{
Digest dig = new SHA1Digest(); // TODO: include definition of SHA-1 here
byte[] hash = new byte[dig.getDigestSize()];
byte[] spkiEnc = new byte[0];
try
{
spkiEnc = info.getEncoded(ASN1Encoding.DER);
}
catch (IOException e)
{
return new byte[0];
}
// try the outlook 2010 calculation
dig.update(spkiEnc, 0, spkiEnc.length);
dig.doFinal(hash, 0);
return hash;
}
}

@ -0,0 +1,152 @@
package org.bouncycastle.cert.selector;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Selector;
/**
* a basic index for a X509CertificateHolder class
*/
public class X509CertificateHolderSelector
implements Selector
{
private byte[] subjectKeyId;
private X500Name issuer;
private BigInteger serialNumber;
/**
* Construct a selector with the value of a public key's subjectKeyId.
*
* @param subjectKeyId a subjectKeyId
*/
public X509CertificateHolderSelector(byte[] subjectKeyId)
{
this(null, null, subjectKeyId);
}
/**
* Construct a signer ID based on the issuer and serial number of the signer's associated
* certificate.
*
* @param issuer the issuer of the signer's associated certificate.
* @param serialNumber the serial number of the signer's associated certificate.
*/
public X509CertificateHolderSelector(X500Name issuer, BigInteger serialNumber)
{
this(issuer, serialNumber, null);
}
/**
* Construct a signer ID based on the issuer and serial number of the signer's associated
* certificate.
*
* @param issuer the issuer of the signer's associated certificate.
* @param serialNumber the serial number of the signer's associated certificate.
* @param subjectKeyId the subject key identifier to use to match the signers associated certificate.
*/
public X509CertificateHolderSelector(X500Name issuer, BigInteger serialNumber, byte[] subjectKeyId)
{
this.issuer = issuer;
this.serialNumber = serialNumber;
this.subjectKeyId = subjectKeyId;
}
public X500Name getIssuer()
{
return issuer;
}
public BigInteger getSerialNumber()
{
return serialNumber;
}
public byte[] getSubjectKeyIdentifier()
{
return Arrays.clone(subjectKeyId);
}
public int hashCode()
{
int code = Arrays.hashCode(subjectKeyId);
if (this.serialNumber != null)
{
code ^= this.serialNumber.hashCode();
}
if (this.issuer != null)
{
code ^= this.issuer.hashCode();
}
return code;
}
public boolean equals(
Object o)
{
if (!(o instanceof X509CertificateHolderSelector))
{
return false;
}
X509CertificateHolderSelector id = (X509CertificateHolderSelector)o;
return Arrays.areEqual(subjectKeyId, id.subjectKeyId)
&& equalsObj(this.serialNumber, id.serialNumber)
&& equalsObj(this.issuer, id.issuer);
}
private boolean equalsObj(Object a, Object b)
{
return (a != null) ? a.equals(b) : b == null;
}
public boolean match(Object obj)
{
if (obj instanceof X509CertificateHolder)
{
X509CertificateHolder certHldr = (X509CertificateHolder)obj;
if (this.getSerialNumber() != null)
{
IssuerAndSerialNumber iAndS = new IssuerAndSerialNumber(certHldr.toASN1Structure());
return iAndS.getName().equals(this.issuer)
&& iAndS.getSerialNumber().getValue().equals(this.serialNumber);
}
else if (subjectKeyId != null)
{
Extension ext = certHldr.getExtension(Extension.subjectKeyIdentifier);
if (ext == null)
{
return Arrays.areEqual(subjectKeyId, MSOutlookKeyIdCalculator.calculateKeyId(certHldr.getSubjectPublicKeyInfo()));
}
byte[] subKeyID = ASN1OctetString.getInstance(ext.getParsedValue()).getOctets();
return Arrays.areEqual(subjectKeyId, subKeyID);
}
}
else if (obj instanceof byte[])
{
return Arrays.areEqual(subjectKeyId, (byte[])obj);
}
return false;
}
public Object clone()
{
return new X509CertificateHolderSelector(this.issuer, this.serialNumber, this.subjectKeyId);
}
}

@ -0,0 +1,49 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
/**
* a class representing null or absent content.
*/
public class CMSAbsentContent
implements CMSTypedData, CMSReadable
{
private final ASN1ObjectIdentifier type;
public CMSAbsentContent()
{
this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()));
}
public CMSAbsentContent(
ASN1ObjectIdentifier type)
{
this.type = type;
}
public InputStream getInputStream()
{
return null;
}
public void write(OutputStream zOut)
throws IOException, CMSException
{
// do nothing
}
public Object getContent()
{
return null;
}
public ASN1ObjectIdentifier getContentType()
{
return type;
}
}

@ -0,0 +1,32 @@
package org.bouncycastle.cms;
public class CMSAttributeTableGenerationException
extends CMSRuntimeException
{
Exception e;
public CMSAttributeTableGenerationException(
String name)
{
super(name);
}
public CMSAttributeTableGenerationException(
String name,
Exception e)
{
super(name);
this.e = e;
}
public Exception getUnderlyingException()
{
return e;
}
public Throwable getCause()
{
return e;
}
}

@ -0,0 +1,19 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.cms.AttributeTable;
import java.util.Map;
/**
* Note: The SIGNATURE parameter is only available when generating unsigned attributes.
*/
public interface CMSAttributeTableGenerator
{
static final String CONTENT_TYPE = "contentType";
static final String DIGEST = "digest";
static final String SIGNATURE = "encryptedDigest";
static final String DIGEST_ALGORITHM_IDENTIFIER = "digestAlgID";
AttributeTable getAttributes(Map parameters)
throws CMSAttributeTableGenerationException;
}

@ -0,0 +1,32 @@
package org.bouncycastle.cms;
public class CMSException
extends Exception
{
Exception e;
public CMSException(
String msg)
{
super(msg);
}
public CMSException(
String msg,
Exception e)
{
super(msg);
this.e = e;
}
public Exception getUnderlyingException()
{
return e;
}
public Throwable getCause()
{
return e;
}
}

@ -0,0 +1,21 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
/**
* Use CMSTypedData instead of this. See CMSProcessableFile/ByteArray for defaults.
*/
public interface CMSProcessable
{
/**
* generic routine to copy out the data we want processed - the OutputStream
* passed in will do the handling on it's own.
* <p>
* Note: this routine may be called multiple times.
*/
public void write(OutputStream out)
throws IOException, CMSException;
public Object getContent();
}

@ -0,0 +1,55 @@
package org.bouncycastle.cms;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.util.Arrays;
/**
* a holding class for a byte array of data to be processed.
*/
public class CMSProcessableByteArray
implements CMSTypedData, CMSReadable
{
private final ASN1ObjectIdentifier type;
private final byte[] bytes;
public CMSProcessableByteArray(
byte[] bytes)
{
this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), bytes);
}
public CMSProcessableByteArray(
ASN1ObjectIdentifier type,
byte[] bytes)
{
this.type = type;
this.bytes = bytes;
}
public InputStream getInputStream()
{
return new ByteArrayInputStream(bytes);
}
public void write(OutputStream zOut)
throws IOException, CMSException
{
zOut.write(bytes);
}
public Object getContent()
{
return Arrays.clone(bytes);
}
public ASN1ObjectIdentifier getContentType()
{
return type;
}
}

@ -0,0 +1,10 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.InputStream;
interface CMSReadable
{
public InputStream getInputStream()
throws IOException, CMSException;
}

@ -0,0 +1,32 @@
package org.bouncycastle.cms;
public class CMSRuntimeException
extends RuntimeException
{
Exception e;
public CMSRuntimeException(
String name)
{
super(name);
}
public CMSRuntimeException(
String name,
Exception e)
{
super(name);
this.e = e;
}
public Exception getUnderlyingException()
{
return e;
}
public Throwable getCause()
{
return e;
}
}

@ -0,0 +1,15 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface CMSSignatureAlgorithmNameGenerator
{
/**
* Return the digest algorithm using one of the standard string
* representations rather than the algorithm object identifier (if possible).
*
* @param digestAlg the digest algorithm id.
* @param encryptionAlg the encryption, or signing, algorithm id.
*/
String getSignatureName(AlgorithmIdentifier digestAlg, AlgorithmIdentifier encryptionAlg);
}

@ -0,0 +1,17 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
/**
* Finder which is used to look up the algorithm identifiers representing the encryption algorithms that
* are associated with a particular signature algorithm.
*/
public interface CMSSignatureEncryptionAlgorithmFinder
{
/**
* Return the encryption algorithm identifier associated with the passed in signatureAlgorithm
* @param signatureAlgorithm the algorithm identifier of the signature of interest
* @return the algorithm identifier to be associated with the encryption algorithm used in signature creation.
*/
AlgorithmIdentifier findEncryptionAlgorithm(AlgorithmIdentifier signatureAlgorithm);
}

@ -0,0 +1,547 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.BERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
import org.bouncycastle.util.Store;
/**
* general class for handling a pkcs7-signature message.
*
* A simple example of usage - note, in the example below the validity of
* the certificate isn't verified, just the fact that one of the certs
* matches the given signer...
*
* <pre>
* Store certStore = s.getCertificates();
* SignerInformationStore signers = s.getSignerInfos();
* Collection c = signers.getSigners();
* Iterator it = c.iterator();
*
* while (it.hasNext())
* {
* SignerInformation signer = (SignerInformation)it.next();
* Collection certCollection = certStore.getMatches(signer.getSID());
*
* Iterator certIt = certCollection.iterator();
* X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
*
* if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
* {
* verified++;
* }
* }
* </pre>
*/
public class CMSSignedData
{
private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
SignedData signedData;
ContentInfo contentInfo;
CMSTypedData signedContent;
SignerInformationStore signerInfoStore;
private Map hashes;
private CMSSignedData(
CMSSignedData c)
{
this.signedData = c.signedData;
this.contentInfo = c.contentInfo;
this.signedContent = c.signedContent;
this.signerInfoStore = c.signerInfoStore;
}
public CMSSignedData(
byte[] sigBlock)
throws CMSException
{
this(CMSUtils.readContentInfo(sigBlock));
}
public CMSSignedData(
CMSProcessable signedContent,
byte[] sigBlock)
throws CMSException
{
this(signedContent, CMSUtils.readContentInfo(sigBlock));
}
/**
* Content with detached signature, digests precomputed
*
* @param hashes a map of precomputed digests for content indexed by name of hash.
* @param sigBlock the signature object.
*/
public CMSSignedData(
Map hashes,
byte[] sigBlock)
throws CMSException
{
this(hashes, CMSUtils.readContentInfo(sigBlock));
}
/**
* base constructor - content with detached signature.
*
* @param signedContent the content that was signed.
* @param sigData the signature object.
*/
public CMSSignedData(
CMSProcessable signedContent,
InputStream sigData)
throws CMSException
{
this(signedContent, CMSUtils.readContentInfo(new ASN1InputStream(sigData)));
}
/**
* base constructor - with encapsulated content
*/
public CMSSignedData(
InputStream sigData)
throws CMSException
{
this(CMSUtils.readContentInfo(sigData));
}
public CMSSignedData(
final CMSProcessable signedContent,
ContentInfo sigData)
throws CMSException
{
if (signedContent instanceof CMSTypedData)
{
this.signedContent = (CMSTypedData)signedContent;
}
else
{
this.signedContent = new CMSTypedData()
{
public ASN1ObjectIdentifier getContentType()
{
return signedData.getEncapContentInfo().getContentType();
}
public void write(OutputStream out)
throws IOException, CMSException
{
signedContent.write(out);
}
public Object getContent()
{
return signedContent.getContent();
}
};
}
this.contentInfo = sigData;
this.signedData = getSignedData();
}
public CMSSignedData(
Map hashes,
ContentInfo sigData)
throws CMSException
{
this.hashes = hashes;
this.contentInfo = sigData;
this.signedData = getSignedData();
}
public CMSSignedData(
ContentInfo sigData)
throws CMSException
{
this.contentInfo = sigData;
this.signedData = getSignedData();
//
// this can happen if the signed message is sent simply to send a
// certificate chain.
//
if (signedData.getEncapContentInfo().getContent() != null)
{
this.signedContent = new CMSProcessableByteArray(signedData.getEncapContentInfo().getContentType(),
((ASN1OctetString)(signedData.getEncapContentInfo()
.getContent())).getOctets());
}
else
{
this.signedContent = null;
}
}
private SignedData getSignedData()
throws CMSException
{
try
{
return SignedData.getInstance(contentInfo.getContent());
}
catch (ClassCastException e)
{
throw new CMSException("Malformed content.", e);
}
catch (IllegalArgumentException e)
{
throw new CMSException("Malformed content.", e);
}
}
/**
* Return the version number for this object
*/
public int getVersion()
{
return signedData.getVersion().getValue().intValue();
}
/**
* return the collection of signers that are associated with the
* signatures for the message.
*/
public SignerInformationStore getSignerInfos()
{
if (signerInfoStore == null)
{
ASN1Set s = signedData.getSignerInfos();
List signerInfos = new ArrayList();
SignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder();
for (int i = 0; i != s.size(); i++)
{
SignerInfo info = SignerInfo.getInstance(s.getObjectAt(i));
ASN1ObjectIdentifier contentType = signedData.getEncapContentInfo().getContentType();
if (hashes == null)
{
signerInfos.add(new SignerInformation(info, contentType, signedContent, null));
}
else
{
Object obj = hashes.keySet().iterator().next();
byte[] hash = (obj instanceof String) ? (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm().getId()) : (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm());
signerInfos.add(new SignerInformation(info, contentType, null, hash));
}
}
signerInfoStore = new SignerInformationStore(signerInfos);
}
return signerInfoStore;
}
/**
* Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
*
* @return a Store of X509CertificateHolder objects.
*/
public Store getCertificates()
{
return HELPER.getCertificates(signedData.getCertificates());
}
/**
* Return any X.509 CRL objects in this SignedData structure as a Store of X509CRLHolder objects.
*
* @return a Store of X509CRLHolder objects.
*/
public Store getCRLs()
{
return HELPER.getCRLs(signedData.getCRLs());
}
/**
* Return any X.509 attribute certificate objects in this SignedData structure as a Store of X509AttributeCertificateHolder objects.
*
* @return a Store of X509AttributeCertificateHolder objects.
*/
public Store getAttributeCertificates()
{
return HELPER.getAttributeCertificates(signedData.getCertificates());
}
// BEGIN android-removed
// /**
// * Return any OtherRevocationInfo OtherRevInfo objects of the type indicated by otherRevocationInfoFormat in
// * this SignedData structure.
// *
// * @param otherRevocationInfoFormat OID of the format type been looked for.
// *
// * @return a Store of ASN1Encodable objects representing any objects of otherRevocationInfoFormat found.
// */
// public Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat)
// {
// return HELPER.getOtherRevocationInfo(otherRevocationInfoFormat, signedData.getCRLs());
// }
// END android-removed
/**
* Return the a string representation of the OID associated with the
* encapsulated content info structure carried in the signed data.
*
* @return the OID for the content type.
*/
public String getSignedContentTypeOID()
{
return signedData.getEncapContentInfo().getContentType().getId();
}
public CMSTypedData getSignedContent()
{
return signedContent;
}
/**
* return the ContentInfo
*/
public ContentInfo toASN1Structure()
{
return contentInfo;
}
/**
* return the ASN.1 encoded representation of this object.
*/
public byte[] getEncoded()
throws IOException
{
return contentInfo.getEncoded();
}
// BEGIN android-removed
// /**
// * Verify all the SignerInformation objects and their associated counter signatures attached
// * to this CMS SignedData object.
// *
// * @param verifierProvider a provider of SignerInformationVerifier objects.
// * @return true if all verify, false otherwise.
// * @throws CMSException if an exception occurs during the verification process.
// */
// public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider)
// throws CMSException
// {
// return verifySignatures(verifierProvider, false);
// }
//
// /**
// * Verify all the SignerInformation objects and optionally their associated counter signatures attached
// * to this CMS SignedData object.
// *
// * @param verifierProvider a provider of SignerInformationVerifier objects.
// * @param ignoreCounterSignatures if true don't check counter signatures. If false check counter signatures as well.
// * @return true if all verify, false otherwise.
// * @throws CMSException if an exception occurs during the verification process.
// */
// public boolean verifySignatures(SignerInformationVerifierProvider verifierProvider, boolean ignoreCounterSignatures)
// throws CMSException
// {
// Collection signers = this.getSignerInfos().getSigners();
//
// for (Iterator it = signers.iterator(); it.hasNext();)
// {
// SignerInformation signer = (SignerInformation)it.next();
//
// try
// {
// SignerInformationVerifier verifier = verifierProvider.get(signer.getSID());
//
// if (!signer.verify(verifier))
// {
// return false;
// }
//
// if (!ignoreCounterSignatures)
// {
// Collection counterSigners = signer.getCounterSignatures().getSigners();
//
// for (Iterator cIt = counterSigners.iterator(); cIt.hasNext();)
// {
// SignerInformation counterSigner = (SignerInformation)cIt.next();
// SignerInformationVerifier counterVerifier = verifierProvider.get(signer.getSID());
//
// if (!counterSigner.verify(counterVerifier))
// {
// return false;
// }
// }
// }
// }
// catch (OperatorCreationException e)
// {
// throw new CMSException("failure in verifier provider: " + e.getMessage(), e);
// }
// }
//
// return true;
// }
// END android-removed
/**
* Replace the SignerInformation store associated with this
* CMSSignedData object with the new one passed in. You would
* probably only want to do this if you wanted to change the unsigned
* attributes associated with a signer, or perhaps delete one.
*
* @param signedData the signed data object to be used as a base.
* @param signerInformationStore the new signer information store to use.
* @return a new signed data object.
*/
public static CMSSignedData replaceSigners(
CMSSignedData signedData,
SignerInformationStore signerInformationStore)
{
//
// copy
//
CMSSignedData cms = new CMSSignedData(signedData);
//
// replace the store
//
cms.signerInfoStore = signerInformationStore;
//
// replace the signers in the SignedData object
//
ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
ASN1EncodableVector vec = new ASN1EncodableVector();
Iterator it = signerInformationStore.getSigners().iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
vec.add(signer.toASN1Structure());
}
ASN1Set digests = new DERSet(digestAlgs);
ASN1Set signers = new DERSet(vec);
ASN1Sequence sD = (ASN1Sequence)signedData.signedData.toASN1Primitive();
vec = new ASN1EncodableVector();
//
// signers are the last item in the sequence.
//
vec.add(sD.getObjectAt(0)); // version
vec.add(digests);
for (int i = 2; i != sD.size() - 1; i++)
{
vec.add(sD.getObjectAt(i));
}
vec.add(signers);
cms.signedData = SignedData.getInstance(new BERSequence(vec));
//
// replace the contentInfo with the new one
//
cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
return cms;
}
/**
* Replace the certificate and CRL information associated with this
* CMSSignedData object with the new one passed in.
*
* @param signedData the signed data object to be used as a base.
* @param certificates the new certificates to be used.
* @param attrCerts the new attribute certificates to be used.
* @param crls the new CRLs to be used.
* @return a new signed data object.
* @exception CMSException if there is an error processing the CertStore
*/
public static CMSSignedData replaceCertificatesAndCRLs(
CMSSignedData signedData,
Store certificates,
Store attrCerts,
Store crls)
throws CMSException
{
//
// copy
//
CMSSignedData cms = new CMSSignedData(signedData);
//
// replace the certs and crls in the SignedData object
//
ASN1Set certSet = null;
ASN1Set crlSet = null;
if (certificates != null || attrCerts != null)
{
List certs = new ArrayList();
if (certificates != null)
{
certs.addAll(CMSUtils.getCertificatesFromStore(certificates));
}
if (attrCerts != null)
{
certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));
}
ASN1Set set = CMSUtils.createBerSetFromList(certs);
if (set.size() != 0)
{
certSet = set;
}
}
if (crls != null)
{
ASN1Set set = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(crls));
if (set.size() != 0)
{
crlSet = set;
}
}
//
// replace the CMS structure.
//
cms.signedData = new SignedData(signedData.signedData.getDigestAlgorithms(),
signedData.signedData.getEncapContentInfo(),
certSet,
crlSet,
signedData.signedData.getSignerInfos());
//
// replace the contentInfo with the new one
//
cms.contentInfo = new ContentInfo(cms.contentInfo.getContentType(), cms.signedData);
return cms;
}
}

@ -0,0 +1,232 @@
package org.bouncycastle.cms;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.BEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.cms.SignerInfo;
/**
* general class for generating a pkcs7-signature message.
* <p>
* A simple example of usage, generating a detached signature.
*
* <pre>
* List certList = new ArrayList();
* CMSTypedData msg = new CMSProcessableByteArray("Hello world!".getBytes());
*
* certList.add(signCert);
*
* Store certs = new JcaCertStore(certList);
*
* CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
* ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(signKP.getPrivate());
*
* gen.addSignerInfoGenerator(
* new JcaSignerInfoGeneratorBuilder(
* new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
* .build(sha1Signer, signCert));
*
* gen.addCertificates(certs);
*
* CMSSignedData sigData = gen.generate(msg, false);
* </pre>
*/
public class CMSSignedDataGenerator
extends CMSSignedGenerator
{
private List signerInfs = new ArrayList();
/**
* base constructor
*/
public CMSSignedDataGenerator()
{
}
/**
* Generate a CMS Signed Data object carrying a detached CMS signature.
*
* @param content the content to be signed.
*/
public CMSSignedData generate(
CMSTypedData content)
throws CMSException
{
return generate(content, false);
}
/**
* Generate a CMS Signed Data object which can be carrying a detached CMS signature, or have encapsulated data, depending on the value
* of the encapsulated parameter.
*
* @param content the content to be signed.
* @param encapsulate true if the content should be encapsulated in the signature, false otherwise.
*/
public CMSSignedData generate(
// FIXME Avoid accessing more than once to support CMSProcessableInputStream
CMSTypedData content,
boolean encapsulate)
throws CMSException
{
if (!signerInfs.isEmpty())
{
throw new IllegalStateException("this method can only be used with SignerInfoGenerator");
}
// TODO
// if (signerInfs.isEmpty())
// {
// /* RFC 3852 5.2
// * "In the degenerate case where there are no signers, the
// * EncapsulatedContentInfo value being "signed" is irrelevant. In this
// * case, the content type within the EncapsulatedContentInfo value being
// * "signed" MUST be id-data (as defined in section 4), and the content
// * field of the EncapsulatedContentInfo value MUST be omitted."
// */
// if (encapsulate)
// {
// throw new IllegalArgumentException("no signers, encapsulate must be false");
// }
// if (!DATA.equals(eContentType))
// {
// throw new IllegalArgumentException("no signers, eContentType must be id-data");
// }
// }
//
// if (!DATA.equals(eContentType))
// {
// /* RFC 3852 5.3
// * [The 'signedAttrs']...
// * field is optional, but it MUST be present if the content type of
// * the EncapsulatedContentInfo value being signed is not id-data.
// */
// // TODO signedAttrs must be present for all signers
// }
ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
ASN1EncodableVector signerInfos = new ASN1EncodableVector();
digests.clear(); // clear the current preserved digest state
//
// add the precalculated SignerInfo objects.
//
for (Iterator it = _signers.iterator(); it.hasNext();)
{
SignerInformation signer = (SignerInformation)it.next();
digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
// TODO Verify the content type and calculated digest match the precalculated SignerInfo
signerInfos.add(signer.toASN1Structure());
}
//
// add the SignerInfo objects
//
ASN1ObjectIdentifier contentTypeOID = content.getContentType();
ASN1OctetString octs = null;
if (content != null)
{
ByteArrayOutputStream bOut = null;
if (encapsulate)
{
bOut = new ByteArrayOutputStream();
}
OutputStream cOut = CMSUtils.attachSignersToOutputStream(signerGens, bOut);
// Just in case it's unencapsulated and there are no signers!
cOut = CMSUtils.getSafeOutputStream(cOut);
try
{
content.write(cOut);
cOut.close();
}
catch (IOException e)
{
throw new CMSException("data processing exception: " + e.getMessage(), e);
}
if (encapsulate)
{
octs = new BEROctetString(bOut.toByteArray());
}
}
for (Iterator it = signerGens.iterator(); it.hasNext();)
{
SignerInfoGenerator sGen = (SignerInfoGenerator)it.next();
SignerInfo inf = sGen.generate(contentTypeOID);
digestAlgs.add(inf.getDigestAlgorithm());
signerInfos.add(inf);
byte[] calcDigest = sGen.getCalculatedDigest();
if (calcDigest != null)
{
digests.put(inf.getDigestAlgorithm().getAlgorithm().getId(), calcDigest);
}
}
ASN1Set certificates = null;
if (certs.size() != 0)
{
certificates = CMSUtils.createBerSetFromList(certs);
}
ASN1Set certrevlist = null;
if (crls.size() != 0)
{
certrevlist = CMSUtils.createBerSetFromList(crls);
}
ContentInfo encInfo = new ContentInfo(contentTypeOID, octs);
SignedData sd = new SignedData(
new DERSet(digestAlgs),
encInfo,
certificates,
certrevlist,
new DERSet(signerInfos));
ContentInfo contentInfo = new ContentInfo(
CMSObjectIdentifiers.signedData, sd);
return new CMSSignedData(content, contentInfo);
}
/**
* generate a set of one or more SignerInformation objects representing counter signatures on
* the passed in SignerInformation object.
*
* @param signer the signer to be countersigned
* @return a store containing the signers.
*/
public SignerInformationStore generateCounterSigners(SignerInformation signer)
throws CMSException
{
return this.generate(new CMSProcessableByteArray(null, signer.getSignature()), false).getSignerInfos();
}
}

@ -0,0 +1,247 @@
package org.bouncycastle.cms;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
// BEGIN android-removed
// import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Store;
public class CMSSignedGenerator
{
/**
* Default type for the signed data.
*/
public static final String DATA = CMSObjectIdentifiers.data.getId();
public static final String DIGEST_SHA1 = OIWObjectIdentifiers.idSHA1.getId();
public static final String DIGEST_SHA224 = NISTObjectIdentifiers.id_sha224.getId();
public static final String DIGEST_SHA256 = NISTObjectIdentifiers.id_sha256.getId();
public static final String DIGEST_SHA384 = NISTObjectIdentifiers.id_sha384.getId();
public static final String DIGEST_SHA512 = NISTObjectIdentifiers.id_sha512.getId();
public static final String DIGEST_MD5 = PKCSObjectIdentifiers.md5.getId();
// BEGIN android-removed
// public static final String DIGEST_GOST3411 = CryptoProObjectIdentifiers.gostR3411.getId();
// public static final String DIGEST_RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.getId();
// public static final String DIGEST_RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.getId();
// public static final String DIGEST_RIPEMD256 = TeleTrusTObjectIdentifiers.ripemd256.getId();
// END android-removed
public static final String ENCRYPTION_RSA = PKCSObjectIdentifiers.rsaEncryption.getId();
public static final String ENCRYPTION_DSA = X9ObjectIdentifiers.id_dsa_with_sha1.getId();
public static final String ENCRYPTION_ECDSA = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
public static final String ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS.getId();
// BEGIN android-removed
// public static final String ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94.getId();
// public static final String ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001.getId();
// END android-removed
private static final String ENCRYPTION_ECDSA_WITH_SHA1 = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
private static final String ENCRYPTION_ECDSA_WITH_SHA224 = X9ObjectIdentifiers.ecdsa_with_SHA224.getId();
private static final String ENCRYPTION_ECDSA_WITH_SHA256 = X9ObjectIdentifiers.ecdsa_with_SHA256.getId();
private static final String ENCRYPTION_ECDSA_WITH_SHA384 = X9ObjectIdentifiers.ecdsa_with_SHA384.getId();
private static final String ENCRYPTION_ECDSA_WITH_SHA512 = X9ObjectIdentifiers.ecdsa_with_SHA512.getId();
private static final Set NO_PARAMS = new HashSet();
private static final Map EC_ALGORITHMS = new HashMap();
static
{
NO_PARAMS.add(ENCRYPTION_DSA);
NO_PARAMS.add(ENCRYPTION_ECDSA);
NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA1);
NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA224);
NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA256);
NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA384);
NO_PARAMS.add(ENCRYPTION_ECDSA_WITH_SHA512);
EC_ALGORITHMS.put(DIGEST_SHA1, ENCRYPTION_ECDSA_WITH_SHA1);
EC_ALGORITHMS.put(DIGEST_SHA224, ENCRYPTION_ECDSA_WITH_SHA224);
EC_ALGORITHMS.put(DIGEST_SHA256, ENCRYPTION_ECDSA_WITH_SHA256);
EC_ALGORITHMS.put(DIGEST_SHA384, ENCRYPTION_ECDSA_WITH_SHA384);
EC_ALGORITHMS.put(DIGEST_SHA512, ENCRYPTION_ECDSA_WITH_SHA512);
}
protected List certs = new ArrayList();
protected List crls = new ArrayList();
protected List _signers = new ArrayList();
protected List signerGens = new ArrayList();
protected Map digests = new HashMap();
/**
* base constructor
*/
protected CMSSignedGenerator()
{
}
protected Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
{
Map param = new HashMap();
param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType);
param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
param.put(CMSAttributeTableGenerator.DIGEST, Arrays.clone(hash));
return param;
}
/**
* Add a certificate to the certificate set to be included with the generated SignedData message.
*
* @param certificate the certificate to be included.
* @throws CMSException if the certificate cannot be encoded for adding.
*/
public void addCertificate(
X509CertificateHolder certificate)
throws CMSException
{
certs.add(certificate.toASN1Structure());
}
/**
* Add the certificates in certStore to the certificate set to be included with the generated SignedData message.
*
* @param certStore the store containing the certificates to be included.
* @throws CMSException if the certificates cannot be encoded for adding.
*/
public void addCertificates(
Store certStore)
throws CMSException
{
certs.addAll(CMSUtils.getCertificatesFromStore(certStore));
}
/**
* Add a CRL to the CRL set to be included with the generated SignedData message.
*
* @param crl the CRL to be included.
*/
public void addCRL(X509CRLHolder crl)
{
crls.add(crl.toASN1Structure());
}
/**
* Add the CRLs in crlStore to the CRL set to be included with the generated SignedData message.
*
* @param crlStore the store containing the CRLs to be included.
* @throws CMSException if the CRLs cannot be encoded for adding.
*/
public void addCRLs(
Store crlStore)
throws CMSException
{
crls.addAll(CMSUtils.getCRLsFromStore(crlStore));
}
/**
* Add the attribute certificates in attrStore to the certificate set to be included with the generated SignedData message.
*
* @param attrCert the store containing the certificates to be included.
* @throws CMSException if the attribute certificate cannot be encoded for adding.
*/
public void addAttributeCertificate(
X509AttributeCertificateHolder attrCert)
throws CMSException
{
certs.add(new DERTaggedObject(false, 2, attrCert.toASN1Structure()));
}
/**
* Add the attribute certificates in attrStore to the certificate set to be included with the generated SignedData message.
*
* @param attrStore the store containing the certificates to be included.
* @throws CMSException if the attribute certificate cannot be encoded for adding.
*/
public void addAttributeCertificates(
Store attrStore)
throws CMSException
{
certs.addAll(CMSUtils.getAttributeCertificatesFromStore(attrStore));
}
// BEGIN android-removed
// /**
// * Add a single instance of otherRevocationData to the CRL set to be included with the generated SignedData message.
// *
// * @param otherRevocationInfoFormat the OID specifying the format of the otherRevocationInfo data.
// * @param otherRevocationInfo the otherRevocationInfo ASN.1 structure.
// */
// public void addOtherRevocationInfo(
// ASN1ObjectIdentifier otherRevocationInfoFormat,
// ASN1Encodable otherRevocationInfo)
// {
// crls.add(new DERTaggedObject(false, 1, new OtherRevocationInfoFormat(otherRevocationInfoFormat, otherRevocationInfo)));
// }
//
// /**
// * Add a Store of otherRevocationData to the CRL set to be included with the generated SignedData message.
// *
// * @param otherRevocationInfoFormat the OID specifying the format of the otherRevocationInfo data.
// * @param otherRevocationInfos a Store of otherRevocationInfo data to add.
// */
// public void addOtherRevocationInfo(
// ASN1ObjectIdentifier otherRevocationInfoFormat,
// Store otherRevocationInfos)
// {
// crls.addAll(CMSUtils.getOthersFromStore(otherRevocationInfoFormat, otherRevocationInfos));
// }
// END android-removed
/**
* Add a store of pre-calculated signers to the generator.
*
* @param signerStore store of signers
*/
public void addSigners(
SignerInformationStore signerStore)
{
Iterator it = signerStore.getSigners().iterator();
while (it.hasNext())
{
_signers.add(it.next());
}
}
/**
* Add a generator for a particular signer to this CMS SignedData generator.
*
* @param infoGen the generator representing the particular signer.
*/
public void addSignerInfoGenerator(SignerInfoGenerator infoGen)
{
signerGens.add(infoGen);
}
/**
* Return a map of oids and byte arrays representing the digests calculated on the content during
* the last generate.
*
* @return a map of oids (as String objects) and byte[] representing digests.
*/
public Map getGeneratedDigests()
{
return new HashMap(digests);
}
}

@ -0,0 +1,263 @@
package org.bouncycastle.cms;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERNull;
// BEGIN android-removed
// import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.util.CollectionStore;
import org.bouncycastle.util.Store;
class CMSSignedHelper
{
static final CMSSignedHelper INSTANCE = new CMSSignedHelper();
private static final Map encryptionAlgs = new HashMap();
private static final Map digestAlgs = new HashMap();
private static final Map digestAliases = new HashMap();
private static void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
{
digestAlgs.put(alias.getId(), digest);
encryptionAlgs.put(alias.getId(), encryption);
}
static
{
addEntries(NISTObjectIdentifiers.dsa_with_sha224, "SHA224", "DSA");
addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
// BEGIN android-removed
// addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
// addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
// END android-removed
addEntries(OIWObjectIdentifiers.md5WithRSA, "MD5", "RSA");
addEntries(OIWObjectIdentifiers.sha1WithRSA, "SHA1", "RSA");
// BEGIN android-removed
// addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2", "RSA");
// addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
// END android-removed
addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5", "RSA");
addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1", "RSA");
addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224", "RSA");
addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
encryptionAlgs.put(X9ObjectIdentifiers.id_dsa.getId(), "DSA");
encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption.getId(), "RSA");
encryptionAlgs.put(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
encryptionAlgs.put(X509ObjectIdentifiers.id_ea_rsa.getId(), "RSA");
// BEGIN android-removed
// encryptionAlgs.put(CMSSignedDataGenerator.ENCRYPTION_RSA_PSS, "RSAandMGF1");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_94.getId(), "GOST3410");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001.getId(), "ECGOST3410");
// encryptionAlgs.put("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410");
// encryptionAlgs.put("1.3.6.1.4.1.5849.1.1.5", "GOST3410");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001.getId(), "ECGOST3410");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94.getId(), "GOST3410");
//
// digestAlgs.put(PKCSObjectIdentifiers.md2.getId(), "MD2");
// digestAlgs.put(PKCSObjectIdentifiers.md4.getId(), "MD4");
// END android-removed
digestAlgs.put(PKCSObjectIdentifiers.md5.getId(), "MD5");
digestAlgs.put(OIWObjectIdentifiers.idSHA1.getId(), "SHA1");
digestAlgs.put(NISTObjectIdentifiers.id_sha224.getId(), "SHA224");
digestAlgs.put(NISTObjectIdentifiers.id_sha256.getId(), "SHA256");
digestAlgs.put(NISTObjectIdentifiers.id_sha384.getId(), "SHA384");
digestAlgs.put(NISTObjectIdentifiers.id_sha512.getId(), "SHA512");
// BEGIN android-removed
// digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128.getId(), "RIPEMD128");
// digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), "RIPEMD160");
// digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), "RIPEMD256");
// digestAlgs.put(CryptoProObjectIdentifiers.gostR3411.getId(), "GOST3411");
// digestAlgs.put("1.3.6.1.4.1.5849.1.2.1", "GOST3411");
// END android-removed
digestAliases.put("SHA1", new String[] { "SHA-1" });
digestAliases.put("SHA224", new String[] { "SHA-224" });
digestAliases.put("SHA256", new String[] { "SHA-256" });
digestAliases.put("SHA384", new String[] { "SHA-384" });
digestAliases.put("SHA512", new String[] { "SHA-512" });
}
/**
* Return the digest encryption algorithm using one of the standard
* JCA string representations rather the the algorithm identifier (if
* possible).
*/
String getEncryptionAlgName(
String encryptionAlgOID)
{
String algName = (String)encryptionAlgs.get(encryptionAlgOID);
if (algName != null)
{
return algName;
}
return encryptionAlgOID;
}
AlgorithmIdentifier fixAlgID(AlgorithmIdentifier algId)
{
if (algId.getParameters() == null)
{
return new AlgorithmIdentifier(algId.getAlgorithm(), DERNull.INSTANCE);
}
return algId;
}
void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
{
encryptionAlgs.put(oid.getId(), algorithmName);
}
void setSigningDigestAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
{
digestAlgs.put(oid.getId(), algorithmName);
}
Store getCertificates(ASN1Set certSet)
{
if (certSet != null)
{
List certList = new ArrayList(certSet.size());
for (Enumeration en = certSet.getObjects(); en.hasMoreElements();)
{
ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
if (obj instanceof ASN1Sequence)
{
certList.add(new X509CertificateHolder(Certificate.getInstance(obj)));
}
}
return new CollectionStore(certList);
}
return new CollectionStore(new ArrayList());
}
Store getAttributeCertificates(ASN1Set certSet)
{
if (certSet != null)
{
List certList = new ArrayList(certSet.size());
for (Enumeration en = certSet.getObjects(); en.hasMoreElements();)
{
ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
if (obj instanceof ASN1TaggedObject)
{
certList.add(new X509AttributeCertificateHolder(AttributeCertificate.getInstance(((ASN1TaggedObject)obj).getObject())));
}
}
return new CollectionStore(certList);
}
return new CollectionStore(new ArrayList());
}
Store getCRLs(ASN1Set crlSet)
{
if (crlSet != null)
{
List crlList = new ArrayList(crlSet.size());
for (Enumeration en = crlSet.getObjects(); en.hasMoreElements();)
{
ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
if (obj instanceof ASN1Sequence)
{
crlList.add(new X509CRLHolder(CertificateList.getInstance(obj)));
}
}
return new CollectionStore(crlList);
}
return new CollectionStore(new ArrayList());
}
// Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat, ASN1Set crlSet)
// {
// if (crlSet != null)
// {
// List crlList = new ArrayList(crlSet.size());
//
// for (Enumeration en = crlSet.getObjects(); en.hasMoreElements();)
// {
// ASN1Primitive obj = ((ASN1Encodable)en.nextElement()).toASN1Primitive();
//
// if (obj instanceof ASN1TaggedObject)
// {
// ASN1TaggedObject tObj = ASN1TaggedObject.getInstance(obj);
//
// if (tObj.getTagNo() == 1)
// {
// OtherRevocationInfoFormat other = OtherRevocationInfoFormat.getInstance(tObj, false);
//
// if (otherRevocationInfoFormat.equals(other.getInfoFormat()))
// {
// crlList.add(other.getInfo());
// }
// }
// }
// }
//
// return new CollectionStore(crlList);
// }
//
// return new CollectionStore(new ArrayList());
// }
}

@ -0,0 +1,11 @@
package org.bouncycastle.cms;
public class CMSSignerDigestMismatchException
extends CMSException
{
public CMSSignerDigestMismatchException(
String msg)
{
super(msg);
}
}

@ -0,0 +1,9 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
public interface CMSTypedData
extends CMSProcessable
{
ASN1ObjectIdentifier getContentType();
}

@ -0,0 +1,257 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.BEROctetStringGenerator;
import org.bouncycastle.asn1.BERSet;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
// BEGIN android-removed
// import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
// import org.bouncycastle.asn1.ocsp.OCSPResponse;
// import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
// END android-removed
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.io.Streams;
import org.bouncycastle.util.io.TeeInputStream;
import org.bouncycastle.util.io.TeeOutputStream;
class CMSUtils
{
static ContentInfo readContentInfo(
byte[] input)
throws CMSException
{
// enforce limit checking as from a byte array
return readContentInfo(new ASN1InputStream(input));
}
static ContentInfo readContentInfo(
InputStream input)
throws CMSException
{
// enforce some limit checking
return readContentInfo(new ASN1InputStream(input));
}
static List getCertificatesFromStore(Store certStore)
throws CMSException
{
List certs = new ArrayList();
try
{
for (Iterator it = certStore.getMatches(null).iterator(); it.hasNext();)
{
X509CertificateHolder c = (X509CertificateHolder)it.next();
certs.add(c.toASN1Structure());
}
return certs;
}
catch (ClassCastException e)
{
throw new CMSException("error processing certs", e);
}
}
static List getAttributeCertificatesFromStore(Store attrStore)
throws CMSException
{
List certs = new ArrayList();
try
{
for (Iterator it = attrStore.getMatches(null).iterator(); it.hasNext();)
{
X509AttributeCertificateHolder attrCert = (X509AttributeCertificateHolder)it.next();
certs.add(new DERTaggedObject(false, 2, attrCert.toASN1Structure()));
}
return certs;
}
catch (ClassCastException e)
{
throw new CMSException("error processing certs", e);
}
}
static List getCRLsFromStore(Store crlStore)
throws CMSException
{
List certs = new ArrayList();
try
{
for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext();)
{
X509CRLHolder c = (X509CRLHolder)it.next();
certs.add(c.toASN1Structure());
}
return certs;
}
catch (ClassCastException e)
{
throw new CMSException("error processing certs", e);
}
}
// BEGIN android-removed
// static Collection getOthersFromStore(ASN1ObjectIdentifier otherRevocationInfoFormat, Store otherRevocationInfos)
// {
// List others = new ArrayList();
//
// for (Iterator it = otherRevocationInfos.getMatches(null).iterator(); it.hasNext();)
// {
// ASN1Encodable info = (ASN1Encodable)it.next();
//
// if (CMSObjectIdentifiers.id_ri_ocsp_response.equals(otherRevocationInfoFormat))
// {
// OCSPResponse resp = OCSPResponse.getInstance(info);
//
// if (resp.getResponseStatus().getValue().intValue() != OCSPResponseStatus.SUCCESSFUL)
// {
// throw new IllegalArgumentException("cannot add unsuccessful OCSP response to CMS SignedData");
// }
// }
//
// others.add(new DERTaggedObject(false, 1, new OtherRevocationInfoFormat(otherRevocationInfoFormat, info)));
// }
//
// return others;
// }
// END android-removed
static ASN1Set createBerSetFromList(List derObjects)
{
ASN1EncodableVector v = new ASN1EncodableVector();
for (Iterator it = derObjects.iterator(); it.hasNext();)
{
v.add((ASN1Encodable)it.next());
}
return new BERSet(v);
}
static ASN1Set createDerSetFromList(List derObjects)
{
ASN1EncodableVector v = new ASN1EncodableVector();
for (Iterator it = derObjects.iterator(); it.hasNext();)
{
v.add((ASN1Encodable)it.next());
}
return new DERSet(v);
}
static OutputStream createBEROctetOutputStream(OutputStream s,
int tagNo, boolean isExplicit, int bufferSize) throws IOException
{
BEROctetStringGenerator octGen = new BEROctetStringGenerator(s, tagNo, isExplicit);
if (bufferSize != 0)
{
return octGen.getOctetOutputStream(new byte[bufferSize]);
}
return octGen.getOctetOutputStream();
}
private static ContentInfo readContentInfo(
ASN1InputStream in)
throws CMSException
{
try
{
return ContentInfo.getInstance(in.readObject());
}
catch (IOException e)
{
throw new CMSException("IOException reading content.", e);
}
catch (ClassCastException e)
{
throw new CMSException("Malformed content.", e);
}
catch (IllegalArgumentException e)
{
throw new CMSException("Malformed content.", e);
}
}
public static byte[] streamToByteArray(
InputStream in)
throws IOException
{
return Streams.readAll(in);
}
public static byte[] streamToByteArray(
InputStream in,
int limit)
throws IOException
{
return Streams.readAllLimited(in, limit);
}
static InputStream attachDigestsToInputStream(Collection digests, InputStream s)
{
InputStream result = s;
Iterator it = digests.iterator();
while (it.hasNext())
{
DigestCalculator digest = (DigestCalculator)it.next();
result = new TeeInputStream(result, digest.getOutputStream());
}
return result;
}
static OutputStream attachSignersToOutputStream(Collection signers, OutputStream s)
{
OutputStream result = s;
Iterator it = signers.iterator();
while (it.hasNext())
{
SignerInfoGenerator signerGen = (SignerInfoGenerator)it.next();
result = getSafeTeeOutputStream(result, signerGen.getCalculatingOutputStream());
}
return result;
}
static OutputStream getSafeOutputStream(OutputStream s)
{
return s == null ? new NullOutputStream() : s;
}
static OutputStream getSafeTeeOutputStream(OutputStream s1,
OutputStream s2)
{
return s1 == null ? getSafeOutputStream(s2)
: s2 == null ? getSafeOutputStream(s1) : new TeeOutputStream(
s1, s2);
}
}

@ -0,0 +1,11 @@
package org.bouncycastle.cms;
public class CMSVerifierCertificateNotValidException
extends CMSException
{
public CMSVerifierCertificateNotValidException(
String msg)
{
super(msg);
}
}

@ -0,0 +1,164 @@
package org.bouncycastle.cms;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
// BEGIN android-removed
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
public class DefaultCMSSignatureAlgorithmNameGenerator
implements CMSSignatureAlgorithmNameGenerator
{
private final Map encryptionAlgs = new HashMap();
private final Map digestAlgs = new HashMap();
private void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
{
digestAlgs.put(alias, digest);
encryptionAlgs.put(alias, encryption);
}
public DefaultCMSSignatureAlgorithmNameGenerator()
{
addEntries(NISTObjectIdentifiers.dsa_with_sha224, "SHA224", "DSA");
addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
// BEGIN android-removed
// addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
// addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
// END android-removed
addEntries(OIWObjectIdentifiers.md5WithRSA, "MD5", "RSA");
addEntries(OIWObjectIdentifiers.sha1WithRSA, "SHA1", "RSA");
// BEGIN android-removed
// addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2", "RSA");
// addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
// END android-removed
addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5", "RSA");
addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1", "RSA");
addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224", "RSA");
addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
encryptionAlgs.put(X9ObjectIdentifiers.id_dsa, "DSA");
encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
encryptionAlgs.put(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
encryptionAlgs.put(X509ObjectIdentifiers.id_ea_rsa, "RSA");
encryptionAlgs.put(PKCSObjectIdentifiers.id_RSASSA_PSS, "RSAandMGF1");
// BEGIN android-removed
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
// encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.6.2"), "ECGOST3410");
// encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.1.5"), "GOST3410");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
// encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
//
// digestAlgs.put(PKCSObjectIdentifiers.md2, "MD2");
// digestAlgs.put(PKCSObjectIdentifiers.md4, "MD4");
// END android-removed
digestAlgs.put(PKCSObjectIdentifiers.md5, "MD5");
digestAlgs.put(OIWObjectIdentifiers.idSHA1, "SHA1");
digestAlgs.put(NISTObjectIdentifiers.id_sha224, "SHA224");
digestAlgs.put(NISTObjectIdentifiers.id_sha256, "SHA256");
digestAlgs.put(NISTObjectIdentifiers.id_sha384, "SHA384");
digestAlgs.put(NISTObjectIdentifiers.id_sha512, "SHA512");
// BEGIN android-removed
// digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
// digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
// digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
// digestAlgs.put(CryptoProObjectIdentifiers.gostR3411, "GOST3411");
// digestAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.2.1"), "GOST3411");
// END android-removed
}
/**
* Return the digest algorithm using one of the standard JCA string
* representations rather than the algorithm identifier (if possible).
*/
private String getDigestAlgName(
ASN1ObjectIdentifier digestAlgOID)
{
String algName = (String)digestAlgs.get(digestAlgOID);
if (algName != null)
{
return algName;
}
return digestAlgOID.getId();
}
/**
* Return the digest encryption algorithm using one of the standard
* JCA string representations rather the the algorithm identifier (if
* possible).
*/
private String getEncryptionAlgName(
ASN1ObjectIdentifier encryptionAlgOID)
{
String algName = (String)encryptionAlgs.get(encryptionAlgOID);
if (algName != null)
{
return algName;
}
return encryptionAlgOID.getId();
}
/**
* Set the mapping for the encryption algorithm used in association with a SignedData generation
* or interpretation.
*
* @param oid object identifier to map.
* @param algorithmName algorithm name to use.
*/
protected void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
{
encryptionAlgs.put(oid, algorithmName);
}
/**
* Set the mapping for the digest algorithm to use in conjunction with a SignedData generation
* or interpretation.
*
* @param oid object identifier to map.
* @param algorithmName algorithm name to use.
*/
protected void setSigningDigestAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
{
digestAlgs.put(oid, algorithmName);
}
public String getSignatureName(AlgorithmIdentifier digestAlg, AlgorithmIdentifier encryptionAlg)
{
return getDigestAlgName(digestAlg.getAlgorithm()) + "with" + getEncryptionAlgName(encryptionAlg.getAlgorithm());
}
}

@ -0,0 +1,52 @@
package org.bouncycastle.cms;
import java.util.HashSet;
import java.util.Set;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public class DefaultCMSSignatureEncryptionAlgorithmFinder
implements CMSSignatureEncryptionAlgorithmFinder
{
private static final Set RSA_PKCS1d5 = new HashSet();
static
{
// BEGIN android-removed
// RSA_PKCS1d5.add(PKCSObjectIdentifiers.md2WithRSAEncryption);
// RSA_PKCS1d5.add(PKCSObjectIdentifiers.md4WithRSAEncryption);
// END android-removed
RSA_PKCS1d5.add(PKCSObjectIdentifiers.md5WithRSAEncryption);
RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha1WithRSAEncryption);
RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha224WithRSAEncryption);
RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha256WithRSAEncryption);
RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha384WithRSAEncryption);
RSA_PKCS1d5.add(PKCSObjectIdentifiers.sha512WithRSAEncryption);
// BEGIN android-removed
// RSA_PKCS1d5.add(OIWObjectIdentifiers.md4WithRSAEncryption);
// RSA_PKCS1d5.add(OIWObjectIdentifiers.md4WithRSA);
// END android-removed
RSA_PKCS1d5.add(OIWObjectIdentifiers.md5WithRSA);
RSA_PKCS1d5.add(OIWObjectIdentifiers.sha1WithRSA);
// BEGIN android-removed
// RSA_PKCS1d5.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
// RSA_PKCS1d5.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
// RSA_PKCS1d5.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
// END android-removed
}
public AlgorithmIdentifier findEncryptionAlgorithm(AlgorithmIdentifier signatureAlgorithm)
{
// RFC3370 section 3.2
if (RSA_PKCS1d5.contains(signatureAlgorithm.getAlgorithm()))
{
return new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
}
return signatureAlgorithm;
}
}

@ -0,0 +1,121 @@
package org.bouncycastle.cms;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.Time;
/**
* Default signed attributes generator.
*/
public class DefaultSignedAttributeTableGenerator
implements CMSAttributeTableGenerator
{
private final Hashtable table;
/**
* Initialise to use all defaults
*/
public DefaultSignedAttributeTableGenerator()
{
table = new Hashtable();
}
/**
* Initialise with some extra attributes or overrides.
*
* @param attributeTable initial attribute table to use.
*/
public DefaultSignedAttributeTableGenerator(
AttributeTable attributeTable)
{
if (attributeTable != null)
{
table = attributeTable.toHashtable();
}
else
{
table = new Hashtable();
}
}
/**
* Create a standard attribute table from the passed in parameters - this will
* normally include contentType, signingTime, and messageDigest. If the constructor
* using an AttributeTable was used, entries in it for contentType, signingTime, and
* messageDigest will override the generated ones.
*
* @param parameters source parameters for table generation.
*
* @return a filled in Hashtable of attributes.
*/
protected Hashtable createStandardAttributeTable(
Map parameters)
{
Hashtable std = copyHashTable(table);
if (!std.containsKey(CMSAttributes.contentType))
{
ASN1ObjectIdentifier contentType = ASN1ObjectIdentifier.getInstance(
parameters.get(CMSAttributeTableGenerator.CONTENT_TYPE));
// contentType will be null if we're trying to generate a counter signature.
if (contentType != null)
{
Attribute attr = new Attribute(CMSAttributes.contentType,
new DERSet(contentType));
std.put(attr.getAttrType(), attr);
}
}
if (!std.containsKey(CMSAttributes.signingTime))
{
Date signingTime = new Date();
Attribute attr = new Attribute(CMSAttributes.signingTime,
new DERSet(new Time(signingTime)));
std.put(attr.getAttrType(), attr);
}
if (!std.containsKey(CMSAttributes.messageDigest))
{
byte[] messageDigest = (byte[])parameters.get(
CMSAttributeTableGenerator.DIGEST);
Attribute attr = new Attribute(CMSAttributes.messageDigest,
new DERSet(new DEROctetString(messageDigest)));
std.put(attr.getAttrType(), attr);
}
return std;
}
/**
* @param parameters source parameters
* @return the populated attribute table
*/
public AttributeTable getAttributes(Map parameters)
{
return new AttributeTable(createStandardAttributeTable(parameters));
}
private static Hashtable copyHashTable(Hashtable paramsMap)
{
Hashtable newTable = new Hashtable();
Enumeration keys = paramsMap.keys();
while (keys.hasMoreElements())
{
Object key = keys.nextElement();
newTable.put(key, paramsMap.get(key));
}
return newTable;
}
}

@ -0,0 +1,28 @@
/**
*
*/
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
class NullOutputStream
extends OutputStream
{
public void write(byte[] buf)
throws IOException
{
// do nothing
}
public void write(byte[] buf, int off, int len)
throws IOException
{
// do nothing
}
public void write(int b) throws IOException
{
// do nothing
}
}

@ -0,0 +1,104 @@
package org.bouncycastle.cms;
import java.math.BigInteger;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.selector.X509CertificateHolderSelector;
import org.bouncycastle.util.Selector;
/**
* a basic index for a signer.
*/
public class SignerId
implements Selector
{
private X509CertificateHolderSelector baseSelector;
private SignerId(X509CertificateHolderSelector baseSelector)
{
this.baseSelector = baseSelector;
}
/**
* Construct a signer ID with the value of a public key's subjectKeyId.
*
* @param subjectKeyId a subjectKeyId
*/
public SignerId(byte[] subjectKeyId)
{
this(null, null, subjectKeyId);
}
/**
* Construct a signer ID based on the issuer and serial number of the signer's associated
* certificate.
*
* @param issuer the issuer of the signer's associated certificate.
* @param serialNumber the serial number of the signer's associated certificate.
*/
public SignerId(X500Name issuer, BigInteger serialNumber)
{
this(issuer, serialNumber, null);
}
/**
* Construct a signer ID based on the issuer and serial number of the signer's associated
* certificate.
*
* @param issuer the issuer of the signer's associated certificate.
* @param serialNumber the serial number of the signer's associated certificate.
* @param subjectKeyId the subject key identifier to use to match the signers associated certificate.
*/
public SignerId(X500Name issuer, BigInteger serialNumber, byte[] subjectKeyId)
{
this(new X509CertificateHolderSelector(issuer, serialNumber, subjectKeyId));
}
public X500Name getIssuer()
{
return baseSelector.getIssuer();
}
public BigInteger getSerialNumber()
{
return baseSelector.getSerialNumber();
}
public byte[] getSubjectKeyIdentifier()
{
return baseSelector.getSubjectKeyIdentifier();
}
public int hashCode()
{
return baseSelector.hashCode();
}
public boolean equals(
Object o)
{
if (!(o instanceof SignerId))
{
return false;
}
SignerId id = (SignerId)o;
return this.baseSelector.equals(id.baseSelector);
}
public boolean match(Object obj)
{
if (obj instanceof SignerInformation)
{
return ((SignerInformation)obj).getSID().equals(this);
}
return baseSelector.match(obj);
}
public Object clone()
{
return new SignerId(this.baseSelector);
}
}

@ -0,0 +1,291 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.SignerIdentifier;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.TeeOutputStream;
public class SignerInfoGenerator
{
private final SignerIdentifier signerIdentifier;
private final CMSAttributeTableGenerator sAttrGen;
private final CMSAttributeTableGenerator unsAttrGen;
private final ContentSigner signer;
private final DigestCalculator digester;
private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
private final CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
private byte[] calculatedDigest = null;
private X509CertificateHolder certHolder;
SignerInfoGenerator(
SignerIdentifier signerIdentifier,
ContentSigner signer,
DigestCalculatorProvider digesterProvider,
CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
throws OperatorCreationException
{
this(signerIdentifier, signer, digesterProvider, sigEncAlgFinder, false);
}
SignerInfoGenerator(
SignerIdentifier signerIdentifier,
ContentSigner signer,
DigestCalculatorProvider digesterProvider,
CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
boolean isDirectSignature)
throws OperatorCreationException
{
this.signerIdentifier = signerIdentifier;
this.signer = signer;
if (digesterProvider != null)
{
this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
}
else
{
this.digester = null;
}
if (isDirectSignature)
{
this.sAttrGen = null;
this.unsAttrGen = null;
}
else
{
this.sAttrGen = new DefaultSignedAttributeTableGenerator();
this.unsAttrGen = null;
}
this.sigEncAlgFinder = sigEncAlgFinder;
}
public SignerInfoGenerator(
SignerInfoGenerator original,
CMSAttributeTableGenerator sAttrGen,
CMSAttributeTableGenerator unsAttrGen)
{
this.signerIdentifier = original.signerIdentifier;
this.signer = original.signer;
this.digester = original.digester;
this.sigEncAlgFinder = original.sigEncAlgFinder;
this.sAttrGen = sAttrGen;
this.unsAttrGen = unsAttrGen;
}
SignerInfoGenerator(
SignerIdentifier signerIdentifier,
ContentSigner signer,
DigestCalculatorProvider digesterProvider,
CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder,
CMSAttributeTableGenerator sAttrGen,
CMSAttributeTableGenerator unsAttrGen)
throws OperatorCreationException
{
this.signerIdentifier = signerIdentifier;
this.signer = signer;
if (digesterProvider != null)
{
this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier()));
}
else
{
this.digester = null;
}
this.sAttrGen = sAttrGen;
this.unsAttrGen = unsAttrGen;
this.sigEncAlgFinder = sigEncAlgFinder;
}
public SignerIdentifier getSID()
{
return signerIdentifier;
}
public int getGeneratedVersion()
{
return signerIdentifier.isTagged() ? 3 : 1;
}
public boolean hasAssociatedCertificate()
{
return certHolder != null;
}
public X509CertificateHolder getAssociatedCertificate()
{
return certHolder;
}
public AlgorithmIdentifier getDigestAlgorithm()
{
if (digester != null)
{
return digester.getAlgorithmIdentifier();
}
return digAlgFinder.find(signer.getAlgorithmIdentifier());
}
public OutputStream getCalculatingOutputStream()
{
if (digester != null)
{
if (sAttrGen == null)
{
return new TeeOutputStream(digester.getOutputStream(), signer.getOutputStream());
}
return digester.getOutputStream();
}
else
{
return signer.getOutputStream();
}
}
public SignerInfo generate(ASN1ObjectIdentifier contentType)
throws CMSException
{
try
{
/* RFC 3852 5.4
* The result of the message digest calculation process depends on
* whether the signedAttrs field is present. When the field is absent,
* the result is just the message digest of the content as described
*
* above. When the field is present, however, the result is the message
* digest of the complete DER encoding of the SignedAttrs value
* contained in the signedAttrs field.
*/
ASN1Set signedAttr = null;
AlgorithmIdentifier digestAlg = null;
if (sAttrGen != null)
{
digestAlg = digester.getAlgorithmIdentifier();
calculatedDigest = digester.getDigest();
Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), calculatedDigest);
AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
signedAttr = getAttributeSet(signed);
// sig must be composed from the DER encoding.
OutputStream sOut = signer.getOutputStream();
sOut.write(signedAttr.getEncoded(ASN1Encoding.DER));
sOut.close();
}
else
{
if (digester != null)
{
digestAlg = digester.getAlgorithmIdentifier();
calculatedDigest = digester.getDigest();
}
else
{
digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier());
calculatedDigest = null;
}
}
byte[] sigBytes = signer.getSignature();
ASN1Set unsignedAttr = null;
if (unsAttrGen != null)
{
Map parameters = getBaseParameters(contentType, digestAlg, calculatedDigest);
parameters.put(CMSAttributeTableGenerator.SIGNATURE, Arrays.clone(sigBytes));
AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
unsignedAttr = getAttributeSet(unsigned);
}
AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier());
return new SignerInfo(signerIdentifier, digestAlg,
signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr);
}
catch (IOException e)
{
throw new CMSException("encoding error.", e);
}
}
void setAssociatedCertificate(X509CertificateHolder certHolder)
{
this.certHolder = certHolder;
}
private ASN1Set getAttributeSet(
AttributeTable attr)
{
if (attr != null)
{
return new DERSet(attr.toASN1EncodableVector());
}
return null;
}
private Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
{
Map param = new HashMap();
if (contentType != null)
{
param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType);
}
param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
param.put(CMSAttributeTableGenerator.DIGEST, Arrays.clone(hash));
return param;
}
public byte[] getCalculatedDigest()
{
if (calculatedDigest != null)
{
return Arrays.clone(calculatedDigest);
}
return null;
}
public CMSAttributeTableGenerator getSignedAttributeTableGenerator()
{
return sAttrGen;
}
public CMSAttributeTableGenerator getUnsignedAttributeTableGenerator()
{
return unsAttrGen;
}
}

@ -0,0 +1,139 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignerIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
/**
* Builder for SignerInfo generator objects.
*/
public class SignerInfoGeneratorBuilder
{
private DigestCalculatorProvider digestProvider;
private boolean directSignature;
private CMSAttributeTableGenerator signedGen;
private CMSAttributeTableGenerator unsignedGen;
private CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder;
/**
* Base constructor.
*
* @param digestProvider a provider of digest calculators for the algorithms required in the signature and attribute calculations.
*/
public SignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider)
{
this(digestProvider, new DefaultCMSSignatureEncryptionAlgorithmFinder());
}
/**
* Base constructor.
*
* @param digestProvider a provider of digest calculators for the algorithms required in the signature and attribute calculations.
*/
public SignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)
{
this.digestProvider = digestProvider;
this.sigEncAlgFinder = sigEncAlgFinder;
}
/**
* If the passed in flag is true, the signer signature will be based on the data, not
* a collection of signed attributes, and no signed attributes will be included.
*
* @return the builder object
*/
public SignerInfoGeneratorBuilder setDirectSignature(boolean hasNoSignedAttributes)
{
this.directSignature = hasNoSignedAttributes;
return this;
}
/**
* Provide a custom signed attribute generator.
*
* @param signedGen a generator of signed attributes.
* @return the builder object
*/
public SignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
{
this.signedGen = signedGen;
return this;
}
/**
* Provide a generator of unsigned attributes.
*
* @param unsignedGen a generator for signed attributes.
* @return the builder object
*/
public SignerInfoGeneratorBuilder setUnsignedAttributeGenerator(CMSAttributeTableGenerator unsignedGen)
{
this.unsignedGen = unsignedGen;
return this;
}
/**
* Build a generator with the passed in certHolder issuer and serial number as the signerIdentifier.
*
* @param contentSigner operator for generating the final signature in the SignerInfo with.
* @param certHolder carrier for the X.509 certificate related to the contentSigner.
* @return a SignerInfoGenerator
* @throws OperatorCreationException if the generator cannot be built.
*/
public SignerInfoGenerator build(ContentSigner contentSigner, X509CertificateHolder certHolder)
throws OperatorCreationException
{
SignerIdentifier sigId = new SignerIdentifier(new IssuerAndSerialNumber(certHolder.toASN1Structure()));
SignerInfoGenerator sigInfoGen = createGenerator(contentSigner, sigId);
sigInfoGen.setAssociatedCertificate(certHolder);
return sigInfoGen;
}
/**
* Build a generator with the passed in subjectKeyIdentifier as the signerIdentifier. If used you should
* try to follow the calculation described in RFC 5280 section 4.2.1.2.
*
* @param contentSigner operator for generating the final signature in the SignerInfo with.
* @param subjectKeyIdentifier key identifier to identify the public key for verifying the signature.
* @return a SignerInfoGenerator
* @throws OperatorCreationException if the generator cannot be built.
*/
public SignerInfoGenerator build(ContentSigner contentSigner, byte[] subjectKeyIdentifier)
throws OperatorCreationException
{
SignerIdentifier sigId = new SignerIdentifier(new DEROctetString(subjectKeyIdentifier));
return createGenerator(contentSigner, sigId);
}
private SignerInfoGenerator createGenerator(ContentSigner contentSigner, SignerIdentifier sigId)
throws OperatorCreationException
{
if (directSignature)
{
return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, true);
}
if (signedGen != null || unsignedGen != null)
{
if (signedGen == null)
{
signedGen = new DefaultSignedAttributeTableGenerator();
}
return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder, signedGen, unsignedGen);
}
return new SignerInfoGenerator(sigId, contentSigner, digestProvider, sigEncAlgFinder);
}
}

@ -0,0 +1,680 @@
package org.bouncycastle.cms;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignerIdentifier;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.RawContentVerifier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.TeeOutputStream;
/**
* an expanded SignerInfo block from a CMS Signed message
*/
public class SignerInformation
{
private SignerId sid;
private SignerInfo info;
private AlgorithmIdentifier digestAlgorithm;
private AlgorithmIdentifier encryptionAlgorithm;
private final ASN1Set signedAttributeSet;
private final ASN1Set unsignedAttributeSet;
private CMSProcessable content;
private byte[] signature;
private ASN1ObjectIdentifier contentType;
private byte[] resultDigest;
// Derived
private AttributeTable signedAttributeValues;
private AttributeTable unsignedAttributeValues;
private boolean isCounterSignature;
SignerInformation(
SignerInfo info,
ASN1ObjectIdentifier contentType,
CMSProcessable content,
byte[] resultDigest)
{
this.info = info;
this.contentType = contentType;
this.isCounterSignature = contentType == null;
SignerIdentifier s = info.getSID();
if (s.isTagged())
{
ASN1OctetString octs = ASN1OctetString.getInstance(s.getId());
sid = new SignerId(octs.getOctets());
}
else
{
IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(s.getId());
sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue());
}
this.digestAlgorithm = info.getDigestAlgorithm();
this.signedAttributeSet = info.getAuthenticatedAttributes();
this.unsignedAttributeSet = info.getUnauthenticatedAttributes();
this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm();
this.signature = info.getEncryptedDigest().getOctets();
this.content = content;
this.resultDigest = resultDigest;
}
public boolean isCounterSignature()
{
return isCounterSignature;
}
public ASN1ObjectIdentifier getContentType()
{
return this.contentType;
}
private byte[] encodeObj(
ASN1Encodable obj)
throws IOException
{
if (obj != null)
{
return obj.toASN1Primitive().getEncoded();
}
return null;
}
public SignerId getSID()
{
return sid;
}
/**
* return the version number for this objects underlying SignerInfo structure.
*/
public int getVersion()
{
return info.getVersion().getValue().intValue();
}
public AlgorithmIdentifier getDigestAlgorithmID()
{
return digestAlgorithm;
}
/**
* return the object identifier for the signature.
*/
public String getDigestAlgOID()
{
return digestAlgorithm.getAlgorithm().getId();
}
/**
* return the signature parameters, or null if there aren't any.
*/
public byte[] getDigestAlgParams()
{
try
{
return encodeObj(digestAlgorithm.getParameters());
}
catch (Exception e)
{
throw new RuntimeException("exception getting digest parameters " + e);
}
}
/**
* return the content digest that was calculated during verification.
*/
public byte[] getContentDigest()
{
if (resultDigest == null)
{
throw new IllegalStateException("method can only be called after verify.");
}
return Arrays.clone(resultDigest);
}
/**
* return the object identifier for the signature.
*/
public String getEncryptionAlgOID()
{
return encryptionAlgorithm.getAlgorithm().getId();
}
/**
* return the signature/encryption algorithm parameters, or null if
* there aren't any.
*/
public byte[] getEncryptionAlgParams()
{
try
{
return encodeObj(encryptionAlgorithm.getParameters());
}
catch (Exception e)
{
throw new RuntimeException("exception getting encryption parameters " + e);
}
}
/**
* return a table of the signed attributes - indexed by
* the OID of the attribute.
*/
public AttributeTable getSignedAttributes()
{
if (signedAttributeSet != null && signedAttributeValues == null)
{
signedAttributeValues = new AttributeTable(signedAttributeSet);
}
return signedAttributeValues;
}
/**
* return a table of the unsigned attributes indexed by
* the OID of the attribute.
*/
public AttributeTable getUnsignedAttributes()
{
if (unsignedAttributeSet != null && unsignedAttributeValues == null)
{
unsignedAttributeValues = new AttributeTable(unsignedAttributeSet);
}
return unsignedAttributeValues;
}
/**
* return the encoded signature
*/
public byte[] getSignature()
{
return Arrays.clone(signature);
}
/**
* Return a SignerInformationStore containing the counter signatures attached to this
* signer. If no counter signatures are present an empty store is returned.
*/
public SignerInformationStore getCounterSignatures()
{
// TODO There are several checks implied by the RFC3852 comments that are missing
/*
The countersignature attribute MUST be an unsigned attribute; it MUST
NOT be a signed attribute, an authenticated attribute, an
unauthenticated attribute, or an unprotected attribute.
*/
AttributeTable unsignedAttributeTable = getUnsignedAttributes();
if (unsignedAttributeTable == null)
{
return new SignerInformationStore(new ArrayList(0));
}
List counterSignatures = new ArrayList();
/*
The UnsignedAttributes syntax is defined as a SET OF Attributes. The
UnsignedAttributes in a signerInfo may include multiple instances of
the countersignature attribute.
*/
ASN1EncodableVector allCSAttrs = unsignedAttributeTable.getAll(CMSAttributes.counterSignature);
for (int i = 0; i < allCSAttrs.size(); ++i)
{
Attribute counterSignatureAttribute = (Attribute)allCSAttrs.get(i);
/*
A countersignature attribute can have multiple attribute values. The
syntax is defined as a SET OF AttributeValue, and there MUST be one
or more instances of AttributeValue present.
*/
ASN1Set values = counterSignatureAttribute.getAttrValues();
if (values.size() < 1)
{
// TODO Throw an appropriate exception?
}
for (Enumeration en = values.getObjects(); en.hasMoreElements();)
{
/*
Countersignature values have the same meaning as SignerInfo values
for ordinary signatures, except that:
1. The signedAttributes field MUST NOT contain a content-type
attribute; there is no content type for countersignatures.
2. The signedAttributes field MUST contain a message-digest
attribute if it contains any other attributes.
3. The input to the message-digesting process is the contents
octets of the DER encoding of the signatureValue field of the
SignerInfo value with which the attribute is associated.
*/
SignerInfo si = SignerInfo.getInstance(en.nextElement());
counterSignatures.add(new SignerInformation(si, null, new CMSProcessableByteArray(getSignature()), null));
}
}
return new SignerInformationStore(counterSignatures);
}
/**
* return the DER encoding of the signed attributes.
* @throws IOException if an encoding error occurs.
*/
public byte[] getEncodedSignedAttributes()
throws IOException
{
if (signedAttributeSet != null)
{
return signedAttributeSet.getEncoded();
}
return null;
}
private boolean doVerify(
SignerInformationVerifier verifier)
throws CMSException
{
String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID());
ContentVerifier contentVerifier;
try
{
contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, info.getDigestAlgorithm());
}
catch (OperatorCreationException e)
{
throw new CMSException("can't create content verifier: " + e.getMessage(), e);
}
try
{
OutputStream sigOut = contentVerifier.getOutputStream();
if (resultDigest == null)
{
DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID());
if (content != null)
{
OutputStream digOut = calc.getOutputStream();
if (signedAttributeSet == null)
{
if (contentVerifier instanceof RawContentVerifier)
{
content.write(digOut);
}
else
{
OutputStream cOut = new TeeOutputStream(digOut, sigOut);
content.write(cOut);
cOut.close();
}
}
else
{
content.write(digOut);
sigOut.write(this.getEncodedSignedAttributes());
}
digOut.close();
}
else if (signedAttributeSet != null)
{
sigOut.write(this.getEncodedSignedAttributes());
}
else
{
// TODO Get rid of this exception and just treat content==null as empty not missing?
throw new CMSException("data not encapsulated in signature - use detached constructor.");
}
resultDigest = calc.getDigest();
}
else
{
if (signedAttributeSet == null)
{
if (content != null)
{
content.write(sigOut);
}
}
else
{
sigOut.write(this.getEncodedSignedAttributes());
}
}
sigOut.close();
}
catch (IOException e)
{
throw new CMSException("can't process mime object to create signature.", e);
}
catch (OperatorCreationException e)
{
throw new CMSException("can't create digest calculator: " + e.getMessage(), e);
}
// RFC 3852 11.1 Check the content-type attribute is correct
{
ASN1Primitive validContentType = getSingleValuedSignedAttribute(
CMSAttributes.contentType, "content-type");
if (validContentType == null)
{
if (!isCounterSignature && signedAttributeSet != null)
{
throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
}
}
else
{
if (isCounterSignature)
{
throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
}
if (!(validContentType instanceof ASN1ObjectIdentifier))
{
throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
}
ASN1ObjectIdentifier signedContentType = (ASN1ObjectIdentifier)validContentType;
if (!signedContentType.equals(contentType))
{
throw new CMSException("content-type attribute value does not match eContentType");
}
}
}
// RFC 3852 11.2 Check the message-digest attribute is correct
{
ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute(
CMSAttributes.messageDigest, "message-digest");
if (validMessageDigest == null)
{
if (signedAttributeSet != null)
{
throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
}
}
else
{
if (!(validMessageDigest instanceof ASN1OctetString))
{
throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
}
ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest;
if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets()))
{
throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value");
}
}
}
// RFC 3852 11.4 Validate countersignature attribute(s)
{
AttributeTable signedAttrTable = this.getSignedAttributes();
if (signedAttrTable != null
&& signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0)
{
throw new CMSException("A countersignature attribute MUST NOT be a signed attribute");
}
AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
if (unsignedAttrTable != null)
{
ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature);
for (int i = 0; i < csAttrs.size(); ++i)
{
Attribute csAttr = (Attribute)csAttrs.get(i);
if (csAttr.getAttrValues().size() < 1)
{
throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue");
}
// Note: We don't recursively validate the countersignature value
}
}
}
try
{
if (signedAttributeSet == null && resultDigest != null)
{
if (contentVerifier instanceof RawContentVerifier)
{
RawContentVerifier rawVerifier = (RawContentVerifier)contentVerifier;
if (encName.equals("RSA"))
{
DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(digestAlgorithm.getAlgorithm(), DERNull.INSTANCE), resultDigest);
return rawVerifier.verify(digInfo.getEncoded(ASN1Encoding.DER), this.getSignature());
}
return rawVerifier.verify(resultDigest, this.getSignature());
}
}
return contentVerifier.verify(this.getSignature());
}
catch (IOException e)
{
throw new CMSException("can't process mime object to create signature.", e);
}
}
/**
* Verify that the given verifier can successfully verify the signature on
* this SignerInformation object.
*
* @param verifier a suitably configured SignerInformationVerifier.
* @return true if the signer information is verified, false otherwise.
* @throws org.bouncycastle.cms.CMSVerifierCertificateNotValidException if the provider has an associated certificate and the certificate is not valid at the time given as the SignerInfo's signing time.
* @throws org.bouncycastle.cms.CMSException if the verifier is unable to create a ContentVerifiers or DigestCalculators.
*/
public boolean verify(SignerInformationVerifier verifier)
throws CMSException
{
Time signingTime = getSigningTime(); // has to be validated if present.
if (verifier.hasAssociatedCertificate())
{
if (signingTime != null)
{
X509CertificateHolder dcv = verifier.getAssociatedCertificate();
if (!dcv.isValidOn(signingTime.getDate()))
{
throw new CMSVerifierCertificateNotValidException("verifier not valid at signingTime");
}
}
}
return doVerify(verifier);
}
/**
* Return the underlying ASN.1 object defining this SignerInformation object.
*
* @return a SignerInfo.
*/
public SignerInfo toASN1Structure()
{
return info;
}
private ASN1Primitive getSingleValuedSignedAttribute(
ASN1ObjectIdentifier attrOID, String printableName)
throws CMSException
{
AttributeTable unsignedAttrTable = this.getUnsignedAttributes();
if (unsignedAttrTable != null
&& unsignedAttrTable.getAll(attrOID).size() > 0)
{
throw new CMSException("The " + printableName
+ " attribute MUST NOT be an unsigned attribute");
}
AttributeTable signedAttrTable = this.getSignedAttributes();
if (signedAttrTable == null)
{
return null;
}
ASN1EncodableVector v = signedAttrTable.getAll(attrOID);
switch (v.size())
{
case 0:
return null;
case 1:
{
Attribute t = (Attribute)v.get(0);
ASN1Set attrValues = t.getAttrValues();
if (attrValues.size() != 1)
{
throw new CMSException("A " + printableName
+ " attribute MUST have a single attribute value");
}
return attrValues.getObjectAt(0).toASN1Primitive();
}
default:
throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+ printableName + " attribute");
}
}
private Time getSigningTime() throws CMSException
{
ASN1Primitive validSigningTime = getSingleValuedSignedAttribute(
CMSAttributes.signingTime, "signing-time");
if (validSigningTime == null)
{
return null;
}
try
{
return Time.getInstance(validSigningTime);
}
catch (IllegalArgumentException e)
{
throw new CMSException("signing-time attribute value not a valid 'Time' structure");
}
}
/**
* Return a signer information object with the passed in unsigned
* attributes replacing the ones that are current associated with
* the object passed in.
*
* @param signerInformation the signerInfo to be used as the basis.
* @param unsignedAttributes the unsigned attributes to add.
* @return a copy of the original SignerInformationObject with the changed attributes.
*/
public static SignerInformation replaceUnsignedAttributes(
SignerInformation signerInformation,
AttributeTable unsignedAttributes)
{
SignerInfo sInfo = signerInformation.info;
ASN1Set unsignedAttr = null;
if (unsignedAttributes != null)
{
unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector());
}
return new SignerInformation(
new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr),
signerInformation.contentType, signerInformation.content, null);
}
/**
* Return a signer information object with passed in SignerInformationStore representing counter
* signatures attached as an unsigned attribute.
*
* @param signerInformation the signerInfo to be used as the basis.
* @param counterSigners signer info objects carrying counter signature.
* @return a copy of the original SignerInformationObject with the changed attributes.
*/
public static SignerInformation addCounterSigners(
SignerInformation signerInformation,
SignerInformationStore counterSigners)
{
// TODO Perform checks from RFC 3852 11.4
SignerInfo sInfo = signerInformation.info;
AttributeTable unsignedAttr = signerInformation.getUnsignedAttributes();
ASN1EncodableVector v;
if (unsignedAttr != null)
{
v = unsignedAttr.toASN1EncodableVector();
}
else
{
v = new ASN1EncodableVector();
}
ASN1EncodableVector sigs = new ASN1EncodableVector();
for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext();)
{
sigs.add(((SignerInformation)it.next()).toASN1Structure());
}
v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs)));
return new SignerInformation(
new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(),
sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)),
signerInformation.contentType, signerInformation.content, null);
}
}

@ -0,0 +1,109 @@
package org.bouncycastle.cms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class SignerInformationStore
{
private List all = new ArrayList();
private Map table = new HashMap();
public SignerInformationStore(
Collection signerInfos)
{
Iterator it = signerInfos.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
SignerId sid = signer.getSID();
List list = (ArrayList)table.get(sid);
if (list == null)
{
list = new ArrayList(1);
table.put(sid, list);
}
list.add(signer);
}
this.all = new ArrayList(signerInfos);
}
/**
* Return the first SignerInformation object that matches the
* passed in selector. Null if there are no matches.
*
* @param selector to identify a signer
* @return a single SignerInformation object. Null if none matches.
*/
public SignerInformation get(
SignerId selector)
{
Collection list = getSigners(selector);
return list.size() == 0 ? null : (SignerInformation) list.iterator().next();
}
/**
* Return the number of signers in the collection.
*
* @return number of signers identified.
*/
public int size()
{
return all.size();
}
/**
* Return all signers in the collection
*
* @return a collection of signers.
*/
public Collection getSigners()
{
return new ArrayList(all);
}
/**
* Return possible empty collection with signers matching the passed in SignerId
*
* @param selector a signer id to select against.
* @return a collection of SignerInformation objects.
*/
public Collection getSigners(
SignerId selector)
{
if (selector.getIssuer() != null && selector.getSubjectKeyIdentifier() != null)
{
List results = new ArrayList();
Collection match1 = getSigners(new SignerId(selector.getIssuer(), selector.getSerialNumber()));
if (match1 != null)
{
results.addAll(match1);
}
Collection match2 = getSigners(new SignerId(selector.getSubjectKeyIdentifier()));
if (match2 != null)
{
results.addAll(match2);
}
return results;
}
else
{
List list = (ArrayList)table.get(selector);
return list == null ? new ArrayList() : new ArrayList(list);
}
}
}

@ -0,0 +1,50 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
public class SignerInformationVerifier
{
private ContentVerifierProvider verifierProvider;
private DigestCalculatorProvider digestProvider;
private SignatureAlgorithmIdentifierFinder sigAlgorithmFinder;
private CMSSignatureAlgorithmNameGenerator sigNameGenerator;
public SignerInformationVerifier(CMSSignatureAlgorithmNameGenerator sigNameGenerator, SignatureAlgorithmIdentifierFinder sigAlgorithmFinder, ContentVerifierProvider verifierProvider, DigestCalculatorProvider digestProvider)
{
this.sigNameGenerator = sigNameGenerator;
this.sigAlgorithmFinder = sigAlgorithmFinder;
this.verifierProvider = verifierProvider;
this.digestProvider = digestProvider;
}
public boolean hasAssociatedCertificate()
{
return verifierProvider.hasAssociatedCertificate();
}
public X509CertificateHolder getAssociatedCertificate()
{
return verifierProvider.getAssociatedCertificate();
}
public ContentVerifier getContentVerifier(AlgorithmIdentifier signingAlgorithm, AlgorithmIdentifier digestAlgorithm)
throws OperatorCreationException
{
String signatureName = sigNameGenerator.getSignatureName(digestAlgorithm, signingAlgorithm);
return verifierProvider.get(sigAlgorithmFinder.find(signatureName));
}
public DigestCalculator getDigestCalculator(AlgorithmIdentifier algorithmIdentifier)
throws OperatorCreationException
{
return digestProvider.get(algorithmIdentifier);
}
}

@ -0,0 +1,25 @@
package org.bouncycastle.cms;
import org.bouncycastle.asn1.cms.AttributeTable;
import java.util.Map;
/**
* Basic generator that just returns a preconstructed attribute table
*/
public class SimpleAttributeTableGenerator
implements CMSAttributeTableGenerator
{
private final AttributeTable attributes;
public SimpleAttributeTableGenerator(
AttributeTable attributes)
{
this.attributes = attributes;
}
public AttributeTable getAttributes(Map parameters)
{
return attributes;
}
}

@ -0,0 +1,68 @@
package org.bouncycastle.cms.jcajce;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cms.CMSAttributeTableGenerator;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
public class JcaSignerInfoGeneratorBuilder
{
private SignerInfoGeneratorBuilder builder;
public JcaSignerInfoGeneratorBuilder(DigestCalculatorProvider digestProvider)
{
builder = new SignerInfoGeneratorBuilder(digestProvider);
}
/**
* If the passed in flag is true, the signer signature will be based on the data, not
* a collection of signed attributes, and no signed attributes will be included.
*
* @return the builder object
*/
public JcaSignerInfoGeneratorBuilder setDirectSignature(boolean hasNoSignedAttributes)
{
builder.setDirectSignature(hasNoSignedAttributes);
return this;
}
public JcaSignerInfoGeneratorBuilder setSignedAttributeGenerator(CMSAttributeTableGenerator signedGen)
{
builder.setSignedAttributeGenerator(signedGen);
return this;
}
public JcaSignerInfoGeneratorBuilder setUnsignedAttributeGenerator(CMSAttributeTableGenerator unsignedGen)
{
builder.setUnsignedAttributeGenerator(unsignedGen);
return this;
}
public SignerInfoGenerator build(ContentSigner contentSigner, X509CertificateHolder certHolder)
throws OperatorCreationException
{
return builder.build(contentSigner, certHolder);
}
public SignerInfoGenerator build(ContentSigner contentSigner, byte[] keyIdentifier)
throws OperatorCreationException
{
return builder.build(contentSigner, keyIdentifier);
}
public SignerInfoGenerator build(ContentSigner contentSigner, X509Certificate certificate)
throws OperatorCreationException, CertificateEncodingException
{
return this.build(contentSigner, new JcaX509CertificateHolder(certificate));
}
}

@ -0,0 +1,180 @@
package org.bouncycastle.cms.jcajce;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSSignatureAlgorithmNameGenerator;
import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
public class JcaSignerInfoVerifierBuilder
{
private Helper helper = new Helper();
private DigestCalculatorProvider digestProvider;
private CMSSignatureAlgorithmNameGenerator sigAlgNameGen = new DefaultCMSSignatureAlgorithmNameGenerator();
private SignatureAlgorithmIdentifierFinder sigAlgIDFinder = new DefaultSignatureAlgorithmIdentifierFinder();
public JcaSignerInfoVerifierBuilder(DigestCalculatorProvider digestProvider)
{
this.digestProvider = digestProvider;
}
public JcaSignerInfoVerifierBuilder setProvider(Provider provider)
{
this.helper = new ProviderHelper(provider);
return this;
}
public JcaSignerInfoVerifierBuilder setProvider(String providerName)
{
this.helper = new NamedHelper(providerName);
return this;
}
/**
* Override the default signature algorithm name generator.
*
* @param sigAlgNameGen the algorithm name generator to use.
* @return the current builder.
*/
public JcaSignerInfoVerifierBuilder setSignatureAlgorithmNameGenerator(CMSSignatureAlgorithmNameGenerator sigAlgNameGen)
{
this.sigAlgNameGen = sigAlgNameGen;
return this;
}
public JcaSignerInfoVerifierBuilder setSignatureAlgorithmFinder(SignatureAlgorithmIdentifierFinder sigAlgIDFinder)
{
this.sigAlgIDFinder = sigAlgIDFinder;
return this;
}
public SignerInformationVerifier build(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(certHolder), digestProvider);
}
public SignerInformationVerifier build(X509Certificate certificate)
throws OperatorCreationException
{
return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(certificate), digestProvider);
}
public SignerInformationVerifier build(PublicKey pubKey)
throws OperatorCreationException
{
return new SignerInformationVerifier(sigAlgNameGen, sigAlgIDFinder, helper.createContentVerifierProvider(pubKey), digestProvider);
}
private class Helper
{
ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().build(publicKey);
}
ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().build(certificate);
}
ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new JcaContentVerifierProviderBuilder().build(certHolder);
}
DigestCalculatorProvider createDigestCalculatorProvider()
throws OperatorCreationException
{
return new JcaDigestCalculatorProviderBuilder().build();
}
}
private class NamedHelper
extends Helper
{
private final String providerName;
public NamedHelper(String providerName)
{
this.providerName = providerName;
}
ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(publicKey);
}
ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certificate);
}
DigestCalculatorProvider createDigestCalculatorProvider()
throws OperatorCreationException
{
return new JcaDigestCalculatorProviderBuilder().setProvider(providerName).build();
}
ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certHolder);
}
}
private class ProviderHelper
extends Helper
{
private final Provider provider;
public ProviderHelper(Provider provider)
{
this.provider = provider;
}
ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(provider).build(publicKey);
}
ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certificate);
}
DigestCalculatorProvider createDigestCalculatorProvider()
throws OperatorCreationException
{
return new JcaDigestCalculatorProviderBuilder().setProvider(provider).build();
}
ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certHolder);
}
}
}

@ -0,0 +1,150 @@
package org.bouncycastle.cms.jcajce;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
public class JcaSimpleSignerInfoVerifierBuilder
{
private Helper helper = new Helper();
public JcaSimpleSignerInfoVerifierBuilder setProvider(Provider provider)
{
this.helper = new ProviderHelper(provider);
return this;
}
public JcaSimpleSignerInfoVerifierBuilder setProvider(String providerName)
{
this.helper = new NamedHelper(providerName);
return this;
}
public SignerInformationVerifier build(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(certHolder), helper.createDigestCalculatorProvider());
}
public SignerInformationVerifier build(X509Certificate certificate)
throws OperatorCreationException
{
return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(certificate), helper.createDigestCalculatorProvider());
}
public SignerInformationVerifier build(PublicKey pubKey)
throws OperatorCreationException
{
return new SignerInformationVerifier(new DefaultCMSSignatureAlgorithmNameGenerator(), new DefaultSignatureAlgorithmIdentifierFinder(), helper.createContentVerifierProvider(pubKey), helper.createDigestCalculatorProvider());
}
private class Helper
{
ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().build(publicKey);
}
ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().build(certificate);
}
ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new JcaContentVerifierProviderBuilder().build(certHolder);
}
DigestCalculatorProvider createDigestCalculatorProvider()
throws OperatorCreationException
{
return new JcaDigestCalculatorProviderBuilder().build();
}
}
private class NamedHelper
extends Helper
{
private final String providerName;
public NamedHelper(String providerName)
{
this.providerName = providerName;
}
ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(publicKey);
}
ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certificate);
}
DigestCalculatorProvider createDigestCalculatorProvider()
throws OperatorCreationException
{
return new JcaDigestCalculatorProviderBuilder().setProvider(providerName).build();
}
ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new JcaContentVerifierProviderBuilder().setProvider(providerName).build(certHolder);
}
}
private class ProviderHelper
extends Helper
{
private final Provider provider;
public ProviderHelper(Provider provider)
{
this.provider = provider;
}
ContentVerifierProvider createContentVerifierProvider(PublicKey publicKey)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(provider).build(publicKey);
}
ContentVerifierProvider createContentVerifierProvider(X509Certificate certificate)
throws OperatorCreationException
{
return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certificate);
}
DigestCalculatorProvider createDigestCalculatorProvider()
throws OperatorCreationException
{
return new JcaDigestCalculatorProviderBuilder().setProvider(provider).build();
}
ContentVerifierProvider createContentVerifierProvider(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return new JcaContentVerifierProviderBuilder().setProvider(provider).build(certHolder);
}
}
}

@ -0,0 +1,27 @@
package org.bouncycastle.operator;
import java.io.OutputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface ContentSigner
{
AlgorithmIdentifier getAlgorithmIdentifier();
/**
* Returns a stream that will accept data for the purpose of calculating
* a signature. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
* the data on the fly as well.
*
* @return an OutputStream
*/
OutputStream getOutputStream();
/**
* Returns a signature based on the current data written to the stream, since the
* start or the last call to getSignature().
*
* @return bytes representing the signature.
*/
byte[] getSignature();
}

@ -0,0 +1,31 @@
package org.bouncycastle.operator;
import java.io.OutputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface ContentVerifier
{
/**
* Return the algorithm identifier describing the signature
* algorithm and parameters this expander supports.
*
* @return algorithm oid and parameters.
*/
AlgorithmIdentifier getAlgorithmIdentifier();
/**
* Returns a stream that will accept data for the purpose of calculating
* a signature for later verification. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
* the data on the fly as well.
*
* @return an OutputStream
*/
OutputStream getOutputStream();
/**
* @param expected expected value of the signature on the data.
* @return true if the signature verifies, false otherwise
*/
boolean verify(byte[] expected);
}

@ -0,0 +1,34 @@
package org.bouncycastle.operator;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
/**
* General interface for providers of ContentVerifier objects.
*/
public interface ContentVerifierProvider
{
/**
* Return whether or not this verifier has a certificate associated with it.
*
* @return true if there is an associated certificate, false otherwise.
*/
boolean hasAssociatedCertificate();
/**
* Return the associated certificate if there is one.
*
* @return a holder containing the associated certificate if there is one, null if there is not.
*/
X509CertificateHolder getAssociatedCertificate();
/**
* Return a ContentVerifier that matches the passed in algorithm identifier,
*
* @param verifierAlgorithmIdentifier the algorithm and parameters required.
* @return a matching ContentVerifier
* @throws OperatorCreationException if the required ContentVerifier cannot be created.
*/
ContentVerifier get(AlgorithmIdentifier verifierAlgorithmIdentifier)
throws OperatorCreationException;
}

@ -0,0 +1,109 @@
package org.bouncycastle.operator;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
// BEGIN android-removed
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
public class DefaultDigestAlgorithmIdentifierFinder
implements DigestAlgorithmIdentifierFinder
{
private static Map digestOids = new HashMap();
private static Map digestNameToOids = new HashMap();
static
{
//
// digests
//
// BEGIN android-removed
// digestOids.put(OIWObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
// digestOids.put(OIWObjectIdentifiers.md4WithRSA, PKCSObjectIdentifiers.md4);
// END android-removed
digestOids.put(OIWObjectIdentifiers.sha1WithRSA, OIWObjectIdentifiers.idSHA1);
digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224);
digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
// BEGIN android-removed
// digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
// digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
// END android-removed
digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5);
digestOids.put(PKCSObjectIdentifiers.sha1WithRSAEncryption, OIWObjectIdentifiers.idSHA1);
digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, OIWObjectIdentifiers.idSHA1);
digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, NISTObjectIdentifiers.id_sha224);
digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, NISTObjectIdentifiers.id_sha256);
digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, NISTObjectIdentifiers.id_sha384);
digestOids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, NISTObjectIdentifiers.id_sha512);
digestOids.put(X9ObjectIdentifiers.id_dsa_with_sha1, OIWObjectIdentifiers.idSHA1);
digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
digestOids.put(NISTObjectIdentifiers.dsa_with_sha384, NISTObjectIdentifiers.id_sha384);
digestOids.put(NISTObjectIdentifiers.dsa_with_sha512, NISTObjectIdentifiers.id_sha512);
// BEGIN android-removed
// digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, TeleTrusTObjectIdentifiers.ripemd128);
// digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, TeleTrusTObjectIdentifiers.ripemd160);
// digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
//
// digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
// digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
// END android-removed
digestNameToOids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
digestNameToOids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
digestNameToOids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
digestNameToOids.put("SHA-384", NISTObjectIdentifiers.id_sha384);
digestNameToOids.put("SHA-512", NISTObjectIdentifiers.id_sha512);
// BEGIN android-removed
// digestNameToOids.put("GOST3411", CryptoProObjectIdentifiers.gostR3411);
//
// digestNameToOids.put("MD2", PKCSObjectIdentifiers.md2);
// digestNameToOids.put("MD4", PKCSObjectIdentifiers.md4);
// END android-removed
digestNameToOids.put("MD5", PKCSObjectIdentifiers.md5);
// BEGIN android-removed
// digestNameToOids.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128);
// digestNameToOids.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160);
// digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
// END android-removed
}
public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
{
AlgorithmIdentifier digAlgId;
if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
{
digAlgId = RSASSAPSSparams.getInstance(sigAlgId.getParameters()).getHashAlgorithm();
}
else
{
digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigAlgId.getAlgorithm()), DERNull.INSTANCE);
}
return digAlgId;
}
public AlgorithmIdentifier find(String digAlgName)
{
return new AlgorithmIdentifier((ASN1ObjectIdentifier)digestNameToOids.get(digAlgName), DERNull.INSTANCE);
}
}

@ -0,0 +1,230 @@
package org.bouncycastle.operator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
// BEGIN android-removed
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.util.Strings;
public class DefaultSignatureAlgorithmIdentifierFinder
implements SignatureAlgorithmIdentifierFinder
{
private static Map algorithms = new HashMap();
private static Set noParams = new HashSet();
private static Map params = new HashMap();
private static Set pkcs15RsaEncryption = new HashSet();
private static Map digestOids = new HashMap();
private static final ASN1ObjectIdentifier ENCRYPTION_RSA = PKCSObjectIdentifiers.rsaEncryption;
private static final ASN1ObjectIdentifier ENCRYPTION_DSA = X9ObjectIdentifiers.id_dsa_with_sha1;
private static final ASN1ObjectIdentifier ENCRYPTION_ECDSA = X9ObjectIdentifiers.ecdsa_with_SHA1;
private static final ASN1ObjectIdentifier ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS;
// BEGIN android-removed
// private static final ASN1ObjectIdentifier ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94;
// private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001;
// END android-removed
static
{
// BEGIN android-removed
// algorithms.put("MD2WITHRSAENCRYPTION", PKCSObjectIdentifiers.md2WithRSAEncryption);
// algorithms.put("MD2WITHRSA", PKCSObjectIdentifiers.md2WithRSAEncryption);
// END android-removed
algorithms.put("MD5WITHRSAENCRYPTION", PKCSObjectIdentifiers.md5WithRSAEncryption);
algorithms.put("MD5WITHRSA", PKCSObjectIdentifiers.md5WithRSAEncryption);
algorithms.put("SHA1WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha1WithRSAEncryption);
algorithms.put("SHA1WITHRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption);
algorithms.put("SHA224WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha224WithRSAEncryption);
algorithms.put("SHA224WITHRSA", PKCSObjectIdentifiers.sha224WithRSAEncryption);
algorithms.put("SHA256WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha256WithRSAEncryption);
algorithms.put("SHA256WITHRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption);
algorithms.put("SHA384WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha384WithRSAEncryption);
algorithms.put("SHA384WITHRSA", PKCSObjectIdentifiers.sha384WithRSAEncryption);
algorithms.put("SHA512WITHRSAENCRYPTION", PKCSObjectIdentifiers.sha512WithRSAEncryption);
algorithms.put("SHA512WITHRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption);
algorithms.put("SHA1WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
algorithms.put("SHA224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
// BEGIN android-removed
// algorithms.put("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
// algorithms.put("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
// algorithms.put("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
// algorithms.put("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
// algorithms.put("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
// algorithms.put("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
// END android-removed
algorithms.put("SHA1WITHDSA", X9ObjectIdentifiers.id_dsa_with_sha1);
algorithms.put("DSAWITHSHA1", X9ObjectIdentifiers.id_dsa_with_sha1);
algorithms.put("SHA224WITHDSA", NISTObjectIdentifiers.dsa_with_sha224);
algorithms.put("SHA256WITHDSA", NISTObjectIdentifiers.dsa_with_sha256);
algorithms.put("SHA384WITHDSA", NISTObjectIdentifiers.dsa_with_sha384);
algorithms.put("SHA512WITHDSA", NISTObjectIdentifiers.dsa_with_sha512);
algorithms.put("SHA1WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA1);
algorithms.put("ECDSAWITHSHA1", X9ObjectIdentifiers.ecdsa_with_SHA1);
algorithms.put("SHA224WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA224);
algorithms.put("SHA256WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256);
algorithms.put("SHA384WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384);
algorithms.put("SHA512WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512);
// BEGIN android-removed
// algorithms.put("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
// algorithms.put("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
// algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// END android-removed
//
// According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
// The parameters field SHALL be NULL for RSA based signature algorithms.
//
noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA1);
noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA224);
noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA256);
noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA384);
noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA512);
noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1);
noParams.add(NISTObjectIdentifiers.dsa_with_sha224);
noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
//
// RFC 4491
//
// BEGIN android-removed
// noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
// noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
// END android-removed
//
// PKCS 1.5 encrypted algorithms
//
pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha1WithRSAEncryption);
pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha224WithRSAEncryption);
pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha256WithRSAEncryption);
pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha384WithRSAEncryption);
pkcs15RsaEncryption.add(PKCSObjectIdentifiers.sha512WithRSAEncryption);
// BEGIN android-removed
// pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
// pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
// pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
// END android-removed
//
// explicit params
//
AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
params.put("SHA1WITHRSAANDMGF1", createPSSParams(sha1AlgId, 20));
AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE);
params.put("SHA224WITHRSAANDMGF1", createPSSParams(sha224AlgId, 28));
AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
params.put("SHA256WITHRSAANDMGF1", createPSSParams(sha256AlgId, 32));
AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE);
params.put("SHA384WITHRSAANDMGF1", createPSSParams(sha384AlgId, 48));
AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
params.put("SHA512WITHRSAANDMGF1", createPSSParams(sha512AlgId, 64));
//
// digests
//
digestOids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, NISTObjectIdentifiers.id_sha224);
digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
// BEGIN android-removed
// digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
// digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
// END android-removed
digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5);
digestOids.put(PKCSObjectIdentifiers.sha1WithRSAEncryption, OIWObjectIdentifiers.idSHA1);
// BEGIN android-removed
// digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128, TeleTrusTObjectIdentifiers.ripemd128);
// digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160, TeleTrusTObjectIdentifiers.ripemd160);
// digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
// digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
// digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
// END android-removed
}
private static AlgorithmIdentifier generate(String signatureAlgorithm)
{
AlgorithmIdentifier sigAlgId;
AlgorithmIdentifier encAlgId;
AlgorithmIdentifier digAlgId;
String algorithmName = Strings.toUpperCase(signatureAlgorithm);
ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
if (sigOID == null)
{
throw new IllegalArgumentException("Unknown signature type requested: " + algorithmName);
}
if (noParams.contains(sigOID))
{
sigAlgId = new AlgorithmIdentifier(sigOID);
}
else if (params.containsKey(algorithmName))
{
sigAlgId = new AlgorithmIdentifier(sigOID, (ASN1Encodable)params.get(algorithmName));
}
else
{
sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
}
if (pkcs15RsaEncryption.contains(sigOID))
{
encAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
}
else
{
encAlgId = sigAlgId;
}
if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
{
digAlgId = ((RSASSAPSSparams)sigAlgId.getParameters()).getHashAlgorithm();
}
else
{
digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigOID), DERNull.INSTANCE);
}
return sigAlgId;
}
private static RSASSAPSSparams createPSSParams(AlgorithmIdentifier hashAlgId, int saltSize)
{
return new RSASSAPSSparams(
hashAlgId,
new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId),
new ASN1Integer(saltSize),
new ASN1Integer(1));
}
public AlgorithmIdentifier find(String sigAlgName)
{
return generate(sigAlgName);
}
}

@ -0,0 +1,24 @@
package org.bouncycastle.operator;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface DigestAlgorithmIdentifierFinder
{
/**
* Find the digest algorithm identifier that matches with
* the passed in signature algorithm identifier.
*
* @param sigAlgId the signature algorithm of interest.
* @return an algorithm identifier for the corresponding digest.
*/
AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId);
/**
* Find the algorithm identifier that matches with
* the passed in digest name.
*
* @param digAlgName the name of the digest algorithm of interest.
* @return an algorithm identifier for the digest signature.
*/
AlgorithmIdentifier find(String digAlgName);
}

@ -0,0 +1,36 @@
package org.bouncycastle.operator;
import java.io.OutputStream;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
/**
* General interface for an operator that is able to calculate a digest from
* a stream of output.
*/
public interface DigestCalculator
{
/**
* Return the algorithm identifier representing the digest implemented by
* this calculator.
*
* @return algorithm id and parameters.
*/
AlgorithmIdentifier getAlgorithmIdentifier();
/**
* Returns a stream that will accept data for the purpose of calculating
* a digest. Use org.bouncycastle.util.io.TeeOutputStream if you want to accumulate
* the data on the fly as well.
*
* @return an OutputStream
*/
OutputStream getOutputStream();
/**
* Return the digest calculated on what has been written to the calculator's output stream.
*
* @return a digest.
*/
byte[] getDigest();
}

@ -0,0 +1,9 @@
package org.bouncycastle.operator;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface DigestCalculatorProvider
{
DigestCalculator get(AlgorithmIdentifier digestAlgorithmIdentifier)
throws OperatorCreationException;
}

@ -0,0 +1,15 @@
package org.bouncycastle.operator;
public class OperatorCreationException
extends OperatorException
{
public OperatorCreationException(String msg, Throwable cause)
{
super(msg, cause);
}
public OperatorCreationException(String msg)
{
super(msg);
}
}

@ -0,0 +1,24 @@
package org.bouncycastle.operator;
public class OperatorException
extends Exception
{
private Throwable cause;
public OperatorException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public OperatorException(String msg)
{
super(msg);
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,21 @@
package org.bouncycastle.operator;
import java.io.IOException;
public class OperatorStreamException
extends IOException
{
private Throwable cause;
public OperatorStreamException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,17 @@
package org.bouncycastle.operator;
/**
* Interface for ContentVerifiers that also support raw signatures that can be
* verified using the digest of the calculated data.
*/
public interface RawContentVerifier
{
/**
* Verify that the expected signature value was derived from the passed in digest.
*
* @param digest digest calculated from the content.
* @param expected expected value of the signature
* @return true if the expected signature is derived from the digest, false otherwise.
*/
boolean verify(byte[] digest, byte[] expected);
}

@ -0,0 +1,24 @@
package org.bouncycastle.operator;
public class RuntimeOperatorException
extends RuntimeException
{
private Throwable cause;
public RuntimeOperatorException(String msg)
{
super(msg);
}
public RuntimeOperatorException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,15 @@
package org.bouncycastle.operator;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public interface SignatureAlgorithmIdentifierFinder
{
/**
* Find the signature algorithm identifier that matches with
* the passed in signature algorithm name.
*
* @param sigAlgName the name of the signature algorithm of interest.
* @return an algorithm identifier for the corresponding signature.
*/
AlgorithmIdentifier find(String sigAlgName);
}

@ -0,0 +1,152 @@
package org.bouncycastle.operator.bc;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
// BEGIN android-removed
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.ExtendedDigest;
// BEGIN android-removed
// import org.bouncycastle.crypto.digests.GOST3411Digest;
// import org.bouncycastle.crypto.digests.MD2Digest;
// import org.bouncycastle.crypto.digests.MD4Digest;
// END android-removed
import org.bouncycastle.crypto.digests.MD5Digest;
// BEGIN android-removed
// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
// import org.bouncycastle.crypto.digests.RIPEMD256Digest;
// END android-removed
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.operator.OperatorCreationException;
public class BcDefaultDigestProvider
implements BcDigestProvider
{
private static final Map lookup = createTable();
private static Map createTable()
{
Map table = new HashMap();
table.put(OIWObjectIdentifiers.idSHA1, new BcDigestProvider()
{
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
{
return new SHA1Digest();
}
});
table.put(NISTObjectIdentifiers.id_sha224, new BcDigestProvider()
{
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
{
return new SHA224Digest();
}
});
table.put(NISTObjectIdentifiers.id_sha256, new BcDigestProvider()
{
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
{
return new SHA256Digest();
}
});
table.put(NISTObjectIdentifiers.id_sha384, new BcDigestProvider()
{
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
{
return new SHA384Digest();
}
});
table.put(NISTObjectIdentifiers.id_sha512, new BcDigestProvider()
{
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
{
return new SHA512Digest();
}
});
table.put(PKCSObjectIdentifiers.md5, new BcDigestProvider()
{
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
{
return new MD5Digest();
}
});
// BEGIN android-removed
// table.put(PKCSObjectIdentifiers.md4, new BcDigestProvider()
// {
// public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
// {
// return new MD4Digest();
// }
// });
// table.put(PKCSObjectIdentifiers.md2, new BcDigestProvider()
// {
// public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
// {
// return new MD2Digest();
// }
// });
// table.put(CryptoProObjectIdentifiers.gostR3411, new BcDigestProvider()
// {
// public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
// {
// return new GOST3411Digest();
// }
// });
// table.put(TeleTrusTObjectIdentifiers.ripemd128, new BcDigestProvider()
// {
// public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
// {
// return new RIPEMD128Digest();
// }
// });
// table.put(TeleTrusTObjectIdentifiers.ripemd160, new BcDigestProvider()
// {
// public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
// {
// return new RIPEMD160Digest();
// }
// });
// table.put(TeleTrusTObjectIdentifiers.ripemd256, new BcDigestProvider()
// {
// public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
// {
// return new RIPEMD256Digest();
// }
// });
// END android-removed
return Collections.unmodifiableMap(table);
}
public static final BcDigestProvider INSTANCE = new BcDefaultDigestProvider();
private BcDefaultDigestProvider()
{
}
public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
throws OperatorCreationException
{
BcDigestProvider extProv = (BcDigestProvider)lookup.get(digestAlgorithmIdentifier.getAlgorithm());
if (extProv == null)
{
throw new OperatorCreationException("cannot recognise digest");
}
return extProv.get(digestAlgorithmIdentifier);
}
}

@ -0,0 +1,82 @@
package org.bouncycastle.operator.bc;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
public class BcDigestCalculatorProvider
implements DigestCalculatorProvider
{
private BcDigestProvider digestProvider = BcDefaultDigestProvider.INSTANCE;
public DigestCalculator get(final AlgorithmIdentifier algorithm)
throws OperatorCreationException
{
Digest dig = digestProvider.get(algorithm);
final DigestOutputStream stream = new DigestOutputStream(dig);
return new DigestCalculator()
{
public AlgorithmIdentifier getAlgorithmIdentifier()
{
return algorithm;
}
public OutputStream getOutputStream()
{
return stream;
}
public byte[] getDigest()
{
return stream.getDigest();
}
};
}
private class DigestOutputStream
extends OutputStream
{
private Digest dig;
DigestOutputStream(Digest dig)
{
this.dig = dig;
}
public void write(byte[] bytes, int off, int len)
throws IOException
{
dig.update(bytes, off, len);
}
public void write(byte[] bytes)
throws IOException
{
dig.update(bytes, 0, bytes.length);
}
public void write(int b)
throws IOException
{
dig.update((byte)b);
}
byte[] getDigest()
{
byte[] d = new byte[dig.getDigestSize()];
dig.doFinal(d, 0);
return d;
}
}
}

@ -0,0 +1,11 @@
package org.bouncycastle.operator.bc;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.operator.OperatorCreationException;
public interface BcDigestProvider
{
ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
throws OperatorCreationException;
}

@ -0,0 +1,160 @@
package org.bouncycastle.operator.jcajce;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OperatorStreamException;
import org.bouncycastle.operator.RuntimeOperatorException;
public class JcaContentSignerBuilder
{
private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
private SecureRandom random;
private String signatureAlgorithm;
private AlgorithmIdentifier sigAlgId;
public JcaContentSignerBuilder(String signatureAlgorithm)
{
this.signatureAlgorithm = signatureAlgorithm;
this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
}
public JcaContentSignerBuilder setProvider(Provider provider)
{
this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
return this;
}
public JcaContentSignerBuilder setProvider(String providerName)
{
this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
return this;
}
public JcaContentSignerBuilder setSecureRandom(SecureRandom random)
{
this.random = random;
return this;
}
public ContentSigner build(PrivateKey privateKey)
throws OperatorCreationException
{
try
{
final Signature sig = helper.createSignature(sigAlgId);
if (random != null)
{
sig.initSign(privateKey, random);
}
else
{
sig.initSign(privateKey);
}
return new ContentSigner()
{
private SignatureOutputStream stream = new SignatureOutputStream(sig);
public AlgorithmIdentifier getAlgorithmIdentifier()
{
return sigAlgId;
}
public OutputStream getOutputStream()
{
return stream;
}
public byte[] getSignature()
{
try
{
return stream.getSignature();
}
catch (SignatureException e)
{
throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
}
}
};
}
catch (GeneralSecurityException e)
{
throw new OperatorCreationException("cannot create signer: " + e.getMessage(), e);
}
}
private class SignatureOutputStream
extends OutputStream
{
private Signature sig;
SignatureOutputStream(Signature sig)
{
this.sig = sig;
}
public void write(byte[] bytes, int off, int len)
throws IOException
{
try
{
sig.update(bytes, off, len);
}
catch (SignatureException e)
{
throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
}
}
public void write(byte[] bytes)
throws IOException
{
try
{
sig.update(bytes);
}
catch (SignatureException e)
{
throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
}
}
public void write(int b)
throws IOException
{
try
{
sig.update((byte)b);
}
catch (SignatureException e)
{
throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
}
}
byte[] getSignature()
throws SignatureException
{
return sig.sign();
}
}
}

@ -0,0 +1,312 @@
package org.bouncycastle.operator.jcajce;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.operator.ContentVerifier;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OperatorStreamException;
import org.bouncycastle.operator.RawContentVerifier;
import org.bouncycastle.operator.RuntimeOperatorException;
public class JcaContentVerifierProviderBuilder
{
private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
public JcaContentVerifierProviderBuilder()
{
}
public JcaContentVerifierProviderBuilder setProvider(Provider provider)
{
this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
return this;
}
public JcaContentVerifierProviderBuilder setProvider(String providerName)
{
this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
return this;
}
public ContentVerifierProvider build(X509CertificateHolder certHolder)
throws OperatorCreationException, CertificateException
{
return build(helper.convertCertificate(certHolder));
}
public ContentVerifierProvider build(final X509Certificate certificate)
throws OperatorCreationException
{
final X509CertificateHolder certHolder;
try
{
certHolder = new JcaX509CertificateHolder(certificate);
}
catch (CertificateEncodingException e)
{
throw new OperatorCreationException("cannot process certificate: " + e.getMessage(), e);
}
return new ContentVerifierProvider()
{
private SignatureOutputStream stream;
public boolean hasAssociatedCertificate()
{
return true;
}
public X509CertificateHolder getAssociatedCertificate()
{
return certHolder;
}
public ContentVerifier get(AlgorithmIdentifier algorithm)
throws OperatorCreationException
{
try
{
Signature sig = helper.createSignature(algorithm);
sig.initVerify(certificate.getPublicKey());
stream = new SignatureOutputStream(sig);
}
catch (GeneralSecurityException e)
{
throw new OperatorCreationException("exception on setup: " + e, e);
}
Signature rawSig = createRawSig(algorithm, certificate.getPublicKey());
if (rawSig != null)
{
return new RawSigVerifier(algorithm, stream, rawSig);
}
else
{
return new SigVerifier(algorithm, stream);
}
}
};
}
public ContentVerifierProvider build(final PublicKey publicKey)
throws OperatorCreationException
{
return new ContentVerifierProvider()
{
public boolean hasAssociatedCertificate()
{
return false;
}
public X509CertificateHolder getAssociatedCertificate()
{
return null;
}
public ContentVerifier get(AlgorithmIdentifier algorithm)
throws OperatorCreationException
{
SignatureOutputStream stream = createSignatureStream(algorithm, publicKey);
Signature rawSig = createRawSig(algorithm, publicKey);
if (rawSig != null)
{
return new RawSigVerifier(algorithm, stream, rawSig);
}
else
{
return new SigVerifier(algorithm, stream);
}
}
};
}
public ContentVerifierProvider build(SubjectPublicKeyInfo publicKey)
throws OperatorCreationException
{
return this.build(helper.convertPublicKey(publicKey));
}
private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey)
throws OperatorCreationException
{
try
{
Signature sig = helper.createSignature(algorithm);
sig.initVerify(publicKey);
return new SignatureOutputStream(sig);
}
catch (GeneralSecurityException e)
{
throw new OperatorCreationException("exception on setup: " + e, e);
}
}
private Signature createRawSig(AlgorithmIdentifier algorithm, PublicKey publicKey)
{
Signature rawSig;
try
{
rawSig = helper.createRawSignature(algorithm);
if (rawSig != null)
{
rawSig.initVerify(publicKey);
}
}
catch (Exception e)
{
rawSig = null;
}
return rawSig;
}
private class SigVerifier
implements ContentVerifier
{
private SignatureOutputStream stream;
private AlgorithmIdentifier algorithm;
SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream)
{
this.algorithm = algorithm;
this.stream = stream;
}
public AlgorithmIdentifier getAlgorithmIdentifier()
{
return algorithm;
}
public OutputStream getOutputStream()
{
if (stream == null)
{
throw new IllegalStateException("verifier not initialised");
}
return stream;
}
public boolean verify(byte[] expected)
{
try
{
return stream.verify(expected);
}
catch (SignatureException e)
{
throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
}
}
}
private class RawSigVerifier
extends SigVerifier
implements RawContentVerifier
{
private Signature rawSignature;
RawSigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream, Signature rawSignature)
{
super(algorithm, stream);
this.rawSignature = rawSignature;
}
public boolean verify(byte[] digest, byte[] expected)
{
try
{
rawSignature.update(digest);
return rawSignature.verify(expected);
}
catch (SignatureException e)
{
throw new RuntimeOperatorException("exception obtaining raw signature: " + e.getMessage(), e);
}
}
}
private class SignatureOutputStream
extends OutputStream
{
private Signature sig;
SignatureOutputStream(Signature sig)
{
this.sig = sig;
}
public void write(byte[] bytes, int off, int len)
throws IOException
{
try
{
sig.update(bytes, off, len);
}
catch (SignatureException e)
{
throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
}
}
public void write(byte[] bytes)
throws IOException
{
try
{
sig.update(bytes);
}
catch (SignatureException e)
{
throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
}
}
public void write(int b)
throws IOException
{
try
{
sig.update((byte)b);
}
catch (SignatureException e)
{
throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
}
}
boolean verify(byte[] expected)
throws SignatureException
{
return sig.verify(expected);
}
}
}

@ -0,0 +1,114 @@
package org.bouncycastle.operator.jcajce;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.Provider;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
public class JcaDigestCalculatorProviderBuilder
{
private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
public JcaDigestCalculatorProviderBuilder()
{
}
public JcaDigestCalculatorProviderBuilder setProvider(Provider provider)
{
this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
return this;
}
public JcaDigestCalculatorProviderBuilder setProvider(String providerName)
{
this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
return this;
}
public DigestCalculatorProvider build()
throws OperatorCreationException
{
return new DigestCalculatorProvider()
{
public DigestCalculator get(final AlgorithmIdentifier algorithm)
throws OperatorCreationException
{
final DigestOutputStream stream;
try
{
MessageDigest dig = helper.createDigest(algorithm);
stream = new DigestOutputStream(dig);
}
catch (GeneralSecurityException e)
{
throw new OperatorCreationException("exception on setup: " + e, e);
}
return new DigestCalculator()
{
public AlgorithmIdentifier getAlgorithmIdentifier()
{
return algorithm;
}
public OutputStream getOutputStream()
{
return stream;
}
public byte[] getDigest()
{
return stream.getDigest();
}
};
}
};
}
private class DigestOutputStream
extends OutputStream
{
private MessageDigest dig;
DigestOutputStream(MessageDigest dig)
{
this.dig = dig;
}
public void write(byte[] bytes, int off, int len)
throws IOException
{
dig.update(bytes, off, len);
}
public void write(byte[] bytes)
throws IOException
{
dig.update(bytes);
}
public void write(int b)
throws IOException
{
dig.update((byte)b);
}
byte[] getDigest()
{
return dig.digest();
}
}
}

@ -0,0 +1,477 @@
package org.bouncycastle.operator.jcajce;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PSSParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
// BEGIN android-removed
// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
// END android-removed
import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jcajce.JcaJceHelper;
import org.bouncycastle.jcajce.JcaJceUtils;
import org.bouncycastle.operator.OperatorCreationException;
class OperatorHelper
{
private static final Map oids = new HashMap();
private static final Map asymmetricWrapperAlgNames = new HashMap();
private static final Map symmetricWrapperAlgNames = new HashMap();
private static final Map symmetricKeyAlgNames = new HashMap();
static
{
//
// reverse mappings
//
oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
// BEGIN android-removed
// oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
// oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
// END android-removed
oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
// BEGIN android-removed
// oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
// END android-removed
oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
oids.put(OIWObjectIdentifiers.idSHA1, "SHA-1");
oids.put(NISTObjectIdentifiers.id_sha224, "SHA-224");
oids.put(NISTObjectIdentifiers.id_sha256, "SHA-256");
oids.put(NISTObjectIdentifiers.id_sha384, "SHA-384");
oids.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
oids.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128");
oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160");
oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-256");
asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWrap");
symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap, "RC2Wrap");
symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes128_wrap, "AESWrap");
symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes192_wrap, "AESWrap");
symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes256_wrap, "AESWrap");
symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia128_wrap, "CamelliaWrap");
symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia192_wrap, "CamelliaWrap");
symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia256_wrap, "CamelliaWrap");
symmetricWrapperAlgNames.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, "SEEDWrap");
symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
symmetricKeyAlgNames.put(NISTObjectIdentifiers.aes, "AES");
symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes128_CBC, "AES");
symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes192_CBC, "AES");
symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes256_CBC, "AES");
symmetricKeyAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
symmetricKeyAlgNames.put(PKCSObjectIdentifiers.RC2_CBC, "RC2");
}
private JcaJceHelper helper;
OperatorHelper(JcaJceHelper helper)
{
this.helper = helper;
}
Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames)
throws OperatorCreationException
{
try
{
String cipherName = null;
if (!extraAlgNames.isEmpty())
{
cipherName = (String)extraAlgNames.get(algorithm);
}
if (cipherName == null)
{
cipherName = (String)asymmetricWrapperAlgNames.get(algorithm);
}
if (cipherName != null)
{
try
{
// this is reversed as the Sun policy files now allow unlimited strength RSA
return helper.createCipher(cipherName);
}
catch (NoSuchAlgorithmException e)
{
// try alternate for RSA
if (cipherName.equals("RSA/ECB/PKCS1Padding"))
{
try
{
return helper.createCipher("RSA/NONE/PKCS1Padding");
}
catch (NoSuchAlgorithmException ex)
{
// Ignore
}
}
// Ignore
}
}
return helper.createCipher(algorithm.getId());
}
catch (GeneralSecurityException e)
{
throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
}
}
Cipher createSymmetricWrapper(ASN1ObjectIdentifier algorithm)
throws OperatorCreationException
{
try
{
String cipherName = (String)symmetricWrapperAlgNames.get(algorithm);
if (cipherName != null)
{
try
{
// this is reversed as the Sun policy files now allow unlimited strength RSA
return helper.createCipher(cipherName);
}
catch (NoSuchAlgorithmException e)
{
// Ignore
}
}
return helper.createCipher(algorithm.getId());
}
catch (GeneralSecurityException e)
{
throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
}
}
AlgorithmParameters createAlgorithmParameters(AlgorithmIdentifier cipherAlgId)
throws OperatorCreationException
{
AlgorithmParameters parameters;
if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption))
{
return null;
}
try
{
parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId());
}
catch (NoSuchAlgorithmException e)
{
return null; // There's a good chance there aren't any!
}
catch (NoSuchProviderException e)
{
throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
}
try
{
parameters.init(cipherAlgId.getParameters().toASN1Primitive().getEncoded());
}
catch (IOException e)
{
throw new OperatorCreationException("cannot initialise algorithm parameters: " + e.getMessage(), e);
}
return parameters;
}
MessageDigest createDigest(AlgorithmIdentifier digAlgId)
throws GeneralSecurityException
{
MessageDigest dig;
try
{
dig = helper.createDigest(getDigestAlgName(digAlgId.getAlgorithm()));
}
catch (NoSuchAlgorithmException e)
{
//
// try an alternate
//
if (oids.get(digAlgId.getAlgorithm()) != null)
{
String digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
dig = helper.createDigest(digestAlgorithm);
}
else
{
throw e;
}
}
return dig;
}
Signature createSignature(AlgorithmIdentifier sigAlgId)
throws GeneralSecurityException
{
Signature sig;
try
{
sig = helper.createSignature(getSignatureName(sigAlgId));
}
catch (NoSuchAlgorithmException e)
{
//
// try an alternate
//
if (oids.get(sigAlgId.getAlgorithm()) != null)
{
String signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
sig = helper.createSignature(signatureAlgorithm);
}
else
{
throw e;
}
}
return sig;
}
public Signature createRawSignature(AlgorithmIdentifier algorithm)
{
Signature sig;
try
{
String algName = getSignatureName(algorithm);
algName = "NONE" + algName.substring(algName.indexOf("WITH"));
sig = helper.createSignature(algName);
// RFC 4056
// When the id-RSASSA-PSS algorithm identifier is used for a signature,
// the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params.
if (algorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
{
AlgorithmParameters params = helper.createAlgorithmParameters(algName);
JcaJceUtils.loadParameters(params, algorithm.getParameters());
PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class);
sig.setParameter(spec);
}
}
catch (Exception e)
{
return null;
}
return sig;
}
private static String getSignatureName(
AlgorithmIdentifier sigAlgId)
{
ASN1Encodable params = sigAlgId.getParameters();
if (params != null && !DERNull.INSTANCE.equals(params))
{
if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
{
RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
}
}
if (oids.containsKey(sigAlgId.getAlgorithm()))
{
return (String)oids.get(sigAlgId.getAlgorithm());
}
return sigAlgId.getAlgorithm().getId();
}
private static String getDigestAlgName(
ASN1ObjectIdentifier digestAlgOID)
{
if (PKCSObjectIdentifiers.md5.equals(digestAlgOID))
{
return "MD5";
}
else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID))
{
return "SHA1";
}
else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
{
return "SHA224";
}
else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
{
return "SHA256";
}
else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID))
{
return "SHA384";
}
else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID))
{
return "SHA512";
}
// BEGIN android-removed
// else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
// {
// return "RIPEMD128";
// }
// else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
// {
// return "RIPEMD160";
// }
// else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
// {
// return "RIPEMD256";
// }
// else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
// {
// return "GOST3411";
// }
// END android-removed
else
{
return digestAlgOID.getId();
}
}
public X509Certificate convertCertificate(X509CertificateHolder certHolder)
throws CertificateException
{
try
{
CertificateFactory certFact = helper.createCertificateFactory("X.509");
return (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(certHolder.getEncoded()));
}
catch (IOException e)
{
throw new OpCertificateException("cannot get encoded form of certificate: " + e.getMessage(), e);
}
catch (NoSuchAlgorithmException e)
{
throw new OpCertificateException("cannot create certificate factory: " + e.getMessage(), e);
}
catch (NoSuchProviderException e)
{
throw new OpCertificateException("cannot find factory provider: " + e.getMessage(), e);
}
}
public PublicKey convertPublicKey(SubjectPublicKeyInfo publicKeyInfo)
throws OperatorCreationException
{
try
{
KeyFactory keyFact = helper.createKeyFactory(publicKeyInfo.getAlgorithm().getAlgorithm().getId());
return keyFact.generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded()));
}
catch (IOException e)
{
throw new OperatorCreationException("cannot get encoded form of key: " + e.getMessage(), e);
}
catch (NoSuchAlgorithmException e)
{
throw new OperatorCreationException("cannot create key factory: " + e.getMessage(), e);
}
catch (NoSuchProviderException e)
{
throw new OperatorCreationException("cannot find factory provider: " + e.getMessage(), e);
}
catch (InvalidKeySpecException e)
{
throw new OperatorCreationException("cannot create key factory: " + e.getMessage(), e);
}
}
// TODO: put somewhere public so cause easily accessed
private static class OpCertificateException
extends CertificateException
{
private Throwable cause;
public OpCertificateException(String msg, Throwable cause)
{
super(msg);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}
String getKeyAlgorithmName(ASN1ObjectIdentifier oid)
{
String name = (String)symmetricKeyAlgNames.get(oid);
if (name != null)
{
return name;
}
return oid.getId();
}
}

@ -0,0 +1,26 @@
// Copyright 2023 yuyezhong@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
plugins {
`java-library`
}
repositories {
mavenCentral()
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

@ -0,0 +1,10 @@
package org.bouncycastle.asn1;
import java.io.IOException;
public interface ASN1ApplicationSpecificParser
extends ASN1Encodable, InMemoryRepresentable
{
ASN1Encodable readObject()
throws IOException;
}

@ -0,0 +1,15 @@
package org.bouncycastle.asn1;
public class ASN1Boolean
extends DERBoolean
{
public ASN1Boolean(boolean value)
{
super(value);
}
ASN1Boolean(byte[] value)
{
super(value);
}
}

@ -0,0 +1,14 @@
package org.bouncycastle.asn1;
/**
* Marker interface for CHOICE objects - if you implement this in a role your
* own object any attempt to tag the object implicitly will convert the tag to
* an explicit one as the encoding rules require.
* <p>
* If you use this interface your class should also implement the getInstance
* pattern which takes a tag object and the tagging mode used.
*/
public interface ASN1Choice
{
// marker interface
}

@ -0,0 +1,6 @@
package org.bouncycastle.asn1;
public interface ASN1Encodable
{
ASN1Primitive toASN1Primitive();
}

@ -0,0 +1,36 @@
package org.bouncycastle.asn1;
import java.util.Enumeration;
import java.util.Vector;
public class ASN1EncodableVector
{
Vector v = new Vector();
public ASN1EncodableVector()
{
}
public void add(ASN1Encodable obj)
{
v.addElement(obj);
}
public void addAll(ASN1EncodableVector other)
{
for (Enumeration en = other.v.elements(); en.hasMoreElements();)
{
v.addElement(en.nextElement());
}
}
public ASN1Encodable get(int i)
{
return (ASN1Encodable)v.elementAt(i);
}
public int size()
{
return v.size();
}
}

@ -0,0 +1,8 @@
package org.bouncycastle.asn1;
public interface ASN1Encoding
{
static final String DER = "DER";
static final String DL = "DL";
static final String BER = "BER";
}

@ -0,0 +1,22 @@
package org.bouncycastle.asn1;
import java.math.BigInteger;
public class ASN1Enumerated
extends DEREnumerated
{
ASN1Enumerated(byte[] bytes)
{
super(bytes);
}
public ASN1Enumerated(BigInteger value)
{
super(value);
}
public ASN1Enumerated(int value)
{
super(value);
}
}

@ -0,0 +1,25 @@
package org.bouncycastle.asn1;
import java.io.IOException;
public class ASN1Exception
extends IOException
{
private Throwable cause;
ASN1Exception(String message)
{
super(message);
}
ASN1Exception(String message, Throwable cause)
{
super(message);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,22 @@
package org.bouncycastle.asn1;
import java.util.Date;
public class ASN1GeneralizedTime
extends DERGeneralizedTime
{
ASN1GeneralizedTime(byte[] bytes)
{
super(bytes);
}
public ASN1GeneralizedTime(Date time)
{
super(time);
}
public ASN1GeneralizedTime(String time)
{
super(time);
}
}

@ -0,0 +1,15 @@
package org.bouncycastle.asn1;
import java.io.OutputStream;
public abstract class ASN1Generator
{
protected OutputStream _out;
public ASN1Generator(OutputStream out)
{
_out = out;
}
public abstract OutputStream getRawOutputStream();
}

@ -0,0 +1,466 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.util.io.Streams;
/**
* a general purpose ASN.1 decoder - note: this class differs from the
* others in that it returns null after it has read the last object in
* the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
* returned.
*/
public class ASN1InputStream
extends FilterInputStream
implements BERTags
{
private final int limit;
private final boolean lazyEvaluate;
private final byte[][] tmpBuffers;
public ASN1InputStream(
InputStream is)
{
this(is, StreamUtil.findLimit(is));
}
/**
* Create an ASN1InputStream based on the input byte array. The length of DER objects in
* the stream is automatically limited to the length of the input array.
*
* @param input array containing ASN.1 encoded data.
*/
public ASN1InputStream(
byte[] input)
{
this(new ByteArrayInputStream(input), input.length);
}
/**
* Create an ASN1InputStream based on the input byte array. The length of DER objects in
* the stream is automatically limited to the length of the input array.
*
* @param input array containing ASN.1 encoded data.
* @param lazyEvaluate true if parsing inside constructed objects can be delayed.
*/
public ASN1InputStream(
byte[] input,
boolean lazyEvaluate)
{
this(new ByteArrayInputStream(input), input.length, lazyEvaluate);
}
/**
* Create an ASN1InputStream where no DER object will be longer than limit.
*
* @param input stream containing ASN.1 encoded data.
* @param limit maximum size of a DER encoded object.
*/
public ASN1InputStream(
InputStream input,
int limit)
{
this(input, limit, false);
}
/**
* Create an ASN1InputStream where no DER object will be longer than limit, and constructed
* objects such as sequences will be parsed lazily.
*
* @param input stream containing ASN.1 encoded data.
* @param lazyEvaluate true if parsing inside constructed objects can be delayed.
*/
public ASN1InputStream(
InputStream input,
boolean lazyEvaluate)
{
this(input, StreamUtil.findLimit(input), lazyEvaluate);
}
/**
* Create an ASN1InputStream where no DER object will be longer than limit, and constructed
* objects such as sequences will be parsed lazily.
*
* @param input stream containing ASN.1 encoded data.
* @param limit maximum size of a DER encoded object.
* @param lazyEvaluate true if parsing inside constructed objects can be delayed.
*/
public ASN1InputStream(
InputStream input,
int limit,
boolean lazyEvaluate)
{
super(input);
this.limit = limit;
this.lazyEvaluate = lazyEvaluate;
this.tmpBuffers = new byte[11][];
}
int getLimit()
{
return limit;
}
protected int readLength()
throws IOException
{
return readLength(this, limit);
}
protected void readFully(
byte[] bytes)
throws IOException
{
if (Streams.readFully(this, bytes) != bytes.length)
{
throw new EOFException("EOF encountered in middle of object");
}
}
/**
* build an object given its tag and the number of bytes to construct it from.
*/
protected ASN1Primitive buildObject(
int tag,
int tagNo,
int length)
throws IOException
{
boolean isConstructed = (tag & CONSTRUCTED) != 0;
DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length);
if ((tag & APPLICATION) != 0)
{
return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
}
if ((tag & TAGGED) != 0)
{
return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo);
}
if (isConstructed)
{
// TODO There are other tags that may be constructed (e.g. BIT_STRING)
switch (tagNo)
{
case OCTET_STRING:
//
// yes, people actually do this...
//
ASN1EncodableVector v = buildDEREncodableVector(defIn);
ASN1OctetString[] strings = new ASN1OctetString[v.size()];
for (int i = 0; i != strings.length; i++)
{
strings[i] = (ASN1OctetString)v.get(i);
}
return new BEROctetString(strings);
case SEQUENCE:
if (lazyEvaluate)
{
return new LazyEncodedSequence(defIn.toByteArray());
}
else
{
return DERFactory.createSequence(buildDEREncodableVector(defIn));
}
case SET:
return DERFactory.createSet(buildDEREncodableVector(defIn));
case EXTERNAL:
return new DERExternal(buildDEREncodableVector(defIn));
default:
throw new IOException("unknown tag " + tagNo + " encountered");
}
}
return createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
}
ASN1EncodableVector buildEncodableVector()
throws IOException
{
ASN1EncodableVector v = new ASN1EncodableVector();
ASN1Primitive o;
while ((o = readObject()) != null)
{
v.add(o);
}
return v;
}
ASN1EncodableVector buildDEREncodableVector(
DefiniteLengthInputStream dIn) throws IOException
{
return new ASN1InputStream(dIn).buildEncodableVector();
}
public ASN1Primitive readObject()
throws IOException
{
int tag = read();
if (tag <= 0)
{
if (tag == 0)
{
throw new IOException("unexpected end-of-contents marker");
}
return null;
}
//
// calculate tag number
//
int tagNo = readTagNumber(this, tag);
boolean isConstructed = (tag & CONSTRUCTED) != 0;
//
// calculate length
//
int length = readLength();
if (length < 0) // indefinite length method
{
if (!isConstructed)
{
throw new IOException("indefinite length primitive encoding encountered");
}
IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
ASN1StreamParser sp = new ASN1StreamParser(indIn, limit);
if ((tag & APPLICATION) != 0)
{
return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject();
}
if ((tag & TAGGED) != 0)
{
return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject();
}
// TODO There are other tags that may be constructed (e.g. BIT_STRING)
switch (tagNo)
{
case OCTET_STRING:
return new BEROctetStringParser(sp).getLoadedObject();
case SEQUENCE:
return new BERSequenceParser(sp).getLoadedObject();
case SET:
return new BERSetParser(sp).getLoadedObject();
case EXTERNAL:
return new DERExternalParser(sp).getLoadedObject();
default:
throw new IOException("unknown BER object encountered");
}
}
else
{
try
{
return buildObject(tag, tagNo, length);
}
catch (IllegalArgumentException e)
{
throw new ASN1Exception("corrupted stream detected", e);
}
}
}
static int readTagNumber(InputStream s, int tag)
throws IOException
{
int tagNo = tag & 0x1f;
//
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
//
if (tagNo == 0x1f)
{
tagNo = 0;
int b = s.read();
// X.690-0207 8.1.2.4.2
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
if ((b & 0x7f) == 0) // Note: -1 will pass
{
throw new IOException("corrupted stream - invalid high tag number found");
}
while ((b >= 0) && ((b & 0x80) != 0))
{
tagNo |= (b & 0x7f);
tagNo <<= 7;
b = s.read();
}
if (b < 0)
{
throw new EOFException("EOF found inside tag value.");
}
tagNo |= (b & 0x7f);
}
return tagNo;
}
static int readLength(InputStream s, int limit)
throws IOException
{
int length = s.read();
if (length < 0)
{
throw new EOFException("EOF found when length expected");
}
if (length == 0x80)
{
return -1; // indefinite-length encoding
}
if (length > 127)
{
int size = length & 0x7f;
// Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
if (size > 4)
{
throw new IOException("DER length more than 4 bytes: " + size);
}
length = 0;
for (int i = 0; i < size; i++)
{
int next = s.read();
if (next < 0)
{
throw new EOFException("EOF found reading length");
}
length = (length << 8) + next;
}
if (length < 0)
{
throw new IOException("corrupted stream - negative length found");
}
if (length >= limit) // after all we must have read at least 1 byte
{
throw new IOException("corrupted stream - out of bounds length found");
}
}
return length;
}
private static byte[] getBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
throws IOException
{
int len = defIn.getRemaining();
if (defIn.getRemaining() < tmpBuffers.length)
{
byte[] buf = tmpBuffers[len];
if (buf == null)
{
buf = tmpBuffers[len] = new byte[len];
}
Streams.readFully(defIn, buf);
return buf;
}
else
{
return defIn.toByteArray();
}
}
private static char[] getBMPCharBuffer(DefiniteLengthInputStream defIn)
throws IOException
{
int len = defIn.getRemaining() / 2;
char[] buf = new char[len];
int totalRead = 0;
while (totalRead < len)
{
int ch1 = defIn.read();
if (ch1 < 0)
{
break;
}
int ch2 = defIn.read();
if (ch2 < 0)
{
break;
}
buf[totalRead++] = (char)((ch1 << 8) | (ch2 & 0xff));
}
return buf;
}
static ASN1Primitive createPrimitiveDERObject(
int tagNo,
DefiniteLengthInputStream defIn,
byte[][] tmpBuffers)
throws IOException
{
switch (tagNo)
{
case BIT_STRING:
return DERBitString.fromInputStream(defIn.getRemaining(), defIn);
case BMP_STRING:
return new DERBMPString(getBMPCharBuffer(defIn));
case BOOLEAN:
return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers));
case ENUMERATED:
return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers));
case GENERALIZED_TIME:
return new ASN1GeneralizedTime(defIn.toByteArray());
case GENERAL_STRING:
return new DERGeneralString(defIn.toByteArray());
case IA5_STRING:
return new DERIA5String(defIn.toByteArray());
case INTEGER:
return new ASN1Integer(defIn.toByteArray());
case NULL:
return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?)
case NUMERIC_STRING:
return new DERNumericString(defIn.toByteArray());
case OBJECT_IDENTIFIER:
return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers));
case OCTET_STRING:
return new DEROctetString(defIn.toByteArray());
case PRINTABLE_STRING:
return new DERPrintableString(defIn.toByteArray());
case T61_STRING:
return new DERT61String(defIn.toByteArray());
case UNIVERSAL_STRING:
return new DERUniversalString(defIn.toByteArray());
case UTC_TIME:
return new ASN1UTCTime(defIn.toByteArray());
case UTF8_STRING:
return new DERUTF8String(defIn.toByteArray());
case VISIBLE_STRING:
return new DERVisibleString(defIn.toByteArray());
default:
throw new IOException("unknown tag " + tagNo + " encountered");
}
}
}

@ -0,0 +1,22 @@
package org.bouncycastle.asn1;
import java.math.BigInteger;
public class ASN1Integer
extends DERInteger
{
ASN1Integer(byte[] bytes)
{
super(bytes);
}
public ASN1Integer(BigInteger value)
{
super(value);
}
public ASN1Integer(long value)
{
super(value);
}
}

@ -0,0 +1,69 @@
package org.bouncycastle.asn1;
import java.io.IOException;
/**
* A NULL object.
*/
public abstract class ASN1Null
extends ASN1Primitive
{
/**
* @deprecated use DERNull.INSTANCE
*/
// BEGIN android-changed
/*package*/ ASN1Null()
{
}
// END android-changed
public static ASN1Null getInstance(Object o)
{
if (o instanceof ASN1Null)
{
return (ASN1Null)o;
}
if (o != null)
{
try
{
return ASN1Null.getInstance(ASN1Primitive.fromByteArray((byte[])o));
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct NULL from byte[]: " + e.getMessage());
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("unknown object in getInstance(): " + o.getClass().getName());
}
}
return null;
}
public int hashCode()
{
return -1;
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1Null))
{
return false;
}
return true;
}
abstract void encode(ASN1OutputStream out)
throws IOException;
public String toString()
{
return "NULL";
}
}

@ -0,0 +1,97 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public abstract class ASN1Object
implements ASN1Encodable
{
/**
* Return the default BER or DER encoding for this object.
*
* @return BER/DER byte encoded object.
* @throws java.io.IOException on encoding error.
*/
public byte[] getEncoded()
throws IOException
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
aOut.writeObject(this);
return bOut.toByteArray();
}
/**
* Return either the default for "BER" or a DER encoding if "DER" is specified.
*
* @param encoding name of encoding to use.
* @return byte encoded object.
* @throws IOException on encoding error.
*/
public byte[] getEncoded(
String encoding)
throws IOException
{
if (encoding.equals(ASN1Encoding.DER))
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
DEROutputStream dOut = new DEROutputStream(bOut);
dOut.writeObject(this);
return bOut.toByteArray();
}
else if (encoding.equals(ASN1Encoding.DL))
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
DLOutputStream dOut = new DLOutputStream(bOut);
dOut.writeObject(this);
return bOut.toByteArray();
}
return this.getEncoded();
}
public int hashCode()
{
return this.toASN1Primitive().hashCode();
}
public boolean equals(
Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof ASN1Encodable))
{
return false;
}
ASN1Encodable other = (ASN1Encodable)o;
return this.toASN1Primitive().equals(other.toASN1Primitive());
}
/**
* @deprecated use toASN1Primitive()
* @return the underlying primitive type.
*/
public ASN1Primitive toASN1Object()
{
return this.toASN1Primitive();
}
protected static boolean hasEncodedTagValue(Object obj, int tagValue)
{
return (obj instanceof byte[]) && ((byte[])obj)[0] == tagValue;
}
public abstract ASN1Primitive toASN1Primitive();
}

@ -0,0 +1,42 @@
package org.bouncycastle.asn1;
public class ASN1ObjectIdentifier
extends DERObjectIdentifier
{
public ASN1ObjectIdentifier(String identifier)
{
super(identifier);
}
ASN1ObjectIdentifier(byte[] bytes)
{
super(bytes);
}
ASN1ObjectIdentifier(ASN1ObjectIdentifier oid, String branch)
{
super(oid, branch);
}
/**
* Return an OID that creates a branch under the current one.
*
* @param branchID node numbers for the new branch.
* @return the OID for the new created branch.
*/
public ASN1ObjectIdentifier branch(String branchID)
{
return new ASN1ObjectIdentifier(this, branchID);
}
/**
* Return true if this oid is an extension of the passed in branch, stem.
* @param stem the arc or branch that is a possible parent.
* @return true if the branch is on the passed in stem, false otherwise.
*/
public boolean on(ASN1ObjectIdentifier stem)
{
String id = getId(), stemId = stem.getId();
return id.length() > stemId.length() && id.charAt(stemId.length()) == '.' && id.startsWith(stemId);
}
}

@ -0,0 +1,146 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
public abstract class ASN1OctetString
extends ASN1Primitive
implements ASN1OctetStringParser
{
byte[] string;
/**
* return an Octet String from a tagged object.
*
* @param obj the tagged object holding the object we want.
* @param explicit true if the object is meant to be explicitly
* tagged false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1OctetString getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
ASN1Primitive o = obj.getObject();
if (explicit || o instanceof ASN1OctetString)
{
return getInstance(o);
}
else
{
return BEROctetString.fromSequence(ASN1Sequence.getInstance(o));
}
}
/**
* return an Octet String from the given object.
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1OctetString getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1OctetString)
{
return (ASN1OctetString)obj;
}
else if (obj instanceof byte[])
{
try
{
return ASN1OctetString.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct OCTET STRING from byte[]: " + e.getMessage());
}
}
else if (obj instanceof ASN1Encodable)
{
ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
if (primitive instanceof ASN1OctetString)
{
return (ASN1OctetString)primitive;
}
}
throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
}
/**
* @param string the octets making up the octet string.
*/
public ASN1OctetString(
byte[] string)
{
if (string == null)
{
throw new NullPointerException("string cannot be null");
}
this.string = string;
}
public InputStream getOctetStream()
{
return new ByteArrayInputStream(string);
}
public ASN1OctetStringParser parser()
{
return this;
}
public byte[] getOctets()
{
return string;
}
public int hashCode()
{
return Arrays.hashCode(this.getOctets());
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1OctetString))
{
return false;
}
ASN1OctetString other = (ASN1OctetString)o;
return Arrays.areEqual(string, other.string);
}
public ASN1Primitive getLoadedObject()
{
return this.toASN1Primitive();
}
ASN1Primitive toDERObject()
{
return new DEROctetString(string);
}
ASN1Primitive toDLObject()
{
return new DEROctetString(string);
}
abstract void encode(ASN1OutputStream out)
throws IOException;
public String toString()
{
return "#"+new String(Hex.encode(string));
}
}

@ -0,0 +1,9 @@
package org.bouncycastle.asn1;
import java.io.InputStream;
public interface ASN1OctetStringParser
extends ASN1Encodable, InMemoryRepresentable
{
public InputStream getOctetStream();
}

@ -0,0 +1,194 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.io.OutputStream;
/**
* Stream that produces output based on the default encoding for the passed in objects.
*/
public class ASN1OutputStream
{
private OutputStream os;
public ASN1OutputStream(
OutputStream os)
{
this.os = os;
}
void writeLength(
int length)
throws IOException
{
if (length > 127)
{
int size = 1;
int val = length;
while ((val >>>= 8) != 0)
{
size++;
}
write((byte)(size | 0x80));
for (int i = (size - 1) * 8; i >= 0; i -= 8)
{
write((byte)(length >> i));
}
}
else
{
write((byte)length);
}
}
void write(int b)
throws IOException
{
os.write(b);
}
void write(byte[] bytes)
throws IOException
{
os.write(bytes);
}
void write(byte[] bytes, int off, int len)
throws IOException
{
os.write(bytes, off, len);
}
void writeEncoded(
int tag,
byte[] bytes)
throws IOException
{
write(tag);
writeLength(bytes.length);
write(bytes);
}
void writeTag(int flags, int tagNo)
throws IOException
{
if (tagNo < 31)
{
write(flags | tagNo);
}
else
{
write(flags | 0x1f);
if (tagNo < 128)
{
write(tagNo);
}
else
{
byte[] stack = new byte[5];
int pos = stack.length;
stack[--pos] = (byte)(tagNo & 0x7F);
do
{
tagNo >>= 7;
stack[--pos] = (byte)(tagNo & 0x7F | 0x80);
}
while (tagNo > 127);
write(stack, pos, stack.length - pos);
}
}
}
void writeEncoded(int flags, int tagNo, byte[] bytes)
throws IOException
{
writeTag(flags, tagNo);
writeLength(bytes.length);
write(bytes);
}
protected void writeNull()
throws IOException
{
os.write(BERTags.NULL);
os.write(0x00);
}
public void writeObject(
ASN1Encodable obj)
throws IOException
{
if (obj != null)
{
obj.toASN1Primitive().encode(this);
}
else
{
throw new IOException("null object detected");
}
}
void writeImplicitObject(ASN1Primitive obj)
throws IOException
{
if (obj != null)
{
obj.encode(new ImplicitOutputStream(os));
}
else
{
throw new IOException("null object detected");
}
}
public void close()
throws IOException
{
os.close();
}
public void flush()
throws IOException
{
os.flush();
}
ASN1OutputStream getDERSubStream()
{
return new DEROutputStream(os);
}
ASN1OutputStream getDLSubStream()
{
return new DLOutputStream(os);
}
private class ImplicitOutputStream
extends ASN1OutputStream
{
private boolean first = true;
public ImplicitOutputStream(OutputStream os)
{
super(os);
}
public void write(int b)
throws IOException
{
if (first)
{
first = false;
}
else
{
super.write(b);
}
}
}
}

@ -0,0 +1,23 @@
package org.bouncycastle.asn1;
public class ASN1ParsingException
extends IllegalStateException
{
private Throwable cause;
public ASN1ParsingException(String message)
{
super(message);
}
public ASN1ParsingException(String message, Throwable cause)
{
super(message);
this.cause = cause;
}
public Throwable getCause()
{
return cause;
}
}

@ -0,0 +1,69 @@
package org.bouncycastle.asn1;
import java.io.IOException;
public abstract class ASN1Primitive
extends ASN1Object
{
ASN1Primitive()
{
}
/**
* Create a base ASN.1 object from a byte stream.
*
* @param data the byte stream to parse.
* @return the base ASN.1 object represented by the byte stream.
* @exception IOException if there is a problem parsing the data.
*/
public static ASN1Primitive fromByteArray(byte[] data)
throws IOException
{
ASN1InputStream aIn = new ASN1InputStream(data);
try
{
return aIn.readObject();
}
catch (ClassCastException e)
{
throw new IOException("cannot recognise object in stream");
}
}
public final boolean equals(Object o)
{
if (this == o)
{
return true;
}
return (o instanceof ASN1Encodable) && asn1Equals(((ASN1Encodable)o).toASN1Primitive());
}
public ASN1Primitive toASN1Primitive()
{
return this;
}
ASN1Primitive toDERObject()
{
return this;
}
ASN1Primitive toDLObject()
{
return this;
}
public abstract int hashCode();
abstract boolean isConstructed();
abstract int encodedLength() throws IOException;
abstract void encode(ASN1OutputStream out) throws IOException;
abstract boolean asn1Equals(ASN1Primitive o);
}

@ -0,0 +1,323 @@
package org.bouncycastle.asn1;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
public abstract class ASN1Sequence
extends ASN1Primitive
{
protected Vector seq = new Vector();
/**
* return an ASN1Sequence from the given object.
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1Sequence getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Sequence)
{
return (ASN1Sequence)obj;
}
else if (obj instanceof ASN1SequenceParser)
{
return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive());
}
else if (obj instanceof byte[])
{
try
{
return ASN1Sequence.getInstance(fromByteArray((byte[])obj));
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
}
}
else if (obj instanceof ASN1Encodable)
{
ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
if (primitive instanceof ASN1Sequence)
{
return (ASN1Sequence)primitive;
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
/**
* Return an ASN1 sequence from a tagged object. There is a special
* case here, if an object appears to have been explicitly tagged on
* reading but we were expecting it to be implicitly tagged in the
* normal course of events it indicates that we lost the surrounding
* sequence - so we need to add it back (this will happen if the tagged
* object is a sequence that contains other sequences). If you are
* dealing with implicitly tagged sequences you really <b>should</b>
* be using this method.
*
* @param obj the tagged object.
* @param explicit true if the object is meant to be explicitly tagged,
* false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1Sequence getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
if (explicit)
{
if (!obj.isExplicit())
{
throw new IllegalArgumentException("object implicit - explicit expected.");
}
return ASN1Sequence.getInstance(obj.getObject().toASN1Primitive());
}
else
{
//
// constructed object which appears to be explicitly tagged
// when it should be implicit means we have to add the
// surrounding sequence.
//
if (obj.isExplicit())
{
if (obj instanceof BERTaggedObject)
{
return new BERSequence(obj.getObject());
}
else
{
return new DLSequence(obj.getObject());
}
}
else
{
if (obj.getObject() instanceof ASN1Sequence)
{
return (ASN1Sequence)obj.getObject();
}
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
/**
* create an empty sequence
*/
protected ASN1Sequence()
{
}
/**
* create a sequence containing one object
*/
protected ASN1Sequence(
ASN1Encodable obj)
{
seq.addElement(obj);
}
/**
* create a sequence containing a vector of objects.
*/
protected ASN1Sequence(
ASN1EncodableVector v)
{
for (int i = 0; i != v.size(); i++)
{
seq.addElement(v.get(i));
}
}
/**
* create a sequence containing a vector of objects.
*/
protected ASN1Sequence(
ASN1Encodable[] array)
{
for (int i = 0; i != array.length; i++)
{
seq.addElement(array[i]);
}
}
public ASN1Encodable[] toArray()
{
ASN1Encodable[] values = new ASN1Encodable[this.size()];
for (int i = 0; i != this.size(); i++)
{
values[i] = this.getObjectAt(i);
}
return values;
}
public Enumeration getObjects()
{
return seq.elements();
}
public ASN1SequenceParser parser()
{
final ASN1Sequence outer = this;
return new ASN1SequenceParser()
{
private final int max = size();
private int index;
public ASN1Encodable readObject() throws IOException
{
if (index == max)
{
return null;
}
ASN1Encodable obj = getObjectAt(index++);
if (obj instanceof ASN1Sequence)
{
return ((ASN1Sequence)obj).parser();
}
if (obj instanceof ASN1Set)
{
return ((ASN1Set)obj).parser();
}
return obj;
}
public ASN1Primitive getLoadedObject()
{
return outer;
}
public ASN1Primitive toASN1Primitive()
{
return outer;
}
};
}
/**
* return the object at the sequence position indicated by index.
*
* @param index the sequence number (starting at zero) of the object
* @return the object at the sequence position indicated by index.
*/
public ASN1Encodable getObjectAt(
int index)
{
return (ASN1Encodable)seq.elementAt(index);
}
/**
* return the number of objects in this sequence.
*
* @return the number of objects in this sequence.
*/
public int size()
{
return seq.size();
}
public int hashCode()
{
Enumeration e = this.getObjects();
int hashCode = size();
while (e.hasMoreElements())
{
Object o = getNext(e);
hashCode *= 17;
hashCode ^= o.hashCode();
}
return hashCode;
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1Sequence))
{
return false;
}
ASN1Sequence other = (ASN1Sequence)o;
if (this.size() != other.size())
{
return false;
}
Enumeration s1 = this.getObjects();
Enumeration s2 = other.getObjects();
while (s1.hasMoreElements())
{
ASN1Encodable obj1 = getNext(s1);
ASN1Encodable obj2 = getNext(s2);
ASN1Primitive o1 = obj1.toASN1Primitive();
ASN1Primitive o2 = obj2.toASN1Primitive();
if (o1 == o2 || o1.equals(o2))
{
continue;
}
return false;
}
return true;
}
private ASN1Encodable getNext(Enumeration e)
{
ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
return encObj;
}
ASN1Primitive toDERObject()
{
ASN1Sequence derSeq = new DERSequence();
derSeq.seq = this.seq;
return derSeq;
}
ASN1Primitive toDLObject()
{
ASN1Sequence dlSeq = new DLSequence();
dlSeq.seq = this.seq;
return dlSeq;
}
boolean isConstructed()
{
return true;
}
abstract void encode(ASN1OutputStream out)
throws IOException;
public String toString()
{
return seq.toString();
}
}

@ -0,0 +1,10 @@
package org.bouncycastle.asn1;
import java.io.IOException;
public interface ASN1SequenceParser
extends ASN1Encodable, InMemoryRepresentable
{
ASN1Encodable readObject()
throws IOException;
}

@ -0,0 +1,460 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
abstract public class ASN1Set
extends ASN1Primitive
{
private Vector set = new Vector();
private boolean isSorted = false;
/**
* return an ASN1Set from the given object.
*
* @param obj the object we want converted.
* @exception IllegalArgumentException if the object cannot be converted.
*/
public static ASN1Set getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1Set)
{
return (ASN1Set)obj;
}
else if (obj instanceof ASN1SetParser)
{
return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
}
else if (obj instanceof byte[])
{
try
{
return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
}
}
else if (obj instanceof ASN1Encodable)
{
ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
if (primitive instanceof ASN1Set)
{
return (ASN1Set)primitive;
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
/**
* Return an ASN1 set from a tagged object. There is a special
* case here, if an object appears to have been explicitly tagged on
* reading but we were expecting it to be implicitly tagged in the
* normal course of events it indicates that we lost the surrounding
* set - so we need to add it back (this will happen if the tagged
* object is a sequence that contains other sequences). If you are
* dealing with implicitly tagged sets you really <b>should</b>
* be using this method.
*
* @param obj the tagged object.
* @param explicit true if the object is meant to be explicitly tagged
* false otherwise.
* @exception IllegalArgumentException if the tagged object cannot
* be converted.
*/
public static ASN1Set getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
if (explicit)
{
if (!obj.isExplicit())
{
throw new IllegalArgumentException("object implicit - explicit expected.");
}
return (ASN1Set)obj.getObject();
}
else
{
//
// constructed object which appears to be explicitly tagged
// and it's really implicit means we have to add the
// surrounding set.
//
if (obj.isExplicit())
{
if (obj instanceof BERTaggedObject)
{
return new BERSet(obj.getObject());
}
else
{
return new DLSet(obj.getObject());
}
}
else
{
if (obj.getObject() instanceof ASN1Set)
{
return (ASN1Set)obj.getObject();
}
//
// in this case the parser returns a sequence, convert it
// into a set.
//
if (obj.getObject() instanceof ASN1Sequence)
{
ASN1Sequence s = (ASN1Sequence)obj.getObject();
if (obj instanceof BERTaggedObject)
{
return new BERSet(s.toArray());
}
else
{
return new DLSet(s.toArray());
}
}
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
protected ASN1Set()
{
}
/**
* create a sequence containing one object
*/
protected ASN1Set(
ASN1Encodable obj)
{
set.addElement(obj);
}
/**
* create a sequence containing a vector of objects.
*/
protected ASN1Set(
ASN1EncodableVector v,
boolean doSort)
{
for (int i = 0; i != v.size(); i++)
{
set.addElement(v.get(i));
}
if (doSort)
{
this.sort();
}
}
/**
* create a sequence containing a vector of objects.
*/
protected ASN1Set(
ASN1Encodable[] array,
boolean doSort)
{
for (int i = 0; i != array.length; i++)
{
set.addElement(array[i]);
}
if (doSort)
{
this.sort();
}
}
public Enumeration getObjects()
{
return set.elements();
}
/**
* return the object at the set position indicated by index.
*
* @param index the set number (starting at zero) of the object
* @return the object at the set position indicated by index.
*/
public ASN1Encodable getObjectAt(
int index)
{
return (ASN1Encodable)set.elementAt(index);
}
/**
* return the number of objects in this set.
*
* @return the number of objects in this set.
*/
public int size()
{
return set.size();
}
public ASN1Encodable[] toArray()
{
ASN1Encodable[] values = new ASN1Encodable[this.size()];
for (int i = 0; i != this.size(); i++)
{
values[i] = this.getObjectAt(i);
}
return values;
}
public ASN1SetParser parser()
{
final ASN1Set outer = this;
return new ASN1SetParser()
{
private final int max = size();
private int index;
public ASN1Encodable readObject() throws IOException
{
if (index == max)
{
return null;
}
ASN1Encodable obj = getObjectAt(index++);
if (obj instanceof ASN1Sequence)
{
return ((ASN1Sequence)obj).parser();
}
if (obj instanceof ASN1Set)
{
return ((ASN1Set)obj).parser();
}
return obj;
}
public ASN1Primitive getLoadedObject()
{
return outer;
}
public ASN1Primitive toASN1Primitive()
{
return outer;
}
};
}
public int hashCode()
{
Enumeration e = this.getObjects();
int hashCode = size();
while (e.hasMoreElements())
{
Object o = getNext(e);
hashCode *= 17;
hashCode ^= o.hashCode();
}
return hashCode;
}
ASN1Primitive toDERObject()
{
if (isSorted)
{
ASN1Set derSet = new DERSet();
derSet.set = this.set;
return derSet;
}
else
{
Vector v = new Vector();
for (int i = 0; i != set.size(); i++)
{
v.addElement(set.elementAt(i));
}
ASN1Set derSet = new DERSet();
derSet.set = v;
derSet.sort();
return derSet;
}
}
ASN1Primitive toDLObject()
{
ASN1Set derSet = new DLSet();
derSet.set = this.set;
return derSet;
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1Set))
{
return false;
}
ASN1Set other = (ASN1Set)o;
if (this.size() != other.size())
{
return false;
}
Enumeration s1 = this.getObjects();
Enumeration s2 = other.getObjects();
while (s1.hasMoreElements())
{
ASN1Encodable obj1 = getNext(s1);
ASN1Encodable obj2 = getNext(s2);
ASN1Primitive o1 = obj1.toASN1Primitive();
ASN1Primitive o2 = obj2.toASN1Primitive();
if (o1 == o2 || o1.equals(o2))
{
continue;
}
return false;
}
return true;
}
private ASN1Encodable getNext(Enumeration e)
{
ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
// unfortunately null was allowed as a substitute for DER null
if (encObj == null)
{
return DERNull.INSTANCE;
}
return encObj;
}
/**
* return true if a <= b (arrays are assumed padded with zeros).
*/
private boolean lessThanOrEqual(
byte[] a,
byte[] b)
{
int len = Math.min(a.length, b.length);
for (int i = 0; i != len; ++i)
{
if (a[i] != b[i])
{
return (a[i] & 0xff) < (b[i] & 0xff);
}
}
return len == a.length;
}
private byte[] getEncoded(
ASN1Encodable obj)
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
try
{
aOut.writeObject(obj);
}
catch (IOException e)
{
throw new IllegalArgumentException("cannot encode object added to SET");
}
return bOut.toByteArray();
}
protected void sort()
{
if (!isSorted)
{
isSorted = true;
if (set.size() > 1)
{
boolean swapped = true;
int lastSwap = set.size() - 1;
while (swapped)
{
int index = 0;
int swapIndex = 0;
byte[] a = getEncoded((ASN1Encodable)set.elementAt(0));
swapped = false;
while (index != lastSwap)
{
byte[] b = getEncoded((ASN1Encodable)set.elementAt(index + 1));
if (lessThanOrEqual(a, b))
{
a = b;
}
else
{
Object o = set.elementAt(index);
set.setElementAt(set.elementAt(index + 1), index);
set.setElementAt(o, index + 1);
swapped = true;
swapIndex = index;
}
index++;
}
lastSwap = swapIndex;
}
}
}
}
boolean isConstructed()
{
return true;
}
abstract void encode(ASN1OutputStream out)
throws IOException;
public String toString()
{
return set.toString();
}
}

@ -0,0 +1,10 @@
package org.bouncycastle.asn1;
import java.io.IOException;
public interface ASN1SetParser
extends ASN1Encodable, InMemoryRepresentable
{
public ASN1Encodable readObject()
throws IOException;
}

@ -0,0 +1,247 @@
package org.bouncycastle.asn1;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ASN1StreamParser
{
private final InputStream _in;
private final int _limit;
private final byte[][] tmpBuffers;
public ASN1StreamParser(
InputStream in)
{
this(in, StreamUtil.findLimit(in));
}
public ASN1StreamParser(
InputStream in,
int limit)
{
this._in = in;
this._limit = limit;
this.tmpBuffers = new byte[11][];
}
public ASN1StreamParser(
byte[] encoding)
{
this(new ByteArrayInputStream(encoding), encoding.length);
}
ASN1Encodable readIndef(int tagValue) throws IOException
{
// Note: INDEF => CONSTRUCTED
// TODO There are other tags that may be constructed (e.g. BIT_STRING)
switch (tagValue)
{
case BERTags.EXTERNAL:
return new DERExternalParser(this);
case BERTags.OCTET_STRING:
return new BEROctetStringParser(this);
case BERTags.SEQUENCE:
return new BERSequenceParser(this);
case BERTags.SET:
return new BERSetParser(this);
default:
throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
}
}
ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
{
if (_in instanceof IndefiniteLengthInputStream)
{
if (!constructed)
{
throw new IOException("indefinite length primitive encoding encountered");
}
return readIndef(tag);
}
if (constructed)
{
switch (tag)
{
case BERTags.SET:
return new DERSetParser(this);
case BERTags.SEQUENCE:
return new DERSequenceParser(this);
case BERTags.OCTET_STRING:
return new BEROctetStringParser(this);
}
}
else
{
switch (tag)
{
case BERTags.SET:
throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
case BERTags.SEQUENCE:
throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
case BERTags.OCTET_STRING:
return new DEROctetStringParser((DefiniteLengthInputStream)_in);
}
}
// TODO ASN1Exception
throw new RuntimeException("implicit tagging not implemented");
}
ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
{
if (!constructed)
{
// Note: !CONSTRUCTED => IMPLICIT
DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
}
ASN1EncodableVector v = readVector();
if (_in instanceof IndefiniteLengthInputStream)
{
return v.size() == 1
? new BERTaggedObject(true, tag, v.get(0))
: new BERTaggedObject(false, tag, BERFactory.createSequence(v));
}
return v.size() == 1
? new DERTaggedObject(true, tag, v.get(0))
: new DERTaggedObject(false, tag, DERFactory.createSequence(v));
}
public ASN1Encodable readObject()
throws IOException
{
int tag = _in.read();
if (tag == -1)
{
return null;
}
//
// turn of looking for "00" while we resolve the tag
//
set00Check(false);
//
// calculate tag number
//
int tagNo = ASN1InputStream.readTagNumber(_in, tag);
boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
//
// calculate length
//
int length = ASN1InputStream.readLength(_in, _limit);
if (length < 0) // indefinite length method
{
if (!isConstructed)
{
throw new IOException("indefinite length primitive encoding encountered");
}
IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
if ((tag & BERTags.APPLICATION) != 0)
{
return new BERApplicationSpecificParser(tagNo, sp);
}
if ((tag & BERTags.TAGGED) != 0)
{
return new BERTaggedObjectParser(true, tagNo, sp);
}
return sp.readIndef(tagNo);
}
else
{
DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
if ((tag & BERTags.APPLICATION) != 0)
{
return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
}
if ((tag & BERTags.TAGGED) != 0)
{
return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
}
if (isConstructed)
{
// TODO There are other tags that may be constructed (e.g. BIT_STRING)
switch (tagNo)
{
case BERTags.OCTET_STRING:
//
// yes, people actually do this...
//
return new BEROctetStringParser(new ASN1StreamParser(defIn));
case BERTags.SEQUENCE:
return new DERSequenceParser(new ASN1StreamParser(defIn));
case BERTags.SET:
return new DERSetParser(new ASN1StreamParser(defIn));
case BERTags.EXTERNAL:
return new DERExternalParser(new ASN1StreamParser(defIn));
default:
throw new IOException("unknown tag " + tagNo + " encountered");
}
}
// Some primitive encodings can be handled by parsers too...
switch (tagNo)
{
case BERTags.OCTET_STRING:
return new DEROctetStringParser(defIn);
}
try
{
return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
}
catch (IllegalArgumentException e)
{
throw new ASN1Exception("corrupted stream detected", e);
}
}
}
private void set00Check(boolean enabled)
{
if (_in instanceof IndefiniteLengthInputStream)
{
((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
}
}
ASN1EncodableVector readVector() throws IOException
{
ASN1EncodableVector v = new ASN1EncodableVector();
ASN1Encodable obj;
while ((obj = readObject()) != null)
{
if (obj instanceof InMemoryRepresentable)
{
v.add(((InMemoryRepresentable)obj).getLoadedObject());
}
else
{
v.add(obj.toASN1Primitive());
}
}
return v;
}
}

@ -0,0 +1,6 @@
package org.bouncycastle.asn1;
public interface ASN1String
{
public String getString();
}

@ -0,0 +1,236 @@
package org.bouncycastle.asn1;
import java.io.IOException;
/**
* ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
* a [n] where n is some number - these are assumed to follow the construction
* rules (as with sequences).
*/
public abstract class ASN1TaggedObject
extends ASN1Primitive
implements ASN1TaggedObjectParser
{
int tagNo;
boolean empty = false;
boolean explicit = true;
ASN1Encodable obj = null;
static public ASN1TaggedObject getInstance(
ASN1TaggedObject obj,
boolean explicit)
{
if (explicit)
{
return (ASN1TaggedObject)obj.getObject();
}
throw new IllegalArgumentException("implicitly tagged tagged object");
}
static public ASN1TaggedObject getInstance(
Object obj)
{
if (obj == null || obj instanceof ASN1TaggedObject)
{
return (ASN1TaggedObject)obj;
}
else if (obj instanceof byte[])
{
try
{
return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
}
catch (IOException e)
{
throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage());
}
}
throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
}
/**
* Create a tagged object with the style given by the value of explicit.
* <p>
* If the object implements ASN1Choice the tag style will always be changed
* to explicit in accordance with the ASN.1 encoding rules.
* </p>
* @param explicit true if the object is explicitly tagged.
* @param tagNo the tag number for this object.
* @param obj the tagged object.
*/
public ASN1TaggedObject(
boolean explicit,
int tagNo,
ASN1Encodable obj)
{
if (obj instanceof ASN1Choice)
{
this.explicit = true;
}
else
{
this.explicit = explicit;
}
this.tagNo = tagNo;
if (this.explicit)
{
this.obj = obj;
}
else
{
ASN1Primitive prim = obj.toASN1Primitive();
if (prim instanceof ASN1Set)
{
ASN1Set s = null;
}
this.obj = obj;
}
}
boolean asn1Equals(
ASN1Primitive o)
{
if (!(o instanceof ASN1TaggedObject))
{
return false;
}
ASN1TaggedObject other = (ASN1TaggedObject)o;
if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit)
{
return false;
}
if(obj == null)
{
if (other.obj != null)
{
return false;
}
}
else
{
if (!(obj.toASN1Primitive().equals(other.obj.toASN1Primitive())))
{
return false;
}
}
return true;
}
public int hashCode()
{
int code = tagNo;
// TODO: actually this is wrong - the problem is that a re-encoded
// object may end up with a different hashCode due to implicit
// tagging. As implicit tagging is ambiguous if a sequence is involved
// it seems the only correct method for both equals and hashCode is to
// compare the encodings...
if (obj != null)
{
code ^= obj.hashCode();
}
return code;
}
public int getTagNo()
{
return tagNo;
}
/**
* return whether or not the object may be explicitly tagged.
* <p>
* Note: if the object has been read from an input stream, the only
* time you can be sure if isExplicit is returning the true state of
* affairs is if it returns false. An implicitly tagged object may appear
* to be explicitly tagged, so you need to understand the context under
* which the reading was done as well, see getObject below.
*/
public boolean isExplicit()
{
return explicit;
}
public boolean isEmpty()
{
return empty;
}
/**
* return whatever was following the tag.
* <p>
* Note: tagged objects are generally context dependent if you're
* trying to extract a tagged object you should be going via the
* appropriate getInstance method.
*/
public ASN1Primitive getObject()
{
if (obj != null)
{
return obj.toASN1Primitive();
}
return null;
}
/**
* Return the object held in this tagged object as a parser assuming it has
* the type of the passed in tag. If the object doesn't have a parser
* associated with it, the base object is returned.
*/
public ASN1Encodable getObjectParser(
int tag,
boolean isExplicit)
{
switch (tag)
{
case BERTags.SET:
return ASN1Set.getInstance(this, isExplicit).parser();
case BERTags.SEQUENCE:
return ASN1Sequence.getInstance(this, isExplicit).parser();
case BERTags.OCTET_STRING:
return ASN1OctetString.getInstance(this, isExplicit).parser();
}
if (isExplicit)
{
return getObject();
}
throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
}
public ASN1Primitive getLoadedObject()
{
return this.toASN1Primitive();
}
ASN1Primitive toDERObject()
{
return new DERTaggedObject(explicit, tagNo, obj);
}
ASN1Primitive toDLObject()
{
return new DLTaggedObject(explicit, tagNo, obj);
}
abstract void encode(ASN1OutputStream out)
throws IOException;
public String toString()
{
return "[" + tagNo + "]" + obj;
}
}

@ -0,0 +1,12 @@
package org.bouncycastle.asn1;
import java.io.IOException;
public interface ASN1TaggedObjectParser
extends ASN1Encodable, InMemoryRepresentable
{
public int getTagNo();
public ASN1Encodable getObjectParser(int tag, boolean isExplicit)
throws IOException;
}

@ -0,0 +1,22 @@
package org.bouncycastle.asn1;
import java.util.Date;
public class ASN1UTCTime
extends DERUTCTime
{
ASN1UTCTime(byte[] bytes)
{
super(bytes);
}
public ASN1UTCTime(Date time)
{
super(time);
}
public ASN1UTCTime(String time)
{
super(time);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save