support legacy ota
parent
374aab8f28
commit
657b2d40ce
@ -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…
Reference in New Issue