summaryrefslogtreecommitdiffstats
path: root/bcprov/src/main/java/org/bouncycastle/jce/provider
diff options
context:
space:
mode:
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/jce/provider')
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java32
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java628
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java146
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/CertBlacklist.java216
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java1435
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/CertStatus.java46
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java104
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/DHUtil.java50
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java20
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java1103
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java188
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java178
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java484
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java532
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEMac.java455
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java243
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java149
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java133
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java612
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java532
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java320
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java181
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java177
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java1048
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java1658
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java94
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java155
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java261
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java462
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java1924
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java168
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java2569
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java296
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java576
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java826
-rw-r--r--bcprov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java148
39 files changed, 18308 insertions, 0 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java
new file mode 100644
index 0000000..c9ac46e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java
@@ -0,0 +1,32 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.jce.exception.ExtException;
+
+public class AnnotatedException
+ extends Exception
+ implements ExtException
+{
+ private Throwable _underlyingException;
+
+ AnnotatedException(String string, Throwable e)
+ {
+ super(string);
+
+ _underlyingException = e;
+ }
+
+ AnnotatedException(String string)
+ {
+ this(string, null);
+ }
+
+ Throwable getUnderlyingException()
+ {
+ return _underlyingException;
+ }
+
+ public Throwable getCause()
+ {
+ return _underlyingException;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
new file mode 100644
index 0000000..5ed4df9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -0,0 +1,628 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivateKey;
+import java.security.PrivilegedAction;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter;
+
+/**
+ * To add the provider at runtime use:
+ * <pre>
+ * import java.security.Security;
+ * import org.bouncycastle.jce.provider.BouncyCastleProvider;
+ *
+ * Security.addProvider(new BouncyCastleProvider());
+ * </pre>
+ * The provider can also be configured as part of your environment via
+ * static registration by adding an entry to the java.security properties
+ * file (found in $JAVA_HOME/jre/lib/security/java.security, where
+ * $JAVA_HOME is the location of your JDK/JRE distribution). You'll find
+ * detailed instructions in the file but basically it comes down to adding
+ * a line:
+ * <pre>
+ * <code>
+ * security.provider.&lt;n&gt;=org.bouncycastle.jce.provider.BouncyCastleProvider
+ * </code>
+ * </pre>
+ * Where &lt;n&gt; is the preference you want the provider at (1 being the
+ * most preferred).
+ * <p>Note: JCE algorithm names should be upper-case only so the case insensitive
+ * test for getInstance works.
+ */
+public final class BouncyCastleProvider extends Provider
+ implements ConfigurableProvider
+{
+ private static String info = "BouncyCastle Security Provider v1.47";
+
+ // BEGIN android-changed
+ // this constant should be final
+ public static final String PROVIDER_NAME = "BC";
+ // END android-changed
+
+ public static final ProviderConfiguration CONFIGURATION = new BouncyCastleProviderConfiguration();
+
+
+ private static final Map keyInfoConverters = new HashMap();
+
+ /*
+ * Configurable symmetric ciphers
+ */
+ private static final String SYMMETRIC_CIPHER_PACKAGE = "org.bouncycastle.jcajce.provider.symmetric.";
+ private static final String[] SYMMETRIC_CIPHERS =
+ {
+ // BEGIN android-removed
+ // "AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "DES", "DESede", "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA",
+ // "Noekeon", "RC2", "RC5", "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Skipjack", "TEA", "Twofish", "VMPC", "VMPCKSA3", "XTEA"
+ // END android-removed
+ // BEGIN android-added
+ "AES", "ARC4", "Blowfish", "DES", "DESede",
+ // END android-added
+ };
+
+ /*
+ * Configurable asymmetric ciphers
+ */
+ private static final String ASYMMETRIC_CIPHER_PACKAGE = "org.bouncycastle.jcajce.provider.asymmetric.";
+
+ // this one is required for GNU class path - it needs to be loaded first as the
+ // later ones configure it.
+ private static final String[] ASYMMETRIC_GENERIC =
+ {
+ "X509"
+ };
+
+ private static final String[] ASYMMETRIC_CIPHERS =
+ {
+ // BEGIN android-removed
+ // "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal"
+ // END android-removed
+ // BEGIN android-added
+ "DSA", "DH", "EC", "RSA",
+ // END android-added
+ };
+
+ /*
+ * Configurable digests
+ */
+ private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest.";
+ private static final String[] DIGESTS =
+ {
+ // BEGIN android-removed
+ // "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "Tiger", "Whirlpool"
+ // END android-removed
+ // BEGIN android-added
+ "MD5", "SHA1", "SHA256", "SHA384", "SHA512",
+ // END android-added
+ };
+
+ /**
+ * Construct a new provider. This should only be required when
+ * using runtime registration of the provider using the
+ * <code>Security.addProvider()</code> mechanism.
+ */
+ public BouncyCastleProvider()
+ {
+ super(PROVIDER_NAME, 1.47, info);
+
+ AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ setup();
+ return null;
+ }
+ });
+ }
+
+ private void setup()
+ {
+ loadAlgorithms(DIGEST_PACKAGE, DIGESTS);
+
+ loadAlgorithms(SYMMETRIC_CIPHER_PACKAGE, SYMMETRIC_CIPHERS);
+
+ loadAlgorithms(ASYMMETRIC_CIPHER_PACKAGE, ASYMMETRIC_GENERIC);
+
+ loadAlgorithms(ASYMMETRIC_CIPHER_PACKAGE, ASYMMETRIC_CIPHERS);
+
+ // BEGIN android-removed
+ // //
+ // // X509Store
+ // //
+ // put("X509Store.CERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertCollection");
+ // put("X509Store.ATTRIBUTECERTIFICATE/COLLECTION", "org.bouncycastle.jce.provider.X509StoreAttrCertCollection");
+ // put("X509Store.CRL/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCRLCollection");
+ // put("X509Store.CERTIFICATEPAIR/COLLECTION", "org.bouncycastle.jce.provider.X509StoreCertPairCollection");
+ //
+ // put("X509Store.CERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCerts");
+ // put("X509Store.CRL/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCRLs");
+ // put("X509Store.ATTRIBUTECERTIFICATE/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPAttrCerts");
+ // put("X509Store.CERTIFICATEPAIR/LDAP", "org.bouncycastle.jce.provider.X509StoreLDAPCertPairs");
+ //
+ // //
+ // // X509StreamParser
+ // //
+ // put("X509StreamParser.CERTIFICATE", "org.bouncycastle.jce.provider.X509CertParser");
+ // put("X509StreamParser.ATTRIBUTECERTIFICATE", "org.bouncycastle.jce.provider.X509AttrCertParser");
+ // put("X509StreamParser.CRL", "org.bouncycastle.jce.provider.X509CRLParser");
+ // put("X509StreamParser.CERTIFICATEPAIR", "org.bouncycastle.jce.provider.X509CertPairParser");
+ // END android-removed
+
+
+ //
+ // KeyStore
+ //
+ put("KeyStore.BKS", "org.bouncycastle.jce.provider.JDKKeyStore");
+ put("KeyStore.BouncyCastle", "org.bouncycastle.jce.provider.JDKKeyStore$BouncyCastleStore");
+ put("KeyStore.PKCS12", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore");
+ // BEGIN android-changed
+ put("Alg.Alias.KeyStore.BCPKCS12", "PKCS12");
+ // END android-changed
+ // BEGIN android-removed
+ // put("KeyStore.PKCS12-DEF", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+ // END android-removed
+
+ // BEGIN android-changed
+ put("Alg.Alias.KeyStore.PKCS12-3DES-40RC2", "PKCS12");
+ // END android-changed
+ // BEGIN android-removed
+ // put("KeyStore.PKCS12-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$BCPKCS12KeyStore3DES");
+ // END android-removed
+
+ // BEGIN android-removed
+ // put("KeyStore.PKCS12-DEF-3DES-40RC2", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore");
+ // put("KeyStore.PKCS12-DEF-3DES-3DES", "org.bouncycastle.jce.provider.JDKPKCS12KeyStore$DefPKCS12KeyStore3DES");
+ // END android-removed
+
+ put("Alg.Alias.KeyStore.UBER", "BouncyCastle");
+ put("Alg.Alias.KeyStore.BOUNCYCASTLE", "BouncyCastle");
+ put("Alg.Alias.KeyStore.bouncycastle", "BouncyCastle");
+
+ //
+ // algorithm parameters
+ //
+ // BEGIN android-removed
+ // put("AlgorithmParameters.IES", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$IES");
+ // END android-removed
+ put("AlgorithmParameters.PKCS12PBE", "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PKCS12PBE");
+
+ // BEGIN android-removed
+ // put("AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "org.bouncycastle.jce.provider.JDKAlgorithmParameters$PBKDF2");
+ // END android-removed
+
+
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2", "PKCS12PBE");
+ // BEGIN android-removed
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES", "PKCS12PBE");
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES", "PKCS12PBE");
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC2", "PKCS12PBE");
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDRC4", "PKCS12PBE");
+ // END android-removed
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
+ // BEGIN android-removed
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1ANDRC2-CBC", "PKCS12PBE");
+ // END android-removed
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PKCS12PBE");
+ // BEGIN android-removed
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES3KEY-CBC", "PKCS12PBE");
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDDES2KEY-CBC", "PKCS12PBE");
+ // END android-removed
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC2-CBC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND40BITRC4", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC2-CBC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITRC4", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH", "PKCS12PBE");
+ // BEGIN android-removed
+ // put("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH-CBC", "PKCS12PBE");
+ // END android-removed
+ put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.1", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.2", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.3", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.4", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.5", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.12.1.6", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWithSHAAnd3KeyTripleDES", "PKCS12PBE");
+
+ put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
+
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND128BITAES-CBC-BC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND192BITAES-CBC-BC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHAAND256BITAES-CBC-BC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND128BITAES-CBC-BC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND192BITAES-CBC-BC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA256AND256BITAES-CBC-BC", "PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND128BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND192BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA1AND256BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND128BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND192BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-1AND256BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND128BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND192BITAES-CBC-BC","PKCS12PBE");
+ put("Alg.Alias.AlgorithmParameters.PBEWITHSHA-256AND256BITAES-CBC-BC","PKCS12PBE");
+
+ // BEGIN android-removed
+ // put("AlgorithmParameters.SHA1WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+ // put("AlgorithmParameters.SHA224WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+ // put("AlgorithmParameters.SHA256WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+ // put("AlgorithmParameters.SHA384WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+ // put("AlgorithmParameters.SHA512WITHECDSA", "org.bouncycastle.jce.provider.JDKECDSAAlgParameters$SigAlgParameters");
+ // END android-removed
+
+ //
+ // key agreement
+ //
+
+
+ //
+ // cipher engines
+ //
+ put("Alg.Alias.Cipher.PBEWithSHAAnd3KeyTripleDES", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+
+
+ // BEGIN android-removed
+ // put("Cipher.ECIES", "org.bouncycastle.jce.provider.JCEIESCipher$ECIES");
+ // put("Cipher.BrokenECIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenECIES");
+ // put("Cipher.IES", "org.bouncycastle.jce.provider.JCEIESCipher$IES");
+ // put("Cipher.BrokenIES", "org.bouncycastle.jce.provider.JCEIESCipher$BrokenIES");
+ // END android-removed
+
+ put("Cipher.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndDES");
+ // BEGIN android-removed
+ // put("Cipher.BROKENPBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithMD5AndDES");
+ // END android-removed
+ put("Cipher.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithMD5AndRC2");
+ put("Cipher.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndDES");
+ // BEGIN android-removed
+ // put("Cipher.BROKENPBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHA1AndDES");
+ // END android-removed
+ put("Cipher.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHA1AndRC2");
+
+ put("Cipher.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd128BitRC2");
+ put("Cipher.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAnd40BitRC2");
+ put("Cipher.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd128BitRC4");
+ put("Cipher.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCEStreamCipher$PBEWithSHAAnd40BitRC4");
+
+
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC2-CBC", "PBEWITHSHAAND128BITRC2-CBC");
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC2-CBC", "PBEWITHSHAAND40BITRC2-CBC");
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITRC4", "PBEWITHSHAAND128BITRC4");
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND40BITRC4", "PBEWITHSHAAND40BITRC4");
+
+ put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
+ put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
+ put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
+ put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
+ put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
+ put("Alg.Alias.Cipher." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
+
+ put("Cipher.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
+ put("Alg.Alias.Cipher.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+
+ put("Cipher.PBEWITHMD5AND128BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHMD5AND192BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+ put("Cipher.PBEWITHMD5AND256BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithAESCBC");
+
+ put("Cipher.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCEBlockCipher$PBEWithSHAAndTwofish");
+ // BEGIN android-removed
+ // put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish");
+ //
+ // put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBEWITHMD2ANDDES");
+ // put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, "PBEWITHMD2ANDRC2");
+ // END android-removed
+ put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, "PBEWITHMD5ANDDES");
+ put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBEWITHMD5ANDDES");
+ put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBEWITHSHA1ANDDES");
+ put("Alg.Alias.Cipher." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBEWITHSHA1ANDRC2");
+
+ put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
+ put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
+
+ put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
+ put("Alg.Alias.Cipher.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
+
+ //
+ // key generators.
+ //
+
+
+ //
+ // key pair generators.
+ //
+
+
+
+ //
+ // key factories
+ //
+
+
+
+
+ //
+ // Algorithm parameters
+ //
+
+ //
+ // secret key factories.
+ //
+ // BEGIN android-removed
+ // put("SecretKeyFactory.PBEWITHMD2ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndDES");
+ //
+ // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBEWITHMD2ANDDES");
+ // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, "PBEWITHMD2ANDRC2");
+ // END android-removed
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, "PBEWITHMD5ANDDES");
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBEWITHMD5ANDDES");
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBEWITHSHA1ANDDES");
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBEWITHSHA1ANDRC2");
+
+ // BEGIN android-removed
+ // put("SecretKeyFactory.PBEWITHMD2ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD2AndRC2");
+ // END android-removed
+ put("SecretKeyFactory.PBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndDES");
+ put("SecretKeyFactory.PBEWITHMD5ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5AndRC2");
+ put("SecretKeyFactory.PBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndDES");
+ put("SecretKeyFactory.PBEWITHSHA1ANDRC2", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA1AndRC2");
+ put("SecretKeyFactory.PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES3Key");
+ put("SecretKeyFactory.PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndDES2Key");
+ put("SecretKeyFactory.PBEWITHSHAAND128BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC4");
+ put("SecretKeyFactory.PBEWITHSHAAND40BITRC4", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC4");
+ put("SecretKeyFactory.PBEWITHSHAAND128BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitRC2");
+ put("SecretKeyFactory.PBEWITHSHAAND40BITRC2-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd40BitRC2");
+ put("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAndTwofish");
+ // BEGIN android-removed
+ // put("SecretKeyFactory.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithRIPEMD160");
+ // END android-removed
+ put("SecretKeyFactory.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA");
+ // BEGIN android-removed
+ // put("SecretKeyFactory.PBEWITHHMACTIGER", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithTiger");
+ // END android-removed
+
+ put("SecretKeyFactory.PBEWITHMD5AND128BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And128BitAESCBCOpenSSL");
+ put("SecretKeyFactory.PBEWITHMD5AND192BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And192BitAESCBCOpenSSL");
+ put("SecretKeyFactory.PBEWITHMD5AND256BITAES-CBC-OPENSSL", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithMD5And256BitAESCBCOpenSSL");
+
+ // BEGIN android-removed
+ // put("Alg.Alias.SecretKeyFactory.PBE", "PBE/PKCS5");
+ //
+ // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHMD5ANDDES", "PBE/PKCS5");
+ // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHA1ANDDES", "PBE/PKCS5");
+ // put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+ // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND3-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+ // put("Alg.Alias.SecretKeyFactory.BROKENPBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBE/PKCS12");
+ // put("Alg.Alias.SecretKeyFactory.OLDPBEWITHSHAANDTWOFISH-CBC", "PBE/PKCS12");
+ //
+ // put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDDES-CBC", "PBEWITHMD2ANDDES");
+ // put("Alg.Alias.SecretKeyFactory.PBEWITHMD2ANDRC2-CBC", "PBEWITHMD2ANDRC2");
+ // END android-removed
+ put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDDES-CBC", "PBEWITHMD5ANDDES");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHMD5ANDRC2-CBC", "PBEWITHMD5ANDRC2");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDDES-CBC", "PBEWITHSHA1ANDDES");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1ANDRC2-CBC", "PBEWITHSHA1ANDRC2");
+ // BEGIN android-removed
+ // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, "PBEWITHMD2ANDDES");
+ // put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, "PBEWITHMD2ANDRC2");
+ // END android-removed
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, "PBEWITHMD5ANDDES");
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, "PBEWITHMD5ANDRC2");
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, "PBEWITHSHA1ANDDES");
+ put("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, "PBEWITHSHA1ANDRC2");
+
+ put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.1", "PBEWITHSHAAND128BITRC4");
+ put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.2", "PBEWITHSHAAND40BITRC4");
+ put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.3", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+ put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.4", "PBEWITHSHAAND2-KEYTRIPLEDES-CBC");
+ put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.5", "PBEWITHSHAAND128BITRC2-CBC");
+ put("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.12.1.6", "PBEWITHSHAAND40BITRC2-CBC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHHMACSHA", "PBEWITHHMACSHA1");
+ put("Alg.Alias.SecretKeyFactory.1.3.14.3.2.26", "PBEWITHHMACSHA1");
+ put("Alg.Alias.SecretKeyFactory.PBEWithSHAAnd3KeyTripleDES", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC");
+
+ put("SecretKeyFactory.PBEWITHSHAAND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd128BitAESBC");
+ put("SecretKeyFactory.PBEWITHSHAAND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd192BitAESBC");
+ put("SecretKeyFactory.PBEWITHSHAAND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHAAnd256BitAESBC");
+ put("SecretKeyFactory.PBEWITHSHA256AND128BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And128BitAESBC");
+ put("SecretKeyFactory.PBEWITHSHA256AND192BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And192BitAESBC");
+ put("SecretKeyFactory.PBEWITHSHA256AND256BITAES-CBC-BC", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBEWithSHA256And256BitAESBC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND128BITAES-CBC-BC","PBEWITHSHAAND128BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND192BITAES-CBC-BC","PBEWITHSHAAND192BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-1AND256BITAES-CBC-BC","PBEWITHSHAAND256BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND128BITAES-CBC-BC","PBEWITHSHA256AND128BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND192BITAES-CBC-BC","PBEWITHSHA256AND192BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory.PBEWITHSHA-256AND256BITAES-CBC-BC","PBEWITHSHA256AND256BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.getId(), "PBEWITHSHAAND128BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.getId(), "PBEWITHSHAAND192BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.getId(), "PBEWITHSHAAND256BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PBEWITHSHA256AND128BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PBEWITHSHA256AND192BITAES-CBC-BC");
+ put("Alg.Alias.SecretKeyFactory." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PBEWITHSHA256AND256BITAES-CBC-BC");
+ // BEGIN android-added
+
+ put("SecretKeyFactory.PBKDF2WithHmacSHA1", "org.bouncycastle.jce.provider.JCESecretKeyFactory$PBKDF2WithHmacSHA1");
+ // END android-added
+
+ addMacAlgorithms();
+
+ // Certification Path API
+ // BEGIN android-removed
+ // put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi");
+ // put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi");
+ // END android-removed
+ // BEGIN android-changed
+ // Use Alg.Alias so RFC3280 doesn't show up when iterating provider services, only PKIX
+ put("Alg.Alias.CertPathValidator.RFC3280", "PKIX");
+ put("Alg.Alias.CertPathBuilder.RFC3280", "PKIX");
+ // END android-changed
+ put("CertPathValidator.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi");
+ put("CertPathBuilder.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi");
+ put("CertStore.Collection", "org.bouncycastle.jce.provider.CertStoreCollectionSpi");
+ // BEGIN android-removed
+ // put("CertStore.LDAP", "org.bouncycastle.jce.provider.X509LDAPCertStoreSpi");
+ // put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi");
+ // put("Alg.Alias.CertStore.X509LDAP", "LDAP");
+ // END android-removed
+ }
+
+ private void loadAlgorithms(String packageName, String[] names)
+ {
+ for (int i = 0; i != names.length; i++)
+ {
+ Class clazz = null;
+ try
+ {
+ ClassLoader loader = this.getClass().getClassLoader();
+
+ if (loader != null)
+ {
+ clazz = loader.loadClass(packageName + names[i] + "$Mappings");
+ }
+ else
+ {
+ clazz = Class.forName(packageName + names[i] + "$Mappings");
+ }
+ }
+ catch (ClassNotFoundException e)
+ {
+ // ignore
+ }
+
+ if (clazz != null)
+ {
+ try
+ {
+ ((AlgorithmProvider)clazz.newInstance()).configure(this);
+ }
+ catch (Exception e)
+ { // this should never ever happen!!
+e.printStackTrace();
+ throw new InternalError("cannot create instance of "
+ + packageName + names[i] + "$Mappings : " + e);
+ }
+ }
+ }
+ }
+
+ //
+ // macs
+ //
+ private void addMacAlgorithms()
+ {
+
+ // BEGIN android-removed
+ // put("Mac.DESWITHISO9797", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
+ // put("Alg.Alias.Mac.DESISO9797MAC", "DESWITHISO9797");
+ //
+ // put("Mac.ISO9797ALG3MAC", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3");
+ // put("Alg.Alias.Mac.ISO9797ALG3", "ISO9797ALG3MAC");
+ // put("Mac.ISO9797ALG3WITHISO7816-4PADDING", "org.bouncycastle.jce.provider.JCEMac$DES9797Alg3with7816d4");
+ // put("Alg.Alias.Mac.ISO9797ALG3MACWITHISO7816-4PADDING", "ISO9797ALG3WITHISO7816-4PADDING");
+ //
+ // put("Mac.OLDHMACSHA384", "org.bouncycastle.jce.provider.JCEMac$OldSHA384");
+ //
+ // put("Mac.OLDHMACSHA512", "org.bouncycastle.jce.provider.JCEMac$OldSHA512");
+ // END android-removed
+
+ put("Mac.PBEWITHHMACSHA", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
+ put("Mac.PBEWITHHMACSHA1", "org.bouncycastle.jce.provider.JCEMac$PBEWithSHA");
+ // BEGIN android-removed
+ // put("Mac.PBEWITHHMACRIPEMD160", "org.bouncycastle.jce.provider.JCEMac$PBEWithRIPEMD160");
+ // END android-removed
+ put("Alg.Alias.Mac.1.3.14.3.2.26", "PBEWITHHMACSHA");
+ }
+
+
+ public void setParameter(String parameterName, Object parameter)
+ {
+ synchronized (CONFIGURATION)
+ {
+ ((BouncyCastleProviderConfiguration)CONFIGURATION).setParameter(parameterName, parameter);
+ }
+ }
+
+ public boolean hasAlgorithm(String type, String name)
+ {
+ return containsKey(type + "." + name) || containsKey("Alg.Alias." + type + "." + name);
+ }
+
+ public void addAlgorithm(String key, String value)
+ {
+ if (containsKey(key))
+ {
+ throw new IllegalStateException("duplicate provider key (" + key + ") found");
+ }
+
+ put(key, value);
+ }
+
+ public void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter)
+ {
+ keyInfoConverters.put(oid, keyInfoConverter);
+ }
+
+ public AsymmetricKeyInfoConverter getConverter(ASN1ObjectIdentifier oid)
+ {
+ return (AsymmetricKeyInfoConverter)keyInfoConverters.get(oid);
+ }
+
+ public static PublicKey getPublicKey(SubjectPublicKeyInfo publicKeyInfo)
+ throws IOException
+ {
+ AsymmetricKeyInfoConverter converter = (AsymmetricKeyInfoConverter)keyInfoConverters.get(publicKeyInfo.getAlgorithm().getAlgorithm());
+
+ if (converter == null)
+ {
+ return null;
+ }
+
+ return converter.generatePublic(publicKeyInfo);
+ }
+
+ public static PrivateKey getPrivateKey(PrivateKeyInfo privateKeyInfo)
+ throws IOException
+ {
+ AsymmetricKeyInfoConverter converter = (AsymmetricKeyInfoConverter)keyInfoConverters.get(privateKeyInfo.getPrivateKeyAlgorithm().getAlgorithm());
+
+ if (converter == null)
+ {
+ return null;
+ }
+
+ return converter.generatePrivate(privateKeyInfo);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java
new file mode 100644
index 0000000..b370ea9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.Permission;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import org.bouncycastle.jcajce.provider.asymmetric.ec.EC5Util;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
+import org.bouncycastle.jcajce.provider.config.ProviderConfigurationPermission;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+
+class BouncyCastleProviderConfiguration
+ implements ProviderConfiguration
+{
+ private static Permission BC_EC_LOCAL_PERMISSION = new ProviderConfigurationPermission(
+ BouncyCastleProvider.PROVIDER_NAME, ConfigurableProvider.THREAD_LOCAL_EC_IMPLICITLY_CA);
+ private static Permission BC_EC_PERMISSION = new ProviderConfigurationPermission(
+ BouncyCastleProvider.PROVIDER_NAME, ConfigurableProvider.EC_IMPLICITLY_CA);
+ private static Permission BC_DH_LOCAL_PERMISSION = new ProviderConfigurationPermission(
+ BouncyCastleProvider.PROVIDER_NAME, ConfigurableProvider.THREAD_LOCAL_DH_DEFAULT_PARAMS);
+ private static Permission BC_DH_PERMISSION = new ProviderConfigurationPermission(
+ BouncyCastleProvider.PROVIDER_NAME, ConfigurableProvider.DH_DEFAULT_PARAMS);
+
+ private ThreadLocal ecThreadSpec = new ThreadLocal();
+ private ThreadLocal dhThreadSpec = new ThreadLocal();
+
+ private volatile ECParameterSpec ecImplicitCaParams;
+ private volatile DHParameterSpec dhDefaultParams;
+
+ void setParameter(String parameterName, Object parameter)
+ {
+ SecurityManager securityManager = System.getSecurityManager();
+
+ if (parameterName.equals(ConfigurableProvider.THREAD_LOCAL_EC_IMPLICITLY_CA))
+ {
+ ECParameterSpec curveSpec;
+
+ if (securityManager != null)
+ {
+ securityManager.checkPermission(BC_EC_LOCAL_PERMISSION);
+ }
+
+ if (parameter instanceof ECParameterSpec || parameter == null)
+ {
+ curveSpec = (ECParameterSpec)parameter;
+ }
+ else // assume java.security.spec
+ {
+ curveSpec = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
+ }
+
+ if (curveSpec == null)
+ {
+ ecThreadSpec.remove();
+ }
+ else
+ {
+ ecThreadSpec.set(curveSpec);
+ }
+ }
+ else if (parameterName.equals(ConfigurableProvider.EC_IMPLICITLY_CA))
+ {
+ if (securityManager != null)
+ {
+ securityManager.checkPermission(BC_EC_PERMISSION);
+ }
+
+ if (parameter instanceof ECParameterSpec || parameter == null)
+ {
+ ecImplicitCaParams = (ECParameterSpec)parameter;
+ }
+ else // assume java.security.spec
+ {
+ ecImplicitCaParams = EC5Util.convertSpec((java.security.spec.ECParameterSpec)parameter, false);
+ }
+ }
+ else if (parameterName.equals(ConfigurableProvider.THREAD_LOCAL_DH_DEFAULT_PARAMS))
+ {
+ DHParameterSpec dhSpec;
+
+ if (securityManager != null)
+ {
+ securityManager.checkPermission(BC_DH_LOCAL_PERMISSION);
+ }
+
+ if (parameter instanceof DHParameterSpec || parameter == null)
+ {
+ dhSpec = (DHParameterSpec)parameter;
+ }
+ else
+ {
+ throw new IllegalArgumentException("not a valid DHParameterSpec");
+ }
+
+ if (dhSpec == null)
+ {
+ dhThreadSpec.remove();
+ }
+ else
+ {
+ dhThreadSpec.set(dhSpec);
+ }
+ }
+ else if (parameterName.equals(ConfigurableProvider.DH_DEFAULT_PARAMS))
+ {
+ if (securityManager != null)
+ {
+ securityManager.checkPermission(BC_DH_PERMISSION);
+ }
+
+ if (parameter instanceof DHParameterSpec || parameter == null)
+ {
+ dhDefaultParams = (DHParameterSpec)parameter;
+ }
+ else
+ {
+ throw new IllegalArgumentException("not a valid DHParameterSpec");
+ }
+ }
+ }
+
+ public ECParameterSpec getEcImplicitlyCa()
+ {
+ ECParameterSpec spec = (ECParameterSpec)ecThreadSpec.get();
+
+ if (spec != null)
+ {
+ return spec;
+ }
+
+ return ecImplicitCaParams;
+ }
+
+ public DHParameterSpec getDHDefaultParameters()
+ {
+ DHParameterSpec spec = (DHParameterSpec)dhThreadSpec.get();
+
+ if (spec != null)
+ {
+ return spec;
+ }
+
+ return dhDefaultParams;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertBlacklist.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertBlacklist.java
new file mode 100644
index 0000000..fee3ea8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertBlacklist.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 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 org.bouncycastle.jce.provider;
+
+import java.io.Closeable;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
+import org.bouncycastle.util.encoders.Hex;
+
+public class CertBlacklist {
+
+ private static final String ANDROID_DATA = System.getenv("ANDROID_DATA");
+ private static final String BLACKLIST_ROOT = ANDROID_DATA + "/misc/keychain/";
+ public static final String DEFAULT_PUBKEY_BLACKLIST_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
+ public static final String DEFAULT_SERIAL_BLACKLIST_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";
+
+ private static final Logger logger = Logger.getLogger(CertBlacklist.class.getName());
+
+ // public for testing
+ public final Set<BigInteger> serialBlacklist;
+ public final Set<byte[]> pubkeyBlacklist;
+
+ public CertBlacklist() {
+ this(DEFAULT_PUBKEY_BLACKLIST_PATH, DEFAULT_SERIAL_BLACKLIST_PATH);
+ }
+
+ /** Test only interface, not for public use */
+ public CertBlacklist(String pubkeyBlacklistPath, String serialBlacklistPath) {
+ serialBlacklist = readSerialBlackList(serialBlacklistPath);
+ pubkeyBlacklist = readPublicKeyBlackList(pubkeyBlacklistPath);
+ }
+
+ private static boolean isHex(String value) {
+ try {
+ new BigInteger(value, 16);
+ return true;
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "Could not parse hex value " + value, e);
+ return false;
+ }
+ }
+
+ private static boolean isPubkeyHash(String value) {
+ if (value.length() != 40) {
+ logger.log(Level.WARNING, "Invalid pubkey hash length: " + value.length());
+ return false;
+ }
+ return isHex(value);
+ }
+
+ private static String readBlacklist(String path) {
+ try {
+ return readFileAsString(path);
+ } catch (FileNotFoundException ignored) {
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Could not read blacklist", e);
+ }
+ return "";
+ }
+
+ // From IoUtils.readFileAsString
+ private static String readFileAsString(String path) throws IOException {
+ return readFileAsBytes(path).toString("UTF-8");
+ }
+
+ // Based on IoUtils.readFileAsBytes
+ private static ByteArrayOutputStream readFileAsBytes(String path) throws IOException {
+ RandomAccessFile f = null;
+ try {
+ f = new RandomAccessFile(path, "r");
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream((int) f.length());
+ byte[] buffer = new byte[8192];
+ while (true) {
+ int byteCount = f.read(buffer);
+ if (byteCount == -1) {
+ return bytes;
+ }
+ bytes.write(buffer, 0, byteCount);
+ }
+ } finally {
+ closeQuietly(f);
+ }
+ }
+
+ // Base on IoUtils.closeQuietly
+ private static void closeQuietly(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ private static final Set<BigInteger> readSerialBlackList(String path) {
+
+ // start out with a base set of known bad values
+ Set<BigInteger> bl = new HashSet<BigInteger>(Arrays.asList(
+ // From http://src.chromium.org/viewvc/chrome/trunk/src/net/base/x509_certificate.cc?revision=78748&view=markup
+ // Not a real certificate. For testing only.
+ new BigInteger("077a59bcd53459601ca6907267a6dd1c", 16),
+ new BigInteger("047ecbe9fca55f7bd09eae36e10cae1e", 16),
+ new BigInteger("d8f35f4eb7872b2dab0692e315382fb0", 16),
+ new BigInteger("b0b7133ed096f9b56fae91c874bd3ac0", 16),
+ new BigInteger("9239d5348f40d1695a745470e1f23f43", 16),
+ new BigInteger("e9028b9578e415dc1a710a2b88154447", 16),
+ new BigInteger("d7558fdaf5f1105bb213282b707729a3", 16),
+ new BigInteger("f5c86af36162f13a64f54f6dc9587c06", 16),
+ new BigInteger("392a434f0e07df1f8aa305de34e0c229", 16),
+ new BigInteger("3e75ced46b693021218830ae86a82a71", 16)
+ ));
+
+ // attempt to augment it with values taken from gservices
+ String serialBlacklist = readBlacklist(path);
+ if (!serialBlacklist.equals("")) {
+ for(String value : serialBlacklist.split(",")) {
+ try {
+ bl.add(new BigInteger(value, 16));
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "Tried to blacklist invalid serial number " + value, e);
+ }
+ }
+ }
+
+ // whether that succeeds or fails, send it on its merry way
+ return Collections.unmodifiableSet(bl);
+ }
+
+ private static final Set<byte[]> readPublicKeyBlackList(String path) {
+
+ // start out with a base set of known bad values
+ Set<byte[]> bl = new HashSet<byte[]>(Arrays.asList(
+ // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750
+ // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl
+ "410f36363258f30b347d12ce4863e433437806a8".getBytes(),
+ // Subject: CN=DigiNotar Cyber CA
+ // Issuer: CN=GTE CyberTrust Global Root
+ "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(),
+ // Subject: CN=DigiNotar Services 1024 CA
+ // Issuer: CN=Entrust.net
+ "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(),
+ // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2
+ // Issuer: CN=Staat der Nederlanden Organisatie CA - G2
+ "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(),
+ // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven
+ // Issuer: CN=Staat der Nederlanden Overheid CA
+ "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(),
+ // From http://src.chromium.org/viewvc/chrome?view=rev&revision=108479
+ // Subject: O=Digicert Sdn. Bhd.
+ // Issuer: CN=GTE CyberTrust Global Root
+ "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes()
+ ));
+
+ // attempt to augment it with values taken from gservices
+ String pubkeyBlacklist = readBlacklist(path);
+ if (!pubkeyBlacklist.equals("")) {
+ for (String value : pubkeyBlacklist.split(",")) {
+ value = value.trim();
+ if (isPubkeyHash(value)) {
+ bl.add(value.getBytes());
+ } else {
+ logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value);
+ }
+ }
+ }
+
+ return bl;
+ }
+
+ public boolean isPublicKeyBlackListed(PublicKey publicKey) {
+ byte[] encoded = publicKey.getEncoded();
+ Digest digest = AndroidDigestFactory.getSHA1();
+ digest.update(encoded, 0, encoded.length);
+ byte[] out = new byte[digest.getDigestSize()];
+ digest.doFinal(out, 0);
+ for (byte[] blacklisted : pubkeyBlacklist) {
+ if (Arrays.equals(blacklisted, Hex.encode(out))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isSerialNumberBlackListed(BigInteger serial) {
+ return serialBlacklist.contains(serial);
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
new file mode 100644
index 0000000..f8f6cb4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java
@@ -0,0 +1,1435 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.cert.CRLException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.PKIXParameters;
+import java.security.cert.PolicyQualifierInfo;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509CRLSelector;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.DERGeneralizedTime;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.isismtt.ISISMTTObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+// BEGIN android-removed
+// import org.bouncycastle.jce.X509LDAPCertStoreParameters;
+// END android-removed
+import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.StoreException;
+import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
+// BEGIN android-removed
+// import org.bouncycastle.x509.X509AttributeCertStoreSelector;
+// END android-removed
+import org.bouncycastle.x509.X509AttributeCertificate;
+import org.bouncycastle.x509.X509CRLStoreSelector;
+import org.bouncycastle.x509.X509CertStoreSelector;
+import org.bouncycastle.x509.X509Store;
+
+public class CertPathValidatorUtilities
+{
+ protected static final PKIXCRLUtil CRL_UTIL = new PKIXCRLUtil();
+
+ protected static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId();
+ protected static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId();
+ protected static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId();
+ protected static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId();
+ protected static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId();
+ protected static final String KEY_USAGE = X509Extensions.KeyUsage.getId();
+ protected static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId();
+ protected static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId();
+ protected static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId();
+ protected static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId();
+ protected static final String FRESHEST_CRL = X509Extensions.FreshestCRL.getId();
+ protected static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints.getId();
+ protected static final String AUTHORITY_KEY_IDENTIFIER = X509Extensions.AuthorityKeyIdentifier.getId();
+
+ protected static final String ANY_POLICY = "2.5.29.32.0";
+
+ protected static final String CRL_NUMBER = X509Extensions.CRLNumber.getId();
+
+ /*
+ * key usage bits
+ */
+ protected static final int KEY_CERT_SIGN = 5;
+ protected static final int CRL_SIGN = 6;
+
+ protected static final String[] crlReasons = new String[]{
+ "unspecified",
+ "keyCompromise",
+ "cACompromise",
+ "affiliationChanged",
+ "superseded",
+ "cessationOfOperation",
+ "certificateHold",
+ "unknown",
+ "removeFromCRL",
+ "privilegeWithdrawn",
+ "aACompromise"};
+
+ /**
+ * Search the given Set of TrustAnchor's for one that is the
+ * issuer of the given X509 certificate. Uses the default provider
+ * for signature verification.
+ *
+ * @param cert the X509 certificate
+ * @param trustAnchors a Set of TrustAnchor's
+ * @return the <code>TrustAnchor</code> object if found or
+ * <code>null</code> if not.
+ * @throws AnnotatedException if a TrustAnchor was found but the signature verification
+ * on the given certificate has thrown an exception.
+ */
+ protected static TrustAnchor findTrustAnchor(
+ X509Certificate cert,
+ Set trustAnchors)
+ throws AnnotatedException
+ {
+ return findTrustAnchor(cert, trustAnchors, null);
+ }
+
+ /**
+ * Search the given Set of TrustAnchor's for one that is the
+ * issuer of the given X509 certificate. Uses the specified
+ * provider for signature verification, or the default provider
+ * if null.
+ *
+ * @param cert the X509 certificate
+ * @param trustAnchors a Set of TrustAnchor's
+ * @param sigProvider the provider to use for signature verification
+ * @return the <code>TrustAnchor</code> object if found or
+ * <code>null</code> if not.
+ * @throws AnnotatedException if a TrustAnchor was found but the signature verification
+ * on the given certificate has thrown an exception.
+ */
+ protected static TrustAnchor findTrustAnchor(
+ X509Certificate cert,
+ Set trustAnchors,
+ String sigProvider)
+ throws AnnotatedException
+ {
+ TrustAnchor trust = null;
+ PublicKey trustPublicKey = null;
+ Exception invalidKeyEx = null;
+
+ X509CertSelector certSelectX509 = new X509CertSelector();
+ X500Principal certIssuer = getEncodedIssuerPrincipal(cert);
+
+ try
+ {
+ certSelectX509.setSubject(certIssuer.getEncoded());
+ }
+ catch (IOException ex)
+ {
+ throw new AnnotatedException("Cannot set subject search criteria for trust anchor.", ex);
+ }
+
+ Iterator iter = trustAnchors.iterator();
+ while (iter.hasNext() && trust == null)
+ {
+ trust = (TrustAnchor)iter.next();
+ if (trust.getTrustedCert() != null)
+ {
+ if (certSelectX509.match(trust.getTrustedCert()))
+ {
+ trustPublicKey = trust.getTrustedCert().getPublicKey();
+ }
+ else
+ {
+ trust = null;
+ }
+ }
+ else if (trust.getCAName() != null
+ && trust.getCAPublicKey() != null)
+ {
+ try
+ {
+ X500Principal caName = new X500Principal(trust.getCAName());
+ if (certIssuer.equals(caName))
+ {
+ trustPublicKey = trust.getCAPublicKey();
+ }
+ else
+ {
+ trust = null;
+ }
+ }
+ catch (IllegalArgumentException ex)
+ {
+ trust = null;
+ }
+ }
+ else
+ {
+ trust = null;
+ }
+
+ if (trustPublicKey != null)
+ {
+ try
+ {
+ verifyX509Certificate(cert, trustPublicKey, sigProvider);
+ }
+ catch (Exception ex)
+ {
+ invalidKeyEx = ex;
+ trust = null;
+ trustPublicKey = null;
+ }
+ }
+ }
+
+ if (trust == null && invalidKeyEx != null)
+ {
+ throw new AnnotatedException("TrustAnchor found but certificate validation failed.", invalidKeyEx);
+ }
+
+ return trust;
+ }
+
+ protected static void addAdditionalStoresFromAltNames(
+ X509Certificate cert,
+ ExtendedPKIXParameters pkixParams)
+ throws CertificateParsingException
+ {
+ // if in the IssuerAltName extension an URI
+ // is given, add an additinal X.509 store
+ if (cert.getIssuerAlternativeNames() != null)
+ {
+ Iterator it = cert.getIssuerAlternativeNames().iterator();
+ while (it.hasNext())
+ {
+ // look for URI
+ List list = (List)it.next();
+ // BEGIN android-changed
+ if (list.get(0).equals(Integer.valueOf(GeneralName.uniformResourceIdentifier)))
+ // END android-changed
+ {
+ // found
+ String temp = (String)list.get(1);
+ CertPathValidatorUtilities.addAdditionalStoreFromLocation(temp, pkixParams);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the issuer of an attribute certificate or certificate.
+ *
+ * @param cert The attribute certificate or certificate.
+ * @return The issuer as <code>X500Principal</code>.
+ */
+ protected static X500Principal getEncodedIssuerPrincipal(
+ Object cert)
+ {
+ if (cert instanceof X509Certificate)
+ {
+ return ((X509Certificate)cert).getIssuerX500Principal();
+ }
+ else
+ {
+ return (X500Principal)((X509AttributeCertificate)cert).getIssuer().getPrincipals()[0];
+ }
+ }
+
+ protected static Date getValidDate(PKIXParameters paramsPKIX)
+ {
+ Date validDate = paramsPKIX.getDate();
+
+ if (validDate == null)
+ {
+ validDate = new Date();
+ }
+
+ return validDate;
+ }
+
+ protected static X500Principal getSubjectPrincipal(X509Certificate cert)
+ {
+ return cert.getSubjectX500Principal();
+ }
+
+ protected static boolean isSelfIssued(X509Certificate cert)
+ {
+ return cert.getSubjectDN().equals(cert.getIssuerDN());
+ }
+
+
+ /**
+ * Extract the value of the given extension, if it exists.
+ *
+ * @param ext The extension object.
+ * @param oid The object identifier to obtain.
+ * @throws AnnotatedException if the extension cannot be read.
+ */
+ protected static ASN1Primitive getExtensionValue(
+ java.security.cert.X509Extension ext,
+ String oid)
+ throws AnnotatedException
+ {
+ byte[] bytes = ext.getExtensionValue(oid);
+ if (bytes == null)
+ {
+ return null;
+ }
+
+ return getObject(oid, bytes);
+ }
+
+ private static ASN1Primitive getObject(
+ String oid,
+ byte[] ext)
+ throws AnnotatedException
+ {
+ try
+ {
+ ASN1InputStream aIn = new ASN1InputStream(ext);
+ ASN1OctetString octs = (ASN1OctetString)aIn.readObject();
+
+ aIn = new ASN1InputStream(octs.getOctets());
+ return aIn.readObject();
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("exception processing extension " + oid, e);
+ }
+ }
+
+ protected static X500Principal getIssuerPrincipal(X509CRL crl)
+ {
+ return crl.getIssuerX500Principal();
+ }
+
+ protected static AlgorithmIdentifier getAlgorithmIdentifier(
+ PublicKey key)
+ throws CertPathValidatorException
+ {
+ try
+ {
+ ASN1InputStream aIn = new ASN1InputStream(key.getEncoded());
+
+ SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(aIn.readObject());
+
+ return info.getAlgorithmId();
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Subject public key cannot be decoded.", e);
+ }
+ }
+
+ // crl checking
+
+
+ //
+ // policy checking
+ //
+
+ protected static final Set getQualifierSet(ASN1Sequence qualifiers)
+ throws CertPathValidatorException
+ {
+ Set pq = new HashSet();
+
+ if (qualifiers == null)
+ {
+ return pq;
+ }
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ Enumeration e = qualifiers.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ try
+ {
+ aOut.writeObject((ASN1Encodable)e.nextElement());
+
+ pq.add(new PolicyQualifierInfo(bOut.toByteArray()));
+ }
+ catch (IOException ex)
+ {
+ throw new ExtCertPathValidatorException("Policy qualifier info cannot be decoded.", ex);
+ }
+
+ bOut.reset();
+ }
+
+ return pq;
+ }
+
+ protected static PKIXPolicyNode removePolicyNode(
+ PKIXPolicyNode validPolicyTree,
+ List[] policyNodes,
+ PKIXPolicyNode _node)
+ {
+ PKIXPolicyNode _parent = (PKIXPolicyNode)_node.getParent();
+
+ if (validPolicyTree == null)
+ {
+ return null;
+ }
+
+ if (_parent == null)
+ {
+ for (int j = 0; j < policyNodes.length; j++)
+ {
+ policyNodes[j] = new ArrayList();
+ }
+
+ return null;
+ }
+ else
+ {
+ _parent.removeChild(_node);
+ removePolicyNodeRecurse(policyNodes, _node);
+
+ return validPolicyTree;
+ }
+ }
+
+ private static void removePolicyNodeRecurse(
+ List[] policyNodes,
+ PKIXPolicyNode _node)
+ {
+ policyNodes[_node.getDepth()].remove(_node);
+
+ if (_node.hasChildren())
+ {
+ Iterator _iter = _node.getChildren();
+ while (_iter.hasNext())
+ {
+ PKIXPolicyNode _child = (PKIXPolicyNode)_iter.next();
+ removePolicyNodeRecurse(policyNodes, _child);
+ }
+ }
+ }
+
+
+ protected static boolean processCertD1i(
+ int index,
+ List[] policyNodes,
+ DERObjectIdentifier pOid,
+ Set pq)
+ {
+ List policyNodeVec = policyNodes[index - 1];
+
+ for (int j = 0; j < policyNodeVec.size(); j++)
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)policyNodeVec.get(j);
+ Set expectedPolicies = node.getExpectedPolicies();
+
+ if (expectedPolicies.contains(pOid.getId()))
+ {
+ Set childExpectedPolicies = new HashSet();
+ childExpectedPolicies.add(pOid.getId());
+
+ PKIXPolicyNode child = new PKIXPolicyNode(new ArrayList(),
+ index,
+ childExpectedPolicies,
+ node,
+ pq,
+ pOid.getId(),
+ false);
+ node.addChild(child);
+ policyNodes[index].add(child);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected static void processCertD1ii(
+ int index,
+ List[] policyNodes,
+ DERObjectIdentifier _poid,
+ Set _pq)
+ {
+ List policyNodeVec = policyNodes[index - 1];
+
+ for (int j = 0; j < policyNodeVec.size(); j++)
+ {
+ PKIXPolicyNode _node = (PKIXPolicyNode)policyNodeVec.get(j);
+
+ if (ANY_POLICY.equals(_node.getValidPolicy()))
+ {
+ Set _childExpectedPolicies = new HashSet();
+ _childExpectedPolicies.add(_poid.getId());
+
+ PKIXPolicyNode _child = new PKIXPolicyNode(new ArrayList(),
+ index,
+ _childExpectedPolicies,
+ _node,
+ _pq,
+ _poid.getId(),
+ false);
+ _node.addChild(_child);
+ policyNodes[index].add(_child);
+ return;
+ }
+ }
+ }
+
+ protected static void prepareNextCertB1(
+ int i,
+ List[] policyNodes,
+ String id_p,
+ Map m_idp,
+ X509Certificate cert
+ )
+ throws AnnotatedException, CertPathValidatorException
+ {
+ boolean idp_found = false;
+ Iterator nodes_i = policyNodes[i].iterator();
+ while (nodes_i.hasNext())
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+ if (node.getValidPolicy().equals(id_p))
+ {
+ idp_found = true;
+ node.expectedPolicies = (Set)m_idp.get(id_p);
+ break;
+ }
+ }
+
+ if (!idp_found)
+ {
+ nodes_i = policyNodes[i].iterator();
+ while (nodes_i.hasNext())
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+ if (ANY_POLICY.equals(node.getValidPolicy()))
+ {
+ Set pq = null;
+ ASN1Sequence policies = null;
+ try
+ {
+ policies = DERSequence.getInstance(getExtensionValue(cert, CERTIFICATE_POLICIES));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Certificate policies cannot be decoded.", e);
+ }
+ Enumeration e = policies.getObjects();
+ while (e.hasMoreElements())
+ {
+ PolicyInformation pinfo = null;
+
+ try
+ {
+ pinfo = PolicyInformation.getInstance(e.nextElement());
+ }
+ catch (Exception ex)
+ {
+ throw new AnnotatedException("Policy information cannot be decoded.", ex);
+ }
+ if (ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId()))
+ {
+ try
+ {
+ pq = getQualifierSet(pinfo.getPolicyQualifiers());
+ }
+ catch (CertPathValidatorException ex)
+ {
+ throw new ExtCertPathValidatorException(
+ "Policy qualifier info set could not be built.", ex);
+ }
+ break;
+ }
+ }
+ boolean ci = false;
+ if (cert.getCriticalExtensionOIDs() != null)
+ {
+ ci = cert.getCriticalExtensionOIDs().contains(CERTIFICATE_POLICIES);
+ }
+
+ PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
+ if (ANY_POLICY.equals(p_node.getValidPolicy()))
+ {
+ PKIXPolicyNode c_node = new PKIXPolicyNode(
+ new ArrayList(), i,
+ (Set)m_idp.get(id_p),
+ p_node, pq, id_p, ci);
+ p_node.addChild(c_node);
+ policyNodes[i].add(c_node);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ protected static PKIXPolicyNode prepareNextCertB2(
+ int i,
+ List[] policyNodes,
+ String id_p,
+ PKIXPolicyNode validPolicyTree)
+ {
+ Iterator nodes_i = policyNodes[i].iterator();
+ while (nodes_i.hasNext())
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+ if (node.getValidPolicy().equals(id_p))
+ {
+ PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
+ p_node.removeChild(node);
+ nodes_i.remove();
+ for (int k = (i - 1); k >= 0; k--)
+ {
+ List nodes = policyNodes[k];
+ for (int l = 0; l < nodes.size(); l++)
+ {
+ PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l);
+ if (!node2.hasChildren())
+ {
+ validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, node2);
+ if (validPolicyTree == null)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return validPolicyTree;
+ }
+
+ protected static boolean isAnyPolicy(
+ Set policySet)
+ {
+ return policySet == null || policySet.contains(ANY_POLICY) || policySet.isEmpty();
+ }
+
+ protected static void addAdditionalStoreFromLocation(String location,
+ ExtendedPKIXParameters pkixParams)
+ {
+ if (pkixParams.isAdditionalLocationsEnabled())
+ {
+ try
+ {
+ // BEGIN android-removed
+ // if (location.startsWith("ldap://"))
+ // {
+ // // ldap://directory.d-trust.net/CN=D-TRUST
+ // // Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE
+ // // skip "ldap://"
+ // location = location.substring(7);
+ // // after first / baseDN starts
+ // String base = null;
+ // String url = null;
+ // if (location.indexOf("/") != -1)
+ // {
+ // base = location.substring(location.indexOf("/"));
+ // // URL
+ // url = "ldap://"
+ // + location.substring(0, location.indexOf("/"));
+ // }
+ // else
+ // {
+ // url = "ldap://" + location;
+ // }
+ // // use all purpose parameters
+ // X509LDAPCertStoreParameters params = new X509LDAPCertStoreParameters.Builder(
+ // url, base).build();
+ // pkixParams.addAdditionalStore(X509Store.getInstance(
+ // "CERTIFICATE/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
+ // pkixParams.addAdditionalStore(X509Store.getInstance(
+ // "CRL/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
+ // pkixParams.addAdditionalStore(X509Store.getInstance(
+ // "ATTRIBUTECERTIFICATE/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
+ // pkixParams.addAdditionalStore(X509Store.getInstance(
+ // "CERTIFICATEPAIR/LDAP", params, BouncyCastleProvider.PROVIDER_NAME));
+ // }
+ // END android-removed
+ }
+ catch (Exception e)
+ {
+ // cannot happen
+ throw new RuntimeException("Exception adding X.509 stores.");
+ }
+ }
+ }
+
+ /**
+ * Return a Collection of all certificates or attribute certificates found
+ * in the X509Store's that are matching the certSelect criteriums.
+ *
+ * @param certSelect a {@link Selector} object that will be used to select
+ * the certificates
+ * @param certStores a List containing only {@link X509Store} objects. These
+ * are used to search for certificates.
+ * @return a Collection of all found {@link X509Certificate} or
+ * {@link org.bouncycastle.x509.X509AttributeCertificate} objects.
+ * May be empty but never <code>null</code>.
+ */
+ protected static Collection findCertificates(X509CertStoreSelector certSelect,
+ List certStores)
+ throws AnnotatedException
+ {
+ Set certs = new HashSet();
+ Iterator iter = certStores.iterator();
+
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if (obj instanceof X509Store)
+ {
+ X509Store certStore = (X509Store)obj;
+ try
+ {
+ certs.addAll(certStore.getMatches(certSelect));
+ }
+ catch (StoreException e)
+ {
+ throw new AnnotatedException(
+ "Problem while picking certificates from X.509 store.", e);
+ }
+ }
+ else
+ {
+ CertStore certStore = (CertStore)obj;
+
+ try
+ {
+ certs.addAll(certStore.getCertificates(certSelect));
+ }
+ catch (CertStoreException e)
+ {
+ throw new AnnotatedException(
+ "Problem while picking certificates from certificate store.",
+ e);
+ }
+ }
+ }
+ return certs;
+ }
+
+ // BEGIN android-removed
+ // protected static Collection findCertificates(X509AttributeCertStoreSelector certSelect,
+ // List certStores)
+ // throws AnnotatedException
+ // {
+ // Set certs = new HashSet();
+ // Iterator iter = certStores.iterator();
+ //
+ // while (iter.hasNext())
+ // {
+ // Object obj = iter.next();
+ //
+ // if (obj instanceof X509Store)
+ // {
+ // X509Store certStore = (X509Store)obj;
+ // try
+ // {
+ // certs.addAll(certStore.getMatches(certSelect));
+ // }
+ // catch (StoreException e)
+ // {
+ // throw new AnnotatedException(
+ // "Problem while picking certificates from X.509 store.", e);
+ // }
+ // }
+ // }
+ // return certs;
+ // }
+ // END android-removed
+
+ protected static void addAdditionalStoresFromCRLDistributionPoint(
+ CRLDistPoint crldp, ExtendedPKIXParameters pkixParams)
+ throws AnnotatedException
+ {
+ if (crldp != null)
+ {
+ DistributionPoint dps[] = null;
+ try
+ {
+ dps = crldp.getDistributionPoints();
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "Distribution points could not be read.", e);
+ }
+ for (int i = 0; i < dps.length; i++)
+ {
+ DistributionPointName dpn = dps[i].getDistributionPoint();
+ // look for URIs in fullName
+ if (dpn != null)
+ {
+ if (dpn.getType() == DistributionPointName.FULL_NAME)
+ {
+ GeneralName[] genNames = GeneralNames.getInstance(
+ dpn.getName()).getNames();
+ // look for an URI
+ for (int j = 0; j < genNames.length; j++)
+ {
+ if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier)
+ {
+ String location = DERIA5String.getInstance(
+ genNames[j].getName()).getString();
+ CertPathValidatorUtilities
+ .addAdditionalStoreFromLocation(location,
+ pkixParams);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add the CRL issuers from the cRLIssuer field of the distribution point or
+ * from the certificate if not given to the issuer criterion of the
+ * <code>selector</code>.
+ * <p/>
+ * The <code>issuerPrincipals</code> are a collection with a single
+ * <code>X500Principal</code> for <code>X509Certificate</code>s. For
+ * {@link X509AttributeCertificate}s the issuer may contain more than one
+ * <code>X500Principal</code>.
+ *
+ * @param dp The distribution point.
+ * @param issuerPrincipals The issuers of the certificate or attribute
+ * certificate which contains the distribution point.
+ * @param selector The CRL selector.
+ * @param pkixParams The PKIX parameters containing the cert stores.
+ * @throws AnnotatedException if an exception occurs while processing.
+ * @throws ClassCastException if <code>issuerPrincipals</code> does not
+ * contain only <code>X500Principal</code>s.
+ */
+ protected static void getCRLIssuersFromDistributionPoint(
+ DistributionPoint dp,
+ Collection issuerPrincipals,
+ X509CRLSelector selector,
+ ExtendedPKIXParameters pkixParams)
+ throws AnnotatedException
+ {
+ List issuers = new ArrayList();
+ // indirect CRL
+ if (dp.getCRLIssuer() != null)
+ {
+ GeneralName genNames[] = dp.getCRLIssuer().getNames();
+ // look for a DN
+ for (int j = 0; j < genNames.length; j++)
+ {
+ if (genNames[j].getTagNo() == GeneralName.directoryName)
+ {
+ try
+ {
+ issuers.add(new X500Principal(genNames[j].getName()
+ .toASN1Primitive().getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException(
+ "CRL issuer information from distribution point cannot be decoded.",
+ e);
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * certificate issuer is CRL issuer, distributionPoint field MUST be
+ * present.
+ */
+ if (dp.getDistributionPoint() == null)
+ {
+ throw new AnnotatedException(
+ "CRL issuer is omitted from distribution point but no distributionPoint field present.");
+ }
+ // add and check issuer principals
+ for (Iterator it = issuerPrincipals.iterator(); it.hasNext(); )
+ {
+ issuers.add((X500Principal)it.next());
+ }
+ }
+ // TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
+ // distributionPoint
+// if (dp.getDistributionPoint() != null)
+// {
+// // look for nameRelativeToCRLIssuer
+// if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+// {
+// // append fragment to issuer, only one
+// // issuer can be there, if this is given
+// if (issuers.size() != 1)
+// {
+// throw new AnnotatedException(
+// "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
+// }
+// ASN1Encodable relName = dp.getDistributionPoint().getName();
+// Iterator it = issuers.iterator();
+// List issuersTemp = new ArrayList(issuers.size());
+// while (it.hasNext())
+// {
+// Enumeration e = null;
+// try
+// {
+// e = ASN1Sequence.getInstance(
+// new ASN1InputStream(((X500Principal) it.next())
+// .getEncoded()).readObject()).getObjects();
+// }
+// catch (IOException ex)
+// {
+// throw new AnnotatedException(
+// "Cannot decode CRL issuer information.", ex);
+// }
+// ASN1EncodableVector v = new ASN1EncodableVector();
+// while (e.hasMoreElements())
+// {
+// v.add((ASN1Encodable) e.nextElement());
+// }
+// v.add(relName);
+// issuersTemp.add(new X500Principal(new DERSequence(v)
+// .getDEREncoded()));
+// }
+// issuers.clear();
+// issuers.addAll(issuersTemp);
+// }
+// }
+ Iterator it = issuers.iterator();
+ while (it.hasNext())
+ {
+ try
+ {
+ selector.addIssuerName(((X500Principal)it.next()).getEncoded());
+ }
+ catch (IOException ex)
+ {
+ throw new AnnotatedException(
+ "Cannot decode CRL issuer information.", ex);
+ }
+ }
+ }
+
+ private static BigInteger getSerialNumber(
+ Object cert)
+ {
+ if (cert instanceof X509Certificate)
+ {
+ return ((X509Certificate)cert).getSerialNumber();
+ }
+ else
+ {
+ return ((X509AttributeCertificate)cert).getSerialNumber();
+ }
+ }
+
+ protected static void getCertStatus(
+ Date validDate,
+ X509CRL crl,
+ Object cert,
+ CertStatus certStatus)
+ throws AnnotatedException
+ {
+ X509CRLEntry crl_entry = null;
+
+ boolean isIndirect;
+ try
+ {
+ isIndirect = X509CRLObject.isIndirectCRL(crl);
+ }
+ catch (CRLException exception)
+ {
+ throw new AnnotatedException("Failed check for indirect CRL.", exception);
+ }
+
+ if (isIndirect)
+ {
+ crl_entry = crl.getRevokedCertificate(getSerialNumber(cert));
+
+ if (crl_entry == null)
+ {
+ return;
+ }
+
+ X500Principal certIssuer = crl_entry.getCertificateIssuer();
+
+ if (certIssuer == null)
+ {
+ certIssuer = getIssuerPrincipal(crl);
+ }
+
+ if (!getEncodedIssuerPrincipal(cert).equals(certIssuer))
+ {
+ return;
+ }
+ }
+ else if (!getEncodedIssuerPrincipal(cert).equals(getIssuerPrincipal(crl)))
+ {
+ return; // not for our issuer, ignore
+ }
+ else
+ {
+ crl_entry = crl.getRevokedCertificate(getSerialNumber(cert));
+
+ if (crl_entry == null)
+ {
+ return;
+ }
+ }
+
+ DEREnumerated reasonCode = null;
+ if (crl_entry.hasExtensions())
+ {
+ try
+ {
+ reasonCode = DEREnumerated
+ .getInstance(CertPathValidatorUtilities
+ .getExtensionValue(crl_entry,
+ X509Extension.reasonCode.getId()));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "Reason code CRL entry extension could not be decoded.",
+ e);
+ }
+ }
+
+ // for reason keyCompromise, caCompromise, aACompromise or
+ // unspecified
+ if (!(validDate.getTime() < crl_entry.getRevocationDate().getTime())
+ || reasonCode == null
+ || reasonCode.getValue().intValue() == 0
+ || reasonCode.getValue().intValue() == 1
+ || reasonCode.getValue().intValue() == 2
+ || reasonCode.getValue().intValue() == 8)
+ {
+
+ // (i) or (j) (1)
+ if (reasonCode != null)
+ {
+ certStatus.setCertStatus(reasonCode.getValue().intValue());
+ }
+ // (i) or (j) (2)
+ else
+ {
+ certStatus.setCertStatus(CRLReason.unspecified);
+ }
+ certStatus.setRevocationDate(crl_entry.getRevocationDate());
+ }
+ }
+
+ /**
+ * Fetches delta CRLs according to RFC 3280 section 5.2.4.
+ *
+ * @param currentDate The date for which the delta CRLs must be valid.
+ * @param paramsPKIX The extended PKIX parameters.
+ * @param completeCRL The complete CRL the delta CRL is for.
+ * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs.
+ * @throws AnnotatedException if an exception occurs while picking the delta
+ * CRLs.
+ */
+ protected static Set getDeltaCRLs(Date currentDate,
+ ExtendedPKIXParameters paramsPKIX, X509CRL completeCRL)
+ throws AnnotatedException
+ {
+
+ X509CRLStoreSelector deltaSelect = new X509CRLStoreSelector();
+
+ // 5.2.4 (a)
+ try
+ {
+ deltaSelect.addIssuerName(CertPathValidatorUtilities
+ .getIssuerPrincipal(completeCRL).getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException("Cannot extract issuer from CRL.", e);
+ }
+
+ BigInteger completeCRLNumber = null;
+ try
+ {
+ ASN1Primitive derObject = CertPathValidatorUtilities.getExtensionValue(completeCRL,
+ CRL_NUMBER);
+ if (derObject != null)
+ {
+ completeCRLNumber = ASN1Integer.getInstance(derObject).getPositiveValue();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "CRL number extension could not be extracted from CRL.", e);
+ }
+
+ // 5.2.4 (b)
+ byte[] idp = null;
+ try
+ {
+ idp = completeCRL.getExtensionValue(ISSUING_DISTRIBUTION_POINT);
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "Issuing distribution point extension value could not be read.",
+ e);
+ }
+
+ // 5.2.4 (d)
+
+ deltaSelect.setMinCRLNumber(completeCRLNumber == null ? null : completeCRLNumber
+ .add(BigInteger.valueOf(1)));
+
+ deltaSelect.setIssuingDistributionPoint(idp);
+ deltaSelect.setIssuingDistributionPointEnabled(true);
+
+ // 5.2.4 (c)
+ deltaSelect.setMaxBaseCRLNumber(completeCRLNumber);
+
+ // find delta CRLs
+ Set temp = CRL_UTIL.findCRLs(deltaSelect, paramsPKIX, currentDate);
+
+ Set result = new HashSet();
+
+ for (Iterator it = temp.iterator(); it.hasNext(); )
+ {
+ X509CRL crl = (X509CRL)it.next();
+
+ if (isDeltaCRL(crl))
+ {
+ result.add(crl);
+ }
+ }
+
+ return result;
+ }
+
+ private static boolean isDeltaCRL(X509CRL crl)
+ {
+ Set critical = crl.getCriticalExtensionOIDs();
+
+ if (critical == null)
+ {
+ return false;
+ }
+
+ return critical.contains(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+ }
+
+ /**
+ * Fetches complete CRLs according to RFC 3280.
+ *
+ * @param dp The distribution point for which the complete CRL
+ * @param cert The <code>X509Certificate</code> or
+ * {@link org.bouncycastle.x509.X509AttributeCertificate} for
+ * which the CRL should be searched.
+ * @param currentDate The date for which the delta CRLs must be valid.
+ * @param paramsPKIX The extended PKIX parameters.
+ * @return A <code>Set</code> of <code>X509CRL</code>s with complete
+ * CRLs.
+ * @throws AnnotatedException if an exception occurs while picking the CRLs
+ * or no CRLs are found.
+ */
+ protected static Set getCompleteCRLs(DistributionPoint dp, Object cert,
+ Date currentDate, ExtendedPKIXParameters paramsPKIX)
+ throws AnnotatedException
+ {
+ X509CRLStoreSelector crlselect = new X509CRLStoreSelector();
+ try
+ {
+ Set issuers = new HashSet();
+ if (cert instanceof X509AttributeCertificate)
+ {
+ issuers.add(((X509AttributeCertificate)cert)
+ .getIssuer().getPrincipals()[0]);
+ }
+ else
+ {
+ issuers.add(getEncodedIssuerPrincipal(cert));
+ }
+ CertPathValidatorUtilities.getCRLIssuersFromDistributionPoint(dp, issuers, crlselect, paramsPKIX);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException(
+ "Could not get issuer information from distribution point.", e);
+ }
+ if (cert instanceof X509Certificate)
+ {
+ crlselect.setCertificateChecking((X509Certificate)cert);
+ }
+ else if (cert instanceof X509AttributeCertificate)
+ {
+ crlselect.setAttrCertificateChecking((X509AttributeCertificate)cert);
+ }
+
+
+ crlselect.setCompleteCRLEnabled(true);
+
+ Set crls = CRL_UTIL.findCRLs(crlselect, paramsPKIX, currentDate);
+
+ if (crls.isEmpty())
+ {
+ if (cert instanceof X509AttributeCertificate)
+ {
+ X509AttributeCertificate aCert = (X509AttributeCertificate)cert;
+
+ throw new AnnotatedException("No CRLs found for issuer \"" + aCert.getIssuer().getPrincipals()[0] + "\"");
+ }
+ else
+ {
+ X509Certificate xCert = (X509Certificate)cert;
+
+ throw new AnnotatedException("No CRLs found for issuer \"" + xCert.getIssuerX500Principal() + "\"");
+ }
+ }
+ return crls;
+ }
+
+ protected static Date getValidCertDateFromValidityModel(
+ ExtendedPKIXParameters paramsPKIX, CertPath certPath, int index)
+ throws AnnotatedException
+ {
+ if (paramsPKIX.getValidityModel() == ExtendedPKIXParameters.CHAIN_VALIDITY_MODEL)
+ {
+ // if end cert use given signing/encryption/... time
+ if (index <= 0)
+ {
+ return CertPathValidatorUtilities.getValidDate(paramsPKIX);
+ // else use time when previous cert was created
+ }
+ else
+ {
+ if (index - 1 == 0)
+ {
+ DERGeneralizedTime dateOfCertgen = null;
+ try
+ {
+ byte[] extBytes = ((X509Certificate)certPath.getCertificates().get(index - 1)).getExtensionValue(ISISMTTObjectIdentifiers.id_isismtt_at_dateOfCertGen.getId());
+ if (extBytes != null)
+ {
+ dateOfCertgen = DERGeneralizedTime.getInstance(ASN1Primitive.fromByteArray(extBytes));
+ }
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException(
+ "Date of cert gen extension could not be read.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new AnnotatedException(
+ "Date of cert gen extension could not be read.");
+ }
+ if (dateOfCertgen != null)
+ {
+ try
+ {
+ return dateOfCertgen.getDate();
+ }
+ catch (ParseException e)
+ {
+ throw new AnnotatedException(
+ "Date from date of cert gen extension could not be parsed.",
+ e);
+ }
+ }
+ return ((X509Certificate)certPath.getCertificates().get(
+ index - 1)).getNotBefore();
+ }
+ else
+ {
+ return ((X509Certificate)certPath.getCertificates().get(
+ index - 1)).getNotBefore();
+ }
+ }
+ }
+ else
+ {
+ return getValidDate(paramsPKIX);
+ }
+ }
+
+ /**
+ * Return the next working key inheriting DSA parameters if necessary.
+ * <p>
+ * This methods inherits DSA parameters from the indexed certificate or
+ * previous certificates in the certificate chain to the returned
+ * <code>PublicKey</code>. The list is searched upwards, meaning the end
+ * certificate is at position 0 and previous certificates are following.
+ * </p>
+ * <p>
+ * If the indexed certificate does not contain a DSA key this method simply
+ * returns the public key. If the DSA key already contains DSA parameters
+ * the key is also only returned.
+ * </p>
+ *
+ * @param certs The certification path.
+ * @param index The index of the certificate which contains the public key
+ * which should be extended with DSA parameters.
+ * @return The public key of the certificate in list position
+ * <code>index</code> extended with DSA parameters if applicable.
+ * @throws AnnotatedException if DSA parameters cannot be inherited.
+ */
+ protected static PublicKey getNextWorkingKey(List certs, int index)
+ throws CertPathValidatorException
+ {
+ Certificate cert = (Certificate)certs.get(index);
+ PublicKey pubKey = cert.getPublicKey();
+ if (!(pubKey instanceof DSAPublicKey))
+ {
+ return pubKey;
+ }
+ DSAPublicKey dsaPubKey = (DSAPublicKey)pubKey;
+ if (dsaPubKey.getParams() != null)
+ {
+ return dsaPubKey;
+ }
+ for (int i = index + 1; i < certs.size(); i++)
+ {
+ X509Certificate parentCert = (X509Certificate)certs.get(i);
+ pubKey = parentCert.getPublicKey();
+ if (!(pubKey instanceof DSAPublicKey))
+ {
+ throw new CertPathValidatorException(
+ "DSA parameters cannot be inherited from previous certificate.");
+ }
+ DSAPublicKey prevDSAPubKey = (DSAPublicKey)pubKey;
+ if (prevDSAPubKey.getParams() == null)
+ {
+ continue;
+ }
+ DSAParams dsaParams = prevDSAPubKey.getParams();
+ DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(
+ dsaPubKey.getY(), dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
+ try
+ {
+ KeyFactory keyFactory = KeyFactory.getInstance("DSA", BouncyCastleProvider.PROVIDER_NAME);
+ return keyFactory.generatePublic(dsaPubKeySpec);
+ }
+ catch (Exception exception)
+ {
+ throw new RuntimeException(exception.getMessage());
+ }
+ }
+ throw new CertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
+ }
+
+ /**
+ * Find the issuer certificates of a given certificate.
+ *
+ * @param cert The certificate for which an issuer should be found.
+ * @param pkixParams
+ * @return A <code>Collection</code> object containing the issuer
+ * <code>X509Certificate</code>s. Never <code>null</code>.
+ * @throws AnnotatedException if an error occurs.
+ */
+ protected static Collection findIssuerCerts(
+ X509Certificate cert,
+ ExtendedPKIXBuilderParameters pkixParams)
+ throws AnnotatedException
+ {
+ X509CertStoreSelector certSelect = new X509CertStoreSelector();
+ Set certs = new HashSet();
+ try
+ {
+ certSelect.setSubject(cert.getIssuerX500Principal().getEncoded());
+ }
+ catch (IOException ex)
+ {
+ throw new AnnotatedException(
+ "Subject criteria for certificate selector to find issuer certificate could not be set.", ex);
+ }
+
+ Iterator iter;
+
+ try
+ {
+ List matches = new ArrayList();
+
+ matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixParams.getCertStores()));
+ matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixParams.getStores()));
+ matches.addAll(CertPathValidatorUtilities.findCertificates(certSelect, pkixParams.getAdditionalStores()));
+
+ iter = matches.iterator();
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Issuer certificate cannot be searched.", e);
+ }
+
+ X509Certificate issuer = null;
+ while (iter.hasNext())
+ {
+ issuer = (X509Certificate)iter.next();
+ // issuer cannot be verified because possible DSA inheritance
+ // parameters are missing
+ certs.add(issuer);
+ }
+ return certs;
+ }
+
+ protected static void verifyX509Certificate(X509Certificate cert, PublicKey publicKey,
+ String sigProvider)
+ throws GeneralSecurityException
+ {
+ if (sigProvider == null)
+ {
+ cert.verify(publicKey);
+ }
+ else
+ {
+ cert.verify(publicKey, sigProvider);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertStatus.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertStatus.java
new file mode 100644
index 0000000..ba3da16
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertStatus.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.jce.provider;
+
+import java.util.Date;
+
+class CertStatus
+{
+ public static final int UNREVOKED = 11;
+
+ public static final int UNDETERMINED = 12;
+
+ int certStatus = UNREVOKED;
+
+ Date revocationDate = null;
+
+ /**
+ * @return Returns the revocationDate.
+ */
+ public Date getRevocationDate()
+ {
+ return revocationDate;
+ }
+
+ /**
+ * @param revocationDate The revocationDate to set.
+ */
+ public void setRevocationDate(Date revocationDate)
+ {
+ this.revocationDate = revocationDate;
+ }
+
+ /**
+ * @return Returns the certStatus.
+ */
+ public int getCertStatus()
+ {
+ return certStatus;
+ }
+
+ /**
+ * @param certStatus The certStatus to set.
+ */
+ public void setCertStatus(int certStatus)
+ {
+ this.certStatus = certStatus;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java
new file mode 100644
index 0000000..210d986
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.CRL;
+import java.security.cert.CRLSelector;
+import java.security.cert.CertSelector;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CertStoreSpi;
+import java.security.cert.Certificate;
+import java.security.cert.CollectionCertStoreParameters;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public class CertStoreCollectionSpi extends CertStoreSpi
+{
+ private CollectionCertStoreParameters params;
+
+ public CertStoreCollectionSpi(CertStoreParameters params)
+ throws InvalidAlgorithmParameterException
+ {
+ super(params);
+
+ if (!(params instanceof CollectionCertStoreParameters))
+ {
+ throw new InvalidAlgorithmParameterException("org.bouncycastle.jce.provider.CertStoreCollectionSpi: parameter must be a CollectionCertStoreParameters object\n" + params.toString());
+ }
+
+ this.params = (CollectionCertStoreParameters)params;
+ }
+
+ public Collection engineGetCertificates(
+ CertSelector selector)
+ throws CertStoreException
+ {
+ List col = new ArrayList();
+ Iterator iter = params.getCollection().iterator();
+
+ if (selector == null)
+ {
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if (obj instanceof Certificate)
+ {
+ col.add(obj);
+ }
+ }
+ }
+ else
+ {
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if ((obj instanceof Certificate) && selector.match((Certificate)obj))
+ {
+ col.add(obj);
+ }
+ }
+ }
+
+ return col;
+ }
+
+
+ public Collection engineGetCRLs(
+ CRLSelector selector)
+ throws CertStoreException
+ {
+ List col = new ArrayList();
+ Iterator iter = params.getCollection().iterator();
+
+ if (selector == null)
+ {
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if (obj instanceof CRL)
+ {
+ col.add(obj);
+ }
+ }
+ }
+ else
+ {
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if ((obj instanceof CRL) && selector.match((CRL)obj))
+ {
+ col.add(obj);
+ }
+ }
+ }
+
+ return col;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/DHUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/DHUtil.java
new file mode 100644
index 0000000..2470af9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/DHUtil.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+/**
+ * utility class for converting jce/jca DH objects
+ * objects into their org.bouncycastle.crypto counterparts.
+ */
+public class DHUtil
+{
+ static public AsymmetricKeyParameter generatePublicKeyParameter(
+ PublicKey key)
+ throws InvalidKeyException
+ {
+ if (key instanceof DHPublicKey)
+ {
+ DHPublicKey k = (DHPublicKey)key;
+
+ return new DHPublicKeyParameters(k.getY(),
+ new DHParameters(k.getParams().getP(), k.getParams().getG(), null, k.getParams().getL()));
+ }
+
+ throw new InvalidKeyException("can't identify DH public key.");
+ }
+
+ static public AsymmetricKeyParameter generatePrivateKeyParameter(
+ PrivateKey key)
+ throws InvalidKeyException
+ {
+ if (key instanceof DHPrivateKey)
+ {
+ DHPrivateKey k = (DHPrivateKey)key;
+
+ return new DHPrivateKeyParameters(k.getX(),
+ new DHParameters(k.getParams().getP(), k.getParams().getG(), null, k.getParams().getL()));
+ }
+
+ throw new InvalidKeyException("can't identify DH private key.");
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java
new file mode 100644
index 0000000..3bc820f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.cert.CRLException;
+
+class ExtCRLException
+ extends CRLException
+{
+ Throwable cause;
+
+ ExtCRLException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
new file mode 100644
index 0000000..2205a26
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
@@ -0,0 +1,1103 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+// BEGIN android-removed
+// import javax.crypto.spec.RC2ParameterSpec;
+// import javax.crypto.spec.RC5ParameterSpec;
+// END android-removed
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.GOST28147Engine;
+// END android-removed
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CCMBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.CTSBlockCipher;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.modes.EAXBlockCipher;
+// END android-removed
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.modes.GOFBBlockCipher;
+// END android-removed
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
+// import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
+// END android-removed
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.paddings.TBCPadding;
+import org.bouncycastle.crypto.paddings.X923Padding;
+import org.bouncycastle.crypto.paddings.ZeroBytePadding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.params.ParametersWithSBox;
+// import org.bouncycastle.crypto.params.RC2Parameters;
+// import org.bouncycastle.crypto.params.RC5Parameters;
+// END android-removed
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
+// END android-removed
+import org.bouncycastle.jce.spec.RepeatedSecretKeySpec;
+import org.bouncycastle.util.Strings;
+
+public class JCEBlockCipher
+ extends CipherSpi
+ implements PBE
+{
+ //
+ // specs we can handle.
+ //
+ private Class[] availableSpecs =
+ {
+ // BEGIN android-removed
+ // RC2ParameterSpec.class,
+ // RC5ParameterSpec.class,
+ // END android-removed
+ IvParameterSpec.class,
+ PBEParameterSpec.class,
+ // BEGIN android-removed
+ // GOST28147ParameterSpec.class
+ // END android-removed
+ };
+
+ private BlockCipher baseEngine;
+ private GenericBlockCipher cipher;
+ private ParametersWithIV ivParam;
+
+ private int ivLength = 0;
+
+ private boolean padded;
+
+ private PBEParameterSpec pbeSpec = null;
+ private String pbeAlgorithm = null;
+
+ private String modeName = null;
+
+ private AlgorithmParameters engineParams;
+
+ protected JCEBlockCipher(
+ BlockCipher engine)
+ {
+ baseEngine = engine;
+
+ cipher = new BufferedGenericBlockCipher(engine);
+ }
+
+ protected JCEBlockCipher(
+ BlockCipher engine,
+ int ivLength)
+ {
+ baseEngine = engine;
+
+ this.cipher = new BufferedGenericBlockCipher(engine);
+ this.ivLength = ivLength / 8;
+ }
+
+ protected JCEBlockCipher(
+ BufferedBlockCipher engine,
+ int ivLength)
+ {
+ baseEngine = engine.getUnderlyingCipher();
+
+ this.cipher = new BufferedGenericBlockCipher(engine);
+ this.ivLength = ivLength / 8;
+ }
+
+ protected int engineGetBlockSize()
+ {
+ return baseEngine.getBlockSize();
+ }
+
+ protected byte[] engineGetIV()
+ {
+ return (ivParam != null) ? ivParam.getIV() : null;
+ }
+
+ protected int engineGetKeySize(
+ Key key)
+ {
+ return key.getEncoded().length * 8;
+ }
+
+ protected int engineGetOutputSize(
+ int inputLen)
+ {
+ return cipher.getOutputSize(inputLen);
+ }
+
+ protected AlgorithmParameters engineGetParameters()
+ {
+ if (engineParams == null)
+ {
+ if (pbeSpec != null)
+ {
+ try
+ {
+ engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
+ engineParams.init(pbeSpec);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ else if (ivParam != null)
+ {
+ String name = cipher.getUnderlyingCipher().getAlgorithmName();
+
+ if (name.indexOf('/') >= 0)
+ {
+ name = name.substring(0, name.indexOf('/'));
+ }
+
+ try
+ {
+ engineParams = AlgorithmParameters.getInstance(name, BouncyCastleProvider.PROVIDER_NAME);
+ engineParams.init(ivParam.getIV());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ }
+
+ return engineParams;
+ }
+
+ protected void engineSetMode(
+ String mode)
+ throws NoSuchAlgorithmException
+ {
+ modeName = Strings.toUpperCase(mode);
+
+ if (modeName.equals("ECB"))
+ {
+ ivLength = 0;
+ cipher = new BufferedGenericBlockCipher(baseEngine);
+ }
+ else if (modeName.equals("CBC"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(
+ new CBCBlockCipher(baseEngine));
+ }
+ else if (modeName.startsWith("OFB"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ if (modeName.length() != 3)
+ {
+ int wordSize = Integer.parseInt(modeName.substring(3));
+
+ cipher = new BufferedGenericBlockCipher(
+ new OFBBlockCipher(baseEngine, wordSize));
+ }
+ else
+ {
+ cipher = new BufferedGenericBlockCipher(
+ new OFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+ }
+ }
+ else if (modeName.startsWith("CFB"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ if (modeName.length() != 3)
+ {
+ int wordSize = Integer.parseInt(modeName.substring(3));
+
+ cipher = new BufferedGenericBlockCipher(
+ new CFBBlockCipher(baseEngine, wordSize));
+ }
+ else
+ {
+ cipher = new BufferedGenericBlockCipher(
+ new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+ }
+ }
+ // BEGIN android-removed
+ // else if (modeName.startsWith("PGP"))
+ // {
+ // boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV");
+ //
+ // ivLength = baseEngine.getBlockSize();
+ // cipher = new BufferedGenericBlockCipher(
+ // new PGPCFBBlockCipher(baseEngine, inlineIV));
+ // }
+ // else if (modeName.equalsIgnoreCase("OpenPGPCFB"))
+ // {
+ // ivLength = 0;
+ // cipher = new BufferedGenericBlockCipher(
+ // new OpenPGPCFBBlockCipher(baseEngine));
+ // }
+ // END android-removed
+ else if (modeName.startsWith("SIC"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ if (ivLength < 16)
+ {
+ throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
+ }
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ new SICBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("CTR"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ new SICBlockCipher(baseEngine)));
+ }
+ // BEGIN android-removed
+ // else if (modeName.startsWith("GOFB"))
+ // {
+ // ivLength = baseEngine.getBlockSize();
+ // cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ // new GOFBBlockCipher(baseEngine)));
+ // }
+ // END android-removed
+ else if (modeName.startsWith("CTS"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("CCM"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+ }
+ // BEGIN android-removed
+ // else if (modeName.startsWith("EAX"))
+ // {
+ // ivLength = baseEngine.getBlockSize();
+ // cipher = new AEADGenericBlockCipher(new EAXBlockCipher(baseEngine));
+ // }
+ // END android-removed
+ else if (modeName.startsWith("GCM"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+ }
+ else
+ {
+ throw new NoSuchAlgorithmException("can't support mode " + mode);
+ }
+ }
+
+ protected void engineSetPadding(
+ String padding)
+ throws NoSuchPaddingException
+ {
+ String paddingName = Strings.toUpperCase(padding);
+
+ if (paddingName.equals("NOPADDING"))
+ {
+ if (cipher.wrapOnNoPadding())
+ {
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
+ }
+ }
+ else if (paddingName.equals("WITHCTS"))
+ {
+ cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher()));
+ }
+ else
+ {
+ padded = true;
+
+ if (isAEADModeName(modeName))
+ {
+ throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes.");
+ }
+ else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher());
+ }
+ else if (paddingName.equals("ZEROBYTEPADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding());
+ }
+ else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding());
+ }
+ else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new X923Padding());
+ }
+ else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding());
+ }
+ else if (paddingName.equals("TBCPADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding());
+ }
+ else
+ {
+ throw new NoSuchPaddingException("Padding " + padding + " unknown.");
+ }
+ }
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ CipherParameters param;
+
+ this.pbeSpec = null;
+ this.pbeAlgorithm = null;
+ this.engineParams = null;
+
+ //
+ // basic key check
+ //
+ if (!(key instanceof SecretKey))
+ {
+ throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption.");
+ }
+
+ // BEGIN android-removed
+ // //
+ // // for RC5-64 we must have some default parameters
+ // //
+ // if (params == null && baseEngine.getAlgorithmName().startsWith("RC5-64"))
+ // {
+ // throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in.");
+ // }
+ // END android-removed
+
+ //
+ // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it).
+ //
+ if (key instanceof BCPBEKey)
+ {
+ BCPBEKey k = (BCPBEKey)key;
+
+ if (k.getOID() != null)
+ {
+ pbeAlgorithm = k.getOID().getId();
+ }
+ else
+ {
+ pbeAlgorithm = k.getAlgorithm();
+ }
+
+ if (k.getParam() != null)
+ {
+ param = k.getParam();
+ pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount());
+ }
+ else if (params instanceof PBEParameterSpec)
+ {
+ pbeSpec = (PBEParameterSpec)params;
+ param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName());
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
+ }
+
+ if (param instanceof ParametersWithIV)
+ {
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else if (params == null)
+ {
+ param = new KeyParameter(key.getEncoded());
+ }
+ else if (params instanceof IvParameterSpec)
+ {
+ if (ivLength != 0)
+ {
+ IvParameterSpec p = (IvParameterSpec)params;
+
+ if (p.getIV().length != ivLength && !isAEADModeName(modeName))
+ {
+ throw new InvalidAlgorithmParameterException("IV must be " + ivLength + " bytes long.");
+ }
+
+ if (key instanceof RepeatedSecretKeySpec)
+ {
+ param = new ParametersWithIV(null, p.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ else
+ {
+ param = new ParametersWithIV(new KeyParameter(key.getEncoded()), p.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else
+ {
+ if (modeName != null && modeName.equals("ECB"))
+ {
+ throw new InvalidAlgorithmParameterException("ECB mode does not use an IV");
+ }
+
+ param = new KeyParameter(key.getEncoded());
+ }
+ }
+ // BEGIN android-removed
+ // else if (params instanceof GOST28147ParameterSpec)
+ // {
+ // GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
+ //
+ // param = new ParametersWithSBox(
+ // new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
+ //
+ // if (gost28147Param.getIV() != null && ivLength != 0)
+ // {
+ // param = new ParametersWithIV(param, gost28147Param.getIV());
+ // ivParam = (ParametersWithIV)param;
+ // }
+ // }
+ // else if (params instanceof RC2ParameterSpec)
+ // {
+ // RC2ParameterSpec rc2Param = (RC2ParameterSpec)params;
+ //
+ // param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits());
+ //
+ // if (rc2Param.getIV() != null && ivLength != 0)
+ // {
+ // param = new ParametersWithIV(param, rc2Param.getIV());
+ // ivParam = (ParametersWithIV)param;
+ // }
+ // }
+ // else if (params instanceof RC5ParameterSpec)
+ // {
+ // RC5ParameterSpec rc5Param = (RC5ParameterSpec)params;
+ //
+ // param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds());
+ // if (baseEngine.getAlgorithmName().startsWith("RC5"))
+ // {
+ // if (baseEngine.getAlgorithmName().equals("RC5-32"))
+ // {
+ // if (rc5Param.getWordSize() != 32)
+ // {
+ // throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 32 not " + rc5Param.getWordSize() + ".");
+ // }
+ // }
+ // else if (baseEngine.getAlgorithmName().equals("RC5-64"))
+ // {
+ // if (rc5Param.getWordSize() != 64)
+ // {
+ // throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 64 not " + rc5Param.getWordSize() + ".");
+ // }
+ // }
+ // }
+ // else
+ // {
+ // throw new InvalidAlgorithmParameterException("RC5 parameters passed to a cipher that is not RC5.");
+ // }
+ // if ((rc5Param.getIV() != null) && (ivLength != 0))
+ // {
+ // param = new ParametersWithIV(param, rc5Param.getIV());
+ // ivParam = (ParametersWithIV)param;
+ // }
+ // }
+ // END android-removed
+ else
+ {
+ throw new InvalidAlgorithmParameterException("unknown parameter type.");
+ }
+
+ if ((ivLength != 0) && !(param instanceof ParametersWithIV))
+ {
+ SecureRandom ivRandom = random;
+
+ if (ivRandom == null)
+ {
+ ivRandom = new SecureRandom();
+ }
+
+ if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
+ {
+ byte[] iv = new byte[ivLength];
+
+ ivRandom.nextBytes(iv);
+ param = new ParametersWithIV(param, iv);
+ ivParam = (ParametersWithIV)param;
+ }
+ else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0)
+ {
+ throw new InvalidAlgorithmParameterException("no IV set when one expected");
+ }
+ }
+
+ if (random != null && padded)
+ {
+ param = new ParametersWithRandom(param, random);
+ }
+
+ try
+ {
+ switch (opmode)
+ {
+ case Cipher.ENCRYPT_MODE:
+ case Cipher.WRAP_MODE:
+ cipher.init(true, param);
+ break;
+ case Cipher.DECRYPT_MODE:
+ case Cipher.UNWRAP_MODE:
+ cipher.init(false, param);
+ break;
+ default:
+ throw new InvalidParameterException("unknown opmode " + opmode + " passed");
+ }
+ }
+ catch (Exception e)
+ {
+ throw new InvalidKeyException(e.getMessage());
+ }
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ AlgorithmParameterSpec paramSpec = null;
+
+ if (params != null)
+ {
+ for (int i = 0; i != availableSpecs.length; i++)
+ {
+ try
+ {
+ paramSpec = params.getParameterSpec(availableSpecs[i]);
+ break;
+ }
+ catch (Exception e)
+ {
+ // try again if possible
+ }
+ }
+
+ if (paramSpec == null)
+ {
+ throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
+ }
+ }
+
+ engineInit(opmode, key, paramSpec, random);
+
+ engineParams = params;
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ try
+ {
+ engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw new InvalidKeyException(e.getMessage());
+ }
+ }
+
+ protected byte[] engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ {
+ int length = cipher.getUpdateOutputSize(inputLen);
+
+ if (length > 0)
+ {
+ byte[] out = new byte[length];
+
+ int len = cipher.processBytes(input, inputOffset, inputLen, out, 0);
+
+ if (len == 0)
+ {
+ return null;
+ }
+ else if (len != out.length)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(out, 0, tmp, 0, len);
+
+ return tmp;
+ }
+
+ return out;
+ }
+
+ cipher.processBytes(input, inputOffset, inputLen, null, 0);
+
+ return null;
+ }
+
+ protected int engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ throws ShortBufferException
+ {
+ try
+ {
+ return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+ }
+ catch (DataLengthException e)
+ {
+ throw new ShortBufferException(e.getMessage());
+ }
+ }
+
+ protected byte[] engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException
+ {
+ int len = 0;
+ byte[] tmp = new byte[engineGetOutputSize(inputLen)];
+
+ if (inputLen != 0)
+ {
+ len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0);
+ }
+
+ try
+ {
+ len += cipher.doFinal(tmp, len);
+ }
+ catch (DataLengthException e)
+ {
+ throw new IllegalBlockSizeException(e.getMessage());
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+
+ if (len == tmp.length)
+ {
+ return tmp;
+ }
+
+ byte[] out = new byte[len];
+
+ System.arraycopy(tmp, 0, out, 0, len);
+
+ return out;
+ }
+
+ protected int engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
+ {
+ // BEGIN android-note
+ // added ShortBufferException to the throws statement
+ // END android-note
+ int len = 0;
+
+ // BEGIN android-added
+ int outputLen = cipher.getOutputSize(inputLen);
+
+ if (outputLen + outputOffset > output.length) {
+ throw new ShortBufferException("need at least " + outputLen + " bytes");
+ }
+ // BEGIN android-added
+
+ if (inputLen != 0)
+ {
+ len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+ }
+
+ try
+ {
+ return (len + cipher.doFinal(output, outputOffset + len));
+ }
+ catch (DataLengthException e)
+ {
+ throw new IllegalBlockSizeException(e.getMessage());
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+ }
+
+ private boolean isAEADModeName(
+ String modeName)
+ {
+ return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName);
+ }
+
+ /*
+ * The ciphers that inherit from us.
+ */
+
+ /**
+ * DES
+ */
+ static public class DES
+ extends JCEBlockCipher
+ {
+ public DES()
+ {
+ super(new DESEngine());
+ }
+ }
+
+ // BEGIN android-removed
+ // /**
+ // * DESCBC
+ // */
+ // static public class DESCBC
+ // extends JCEBlockCipher
+ // {
+ // public DESCBC()
+ // {
+ // super(new CBCBlockCipher(new DESEngine()), 64);
+ // }
+ // }
+ //
+ // /**
+ // * GOST28147
+ // */
+ // static public class GOST28147
+ // extends JCEBlockCipher
+ // {
+ // public GOST28147()
+ // {
+ // super(new GOST28147Engine());
+ // }
+ // }
+ //
+ // static public class GOST28147cbc
+ // extends JCEBlockCipher
+ // {
+ // public GOST28147cbc()
+ // {
+ // super(new CBCBlockCipher(new GOST28147Engine()), 64);
+ // }
+ // }
+ //
+ // /**
+ // * RC2
+ // */
+ // static public class RC2
+ // extends JCEBlockCipher
+ // {
+ // public RC2()
+ // {
+ // super(new RC2Engine());
+ // }
+ // }
+ //
+ // /**
+ // * RC2CBC
+ // */
+ // static public class RC2CBC
+ // extends JCEBlockCipher
+ // {
+ // public RC2CBC()
+ // {
+ // super(new CBCBlockCipher(new RC2Engine()), 64);
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * PBEWithMD5AndDES
+ */
+ static public class PBEWithMD5AndDES
+ extends JCEBlockCipher
+ {
+ public PBEWithMD5AndDES()
+ {
+ super(new CBCBlockCipher(new DESEngine()));
+ }
+ }
+
+ /**
+ * PBEWithMD5AndRC2
+ */
+ static public class PBEWithMD5AndRC2
+ extends JCEBlockCipher
+ {
+ public PBEWithMD5AndRC2()
+ {
+ super(new CBCBlockCipher(new RC2Engine()));
+ }
+ }
+
+ /**
+ * PBEWithSHA1AndDES
+ */
+ static public class PBEWithSHA1AndDES
+ extends JCEBlockCipher
+ {
+ public PBEWithSHA1AndDES()
+ {
+ super(new CBCBlockCipher(new DESEngine()));
+ }
+ }
+
+ /**
+ * PBEWithSHA1AndRC2
+ */
+ static public class PBEWithSHA1AndRC2
+ extends JCEBlockCipher
+ {
+ public PBEWithSHA1AndRC2()
+ {
+ super(new CBCBlockCipher(new RC2Engine()));
+ }
+ }
+
+
+
+ /**
+ * PBEWithSHAAnd128BitRC2-CBC
+ */
+ static public class PBEWithSHAAnd128BitRC2
+ extends JCEBlockCipher
+ {
+ public PBEWithSHAAnd128BitRC2()
+ {
+ super(new CBCBlockCipher(new RC2Engine()));
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd40BitRC2-CBC
+ */
+ static public class PBEWithSHAAnd40BitRC2
+ extends JCEBlockCipher
+ {
+ public PBEWithSHAAnd40BitRC2()
+ {
+ super(new CBCBlockCipher(new RC2Engine()));
+ }
+ }
+
+ /**
+ * PBEWithSHAAndTwofish-CBC
+ */
+ static public class PBEWithSHAAndTwofish
+ extends JCEBlockCipher
+ {
+ public PBEWithSHAAndTwofish()
+ {
+ super(new CBCBlockCipher(new TwofishEngine()));
+ }
+ }
+
+ /**
+ * PBEWithAES-CBC
+ */
+ static public class PBEWithAESCBC
+ extends JCEBlockCipher
+ {
+ public PBEWithAESCBC()
+ {
+ super(new CBCBlockCipher(new AESFastEngine()));
+ }
+ }
+
+ static private interface GenericBlockCipher
+ {
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ public boolean wrapOnNoPadding();
+
+ public String getAlgorithmName();
+
+ public BlockCipher getUnderlyingCipher();
+
+ public int getOutputSize(int len);
+
+ public int getUpdateOutputSize(int len);
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException;
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException;
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException;
+ }
+
+ private static class BufferedGenericBlockCipher
+ implements GenericBlockCipher
+ {
+ private BufferedBlockCipher cipher;
+
+ BufferedGenericBlockCipher(BufferedBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ BufferedGenericBlockCipher(BlockCipher cipher)
+ {
+ this.cipher = new PaddedBufferedBlockCipher(cipher);
+ }
+
+ BufferedGenericBlockCipher(BlockCipher cipher, BlockCipherPadding padding)
+ {
+ this.cipher = new PaddedBufferedBlockCipher(cipher, padding);
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ cipher.init(forEncryption, params);
+ }
+
+ public boolean wrapOnNoPadding()
+ {
+ return !(cipher instanceof CTSBlockCipher);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName();
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher.getUnderlyingCipher();
+ }
+
+ public int getOutputSize(int len)
+ {
+ return cipher.getOutputSize(len);
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ return cipher.getUpdateOutputSize(len);
+ }
+
+ public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processByte(in, out, outOff);
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processBytes(in, inOff, len, out, outOff);
+ }
+
+ public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
+ {
+ return cipher.doFinal(out, outOff);
+ }
+ }
+
+ private static class AEADGenericBlockCipher
+ implements GenericBlockCipher
+ {
+ private AEADBlockCipher cipher;
+
+ AEADGenericBlockCipher(AEADBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ cipher.init(forEncryption, params);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName();
+ }
+
+ public boolean wrapOnNoPadding()
+ {
+ return false;
+ }
+
+ public BlockCipher getUnderlyingCipher()
+ {
+ return cipher.getUnderlyingCipher();
+ }
+
+ public int getOutputSize(int len)
+ {
+ return cipher.getOutputSize(len);
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ return cipher.getUpdateOutputSize(len);
+ }
+
+ public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processByte(in, out, outOff);
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processBytes(in, inOff, len, out, outOff);
+ }
+
+ public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
+ {
+ return cipher.doFinal(out, outOff);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
new file mode 100644
index 0000000..46295c5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.util.Enumeration;
+
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPrivateKeySpec;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.DHParameter;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.DHDomainParameters;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+
+public class JCEDHPrivateKey
+ implements DHPrivateKey, PKCS12BagAttributeCarrier
+{
+ static final long serialVersionUID = 311058815616901812L;
+
+ BigInteger x;
+
+ private DHParameterSpec dhSpec;
+ private PrivateKeyInfo info;
+
+ private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ protected JCEDHPrivateKey()
+ {
+ }
+
+ JCEDHPrivateKey(
+ DHPrivateKey key)
+ {
+ this.x = key.getX();
+ this.dhSpec = key.getParams();
+ }
+
+ JCEDHPrivateKey(
+ DHPrivateKeySpec spec)
+ {
+ this.x = spec.getX();
+ this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
+ }
+
+ JCEDHPrivateKey(
+ PrivateKeyInfo info)
+ throws IOException
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
+ DERInteger derX = DERInteger.getInstance(info.parsePrivateKey());
+ DERObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
+
+ this.info = info;
+ this.x = derX.getValue();
+
+ if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement))
+ {
+ DHParameter params = DHParameter.getInstance(seq);
+
+ if (params.getL() != null)
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
+ }
+ else
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
+ }
+ }
+ else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ DHDomainParameters params = DHDomainParameters.getInstance(seq);
+
+ this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown algorithm type: " + id);
+ }
+ }
+
+ JCEDHPrivateKey(
+ DHPrivateKeyParameters params)
+ {
+ this.x = params.getX();
+ this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
+ }
+
+ public String getAlgorithm()
+ {
+ return "DH";
+ }
+
+ /**
+ * return the encoding format we produce in getEncoded().
+ *
+ * @return the string "PKCS#8"
+ */
+ public String getFormat()
+ {
+ return "PKCS#8";
+ }
+
+ /**
+ * Return a PKCS8 representation of the key. The sequence returned
+ * represents a full PrivateKeyInfo object.
+ *
+ * @return a PKCS8 representation of the key.
+ */
+ public byte[] getEncoded()
+ {
+ try
+ {
+ if (info != null)
+ {
+ return info.getEncoded(ASN1Encoding.DER);
+ }
+
+ PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL())), new DERInteger(getX()));
+
+ return info.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public DHParameterSpec getParams()
+ {
+ return dhSpec;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ x = (BigInteger)in.readObject();
+
+ this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(this.getX());
+ out.writeObject(dhSpec.getP());
+ out.writeObject(dhSpec.getG());
+ out.writeInt(dhSpec.getL());
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ DERObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
new file mode 100644
index 0000000..6ff1e08
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java
@@ -0,0 +1,178 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.DHParameter;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.DHDomainParameters;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+
+public class JCEDHPublicKey
+ implements DHPublicKey
+{
+ static final long serialVersionUID = -216691575254424324L;
+
+ private BigInteger y;
+ private DHParameterSpec dhSpec;
+ private SubjectPublicKeyInfo info;
+
+ JCEDHPublicKey(
+ DHPublicKeySpec spec)
+ {
+ this.y = spec.getY();
+ this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
+ }
+
+ JCEDHPublicKey(
+ DHPublicKey key)
+ {
+ this.y = key.getY();
+ this.dhSpec = key.getParams();
+ }
+
+ JCEDHPublicKey(
+ DHPublicKeyParameters params)
+ {
+ this.y = params.getY();
+ this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
+ }
+
+ JCEDHPublicKey(
+ BigInteger y,
+ DHParameterSpec dhSpec)
+ {
+ this.y = y;
+ this.dhSpec = dhSpec;
+ }
+
+ JCEDHPublicKey(
+ SubjectPublicKeyInfo info)
+ {
+ this.info = info;
+
+ DERInteger derY;
+ try
+ {
+ derY = (DERInteger)info.parsePublicKey();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("invalid info structure in DH public key");
+ }
+
+ this.y = derY.getValue();
+
+ ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
+ DERObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
+
+ // we need the PKCS check to handle older keys marked with the X9 oid.
+ if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
+ {
+ DHParameter params = DHParameter.getInstance(seq);
+
+ if (params.getL() != null)
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
+ }
+ else
+ {
+ this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
+ }
+ }
+ else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
+ {
+ DHDomainParameters params = DHDomainParameters.getInstance(seq);
+
+ this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown algorithm type: " + id);
+ }
+ }
+
+ public String getAlgorithm()
+ {
+ return "DH";
+ }
+
+ public String getFormat()
+ {
+ return "X.509";
+ }
+
+ public byte[] getEncoded()
+ {
+ if (info != null)
+ {
+ return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
+ }
+
+ return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL())), new DERInteger(y));
+ }
+
+ public DHParameterSpec getParams()
+ {
+ return dhSpec;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ private boolean isPKCSParam(ASN1Sequence seq)
+ {
+ if (seq.size() == 2)
+ {
+ return true;
+ }
+
+ if (seq.size() > 3)
+ {
+ return false;
+ }
+
+ DERInteger l = DERInteger.getInstance(seq.getObjectAt(2));
+ DERInteger p = DERInteger.getInstance(seq.getObjectAt(0));
+
+ if (l.getValue().compareTo(BigInteger.valueOf(p.getValue().bitLength())) > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ this.y = (BigInteger)in.readObject();
+ this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(this.getY());
+ out.writeObject(dhSpec.getP());
+ out.writeObject(dhSpec.getG());
+ out.writeInt(dhSpec.getL());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
new file mode 100644
index 0000000..1ff5b80
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
@@ -0,0 +1,484 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.EllipticCurve;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+// END android-removed
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.ECUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.interfaces.ECPointEncoder;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.math.ec.ECCurve;
+
+public class JCEECPrivateKey
+ implements ECPrivateKey, org.bouncycastle.jce.interfaces.ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder
+{
+ private String algorithm = "EC";
+ private BigInteger d;
+ private ECParameterSpec ecSpec;
+ private boolean withCompression;
+
+ private DERBitString publicKey;
+
+ private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ protected JCEECPrivateKey()
+ {
+ }
+
+ public JCEECPrivateKey(
+ ECPrivateKey key)
+ {
+ this.d = key.getS();
+ this.algorithm = key.getAlgorithm();
+ this.ecSpec = key.getParams();
+ }
+
+ public JCEECPrivateKey(
+ String algorithm,
+ org.bouncycastle.jce.spec.ECPrivateKeySpec spec)
+ {
+ this.algorithm = algorithm;
+ this.d = spec.getD();
+
+ if (spec.getParams() != null) // can be null if implicitlyCA
+ {
+ ECCurve curve = spec.getParams().getCurve();
+ EllipticCurve ellipticCurve;
+
+ ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed());
+
+ this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams());
+ }
+ else
+ {
+ this.ecSpec = null;
+ }
+ }
+
+
+ public JCEECPrivateKey(
+ String algorithm,
+ ECPrivateKeySpec spec)
+ {
+ this.algorithm = algorithm;
+ this.d = spec.getS();
+ this.ecSpec = spec.getParams();
+ }
+
+ public JCEECPrivateKey(
+ String algorithm,
+ JCEECPrivateKey key)
+ {
+ this.algorithm = algorithm;
+ this.d = key.d;
+ this.ecSpec = key.ecSpec;
+ this.withCompression = key.withCompression;
+ this.attrCarrier = key.attrCarrier;
+ this.publicKey = key.publicKey;
+ }
+
+ public JCEECPrivateKey(
+ String algorithm,
+ ECPrivateKeyParameters params,
+ JCEECPublicKey pubKey,
+ ECParameterSpec spec)
+ {
+ ECDomainParameters dp = params.getParameters();
+
+ this.algorithm = algorithm;
+ this.d = params.getD();
+
+ if (spec == null)
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+ this.ecSpec = new ECParameterSpec(
+ ellipticCurve,
+ new ECPoint(
+ dp.getG().getX().toBigInteger(),
+ dp.getG().getY().toBigInteger()),
+ dp.getN(),
+ dp.getH().intValue());
+ }
+ else
+ {
+ this.ecSpec = spec;
+ }
+
+ publicKey = getPublicKeyDetails(pubKey);
+ }
+
+ public JCEECPrivateKey(
+ String algorithm,
+ ECPrivateKeyParameters params,
+ JCEECPublicKey pubKey,
+ org.bouncycastle.jce.spec.ECParameterSpec spec)
+ {
+ ECDomainParameters dp = params.getParameters();
+
+ this.algorithm = algorithm;
+ this.d = params.getD();
+
+ if (spec == null)
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+ this.ecSpec = new ECParameterSpec(
+ ellipticCurve,
+ new ECPoint(
+ dp.getG().getX().toBigInteger(),
+ dp.getG().getY().toBigInteger()),
+ dp.getN(),
+ dp.getH().intValue());
+ }
+ else
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
+
+ this.ecSpec = new ECParameterSpec(
+ ellipticCurve,
+ new ECPoint(
+ spec.getG().getX().toBigInteger(),
+ spec.getG().getY().toBigInteger()),
+ spec.getN(),
+ spec.getH().intValue());
+ }
+
+ publicKey = getPublicKeyDetails(pubKey);
+ }
+
+ public JCEECPrivateKey(
+ String algorithm,
+ ECPrivateKeyParameters params)
+ {
+ this.algorithm = algorithm;
+ this.d = params.getD();
+ this.ecSpec = null;
+ }
+
+ JCEECPrivateKey(
+ PrivateKeyInfo info)
+ throws IOException
+ {
+ populateFromPrivKeyInfo(info);
+ }
+
+ private void populateFromPrivKeyInfo(PrivateKeyInfo info)
+ throws IOException
+ {
+ X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters());
+
+ if (params.isNamedCurve())
+ {
+ ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
+ X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
+
+ // BEGIN android-removed
+ // if (ecP == null) // GOST Curve
+ // {
+ // ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
+ // EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
+ //
+ // ecSpec = new ECNamedCurveSpec(
+ // ECGOST3410NamedCurves.getName(oid),
+ // ellipticCurve,
+ // new ECPoint(
+ // gParam.getG().getX().toBigInteger(),
+ // gParam.getG().getY().toBigInteger()),
+ // gParam.getN(),
+ // gParam.getH());
+ // }
+ // else
+ // END android-removed
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+
+ ecSpec = new ECNamedCurveSpec(
+ ECUtil.getCurveName(oid),
+ ellipticCurve,
+ new ECPoint(
+ ecP.getG().getX().toBigInteger(),
+ ecP.getG().getY().toBigInteger()),
+ ecP.getN(),
+ ecP.getH());
+ }
+ }
+ else if (params.isImplicitlyCA())
+ {
+ ecSpec = null;
+ }
+ else
+ {
+ X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+
+ this.ecSpec = new ECParameterSpec(
+ ellipticCurve,
+ new ECPoint(
+ ecP.getG().getX().toBigInteger(),
+ ecP.getG().getY().toBigInteger()),
+ ecP.getN(),
+ ecP.getH().intValue());
+ }
+
+ ASN1Encodable privKey = info.parsePrivateKey();
+ if (privKey instanceof DERInteger)
+ {
+ DERInteger derD = DERInteger.getInstance(privKey);
+
+ this.d = derD.getValue();
+ }
+ else
+ {
+ ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)privKey);
+
+ this.d = ec.getKey();
+ this.publicKey = ec.getPublicKey();
+ }
+ }
+
+ public String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ /**
+ * return the encoding format we produce in getEncoded().
+ *
+ * @return the string "PKCS#8"
+ */
+ public String getFormat()
+ {
+ return "PKCS#8";
+ }
+
+ /**
+ * Return a PKCS8 representation of the key. The sequence returned
+ * represents a full PrivateKeyInfo object.
+ *
+ * @return a PKCS8 representation of the key.
+ */
+ public byte[] getEncoded()
+ {
+ X962Parameters params;
+
+ if (ecSpec instanceof ECNamedCurveSpec)
+ {
+ DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
+ if (curveOid == null) // guess it's the OID
+ {
+ curveOid = new DERObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+ }
+ params = new X962Parameters(curveOid);
+ }
+ else if (ecSpec == null)
+ {
+ params = new X962Parameters(DERNull.INSTANCE);
+ }
+ else
+ {
+ ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
+
+ X9ECParameters ecP = new X9ECParameters(
+ curve,
+ EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
+ ecSpec.getOrder(),
+ BigInteger.valueOf(ecSpec.getCofactor()),
+ ecSpec.getCurve().getSeed());
+
+ params = new X962Parameters(ecP);
+ }
+
+ PrivateKeyInfo info;
+ ECPrivateKeyStructure keyStructure;
+
+ if (publicKey != null)
+ {
+ keyStructure = new ECPrivateKeyStructure(this.getS(), publicKey, params);
+ }
+ else
+ {
+ keyStructure = new ECPrivateKeyStructure(this.getS(), params);
+ }
+
+ try
+ {
+ // BEGIN android-removed
+ // if (algorithm.equals("ECGOST3410"))
+ // {
+ // info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive());
+ // }
+ // else
+ // END android-removed
+ {
+
+ info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive());
+ }
+
+ return info.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public ECParameterSpec getParams()
+ {
+ return ecSpec;
+ }
+
+ public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
+ {
+ if (ecSpec == null)
+ {
+ return null;
+ }
+
+ return EC5Util.convertSpec(ecSpec, withCompression);
+ }
+
+ org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
+ {
+ if (ecSpec != null)
+ {
+ return EC5Util.convertSpec(ecSpec, withCompression);
+ }
+
+ return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+ }
+
+ public BigInteger getS()
+ {
+ return d;
+ }
+
+ public BigInteger getD()
+ {
+ return d;
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ DERObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+
+ public void setPointFormat(String style)
+ {
+ withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof JCEECPrivateKey))
+ {
+ return false;
+ }
+
+ JCEECPrivateKey other = (JCEECPrivateKey)o;
+
+ return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec()));
+ }
+
+ public int hashCode()
+ {
+ return getD().hashCode() ^ engineGetSpec().hashCode();
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append("EC Private Key").append(nl);
+ buf.append(" S: ").append(this.d.toString(16)).append(nl);
+
+ return buf.toString();
+
+ }
+
+ private DERBitString getPublicKeyDetails(JCEECPublicKey pub)
+ {
+ try
+ {
+ SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(pub.getEncoded()));
+
+ return info.getPublicKeyData();
+ }
+ catch (IOException e)
+ { // should never happen
+ return null;
+ }
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ byte[] enc = (byte[])in.readObject();
+
+ populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
+
+ this.algorithm = (String)in.readObject();
+ this.withCompression = in.readBoolean();
+ this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ attrCarrier.readObject(in);
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(this.getEncoded());
+ out.writeObject(algorithm);
+ out.writeBoolean(withCompression);
+
+ attrCarrier.writeObject(out);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
new file mode 100644
index 0000000..15a2996
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
@@ -0,0 +1,532 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+// BEGIN android-removed
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+// import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
+// END android-removed
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.asn1.x9.X9IntegerConverter;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.ECUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+// BEGIN android-removed
+// import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
+// END android-removed
+import org.bouncycastle.jce.interfaces.ECPointEncoder;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
+// END android-removed
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.math.ec.ECCurve;
+
+public class JCEECPublicKey
+ implements ECPublicKey, org.bouncycastle.jce.interfaces.ECPublicKey, ECPointEncoder
+{
+ private String algorithm = "EC";
+ private org.bouncycastle.math.ec.ECPoint q;
+ private ECParameterSpec ecSpec;
+ private boolean withCompression;
+ // BEGIN android-removed
+ // private GOST3410PublicKeyAlgParameters gostParams;
+ // END android-removed
+
+ public JCEECPublicKey(
+ String algorithm,
+ JCEECPublicKey key)
+ {
+ this.algorithm = algorithm;
+ this.q = key.q;
+ this.ecSpec = key.ecSpec;
+ this.withCompression = key.withCompression;
+ // BEGIN android-removed
+ // this.gostParams = key.gostParams;
+ // END android-removed
+ }
+
+ public JCEECPublicKey(
+ String algorithm,
+ ECPublicKeySpec spec)
+ {
+ this.algorithm = algorithm;
+ this.ecSpec = spec.getParams();
+ this.q = EC5Util.convertPoint(ecSpec, spec.getW(), false);
+ }
+
+ public JCEECPublicKey(
+ String algorithm,
+ org.bouncycastle.jce.spec.ECPublicKeySpec spec)
+ {
+ this.algorithm = algorithm;
+ this.q = spec.getQ();
+
+ if (spec.getParams() != null) // can be null if implictlyCa
+ {
+ ECCurve curve = spec.getParams().getCurve();
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getParams().getSeed());
+
+ this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec.getParams());
+ }
+ else
+ {
+ if (q.getCurve() == null)
+ {
+ org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+
+ q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false);
+ }
+ this.ecSpec = null;
+ }
+ }
+
+ public JCEECPublicKey(
+ String algorithm,
+ ECPublicKeyParameters params,
+ ECParameterSpec spec)
+ {
+ ECDomainParameters dp = params.getParameters();
+
+ this.algorithm = algorithm;
+ this.q = params.getQ();
+
+ if (spec == null)
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+ this.ecSpec = createSpec(ellipticCurve, dp);
+ }
+ else
+ {
+ this.ecSpec = spec;
+ }
+ }
+
+ public JCEECPublicKey(
+ String algorithm,
+ ECPublicKeyParameters params,
+ org.bouncycastle.jce.spec.ECParameterSpec spec)
+ {
+ ECDomainParameters dp = params.getParameters();
+
+ this.algorithm = algorithm;
+ this.q = params.getQ();
+
+ if (spec == null)
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
+
+ this.ecSpec = createSpec(ellipticCurve, dp);
+ }
+ else
+ {
+ EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
+
+ this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec);
+ }
+ }
+
+ /*
+ * called for implicitCA
+ */
+ public JCEECPublicKey(
+ String algorithm,
+ ECPublicKeyParameters params)
+ {
+ this.algorithm = algorithm;
+ this.q = params.getQ();
+ this.ecSpec = null;
+ }
+
+ private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp)
+ {
+ return new ECParameterSpec(
+ ellipticCurve,
+ new ECPoint(
+ dp.getG().getX().toBigInteger(),
+ dp.getG().getY().toBigInteger()),
+ dp.getN(),
+ dp.getH().intValue());
+ }
+
+ public JCEECPublicKey(
+ ECPublicKey key)
+ {
+ this.algorithm = key.getAlgorithm();
+ this.ecSpec = key.getParams();
+ this.q = EC5Util.convertPoint(this.ecSpec, key.getW(), false);
+ }
+
+ JCEECPublicKey(
+ SubjectPublicKeyInfo info)
+ {
+ populateFromPubKeyInfo(info);
+ }
+
+ private void populateFromPubKeyInfo(SubjectPublicKeyInfo info)
+ {
+ // BEGIN android-removed
+ // if (info.getAlgorithmId().getObjectId().equals(CryptoProObjectIdentifiers.gostR3410_2001))
+ // {
+ // DERBitString bits = info.getPublicKeyData();
+ // ASN1OctetString key;
+ // this.algorithm = "ECGOST3410";
+ //
+ // try
+ // {
+ // key = (ASN1OctetString) ASN1Primitive.fromByteArray(bits.getBytes());
+ // }
+ // catch (IOException ex)
+ // {
+ // throw new IllegalArgumentException("error recovering public key");
+ // }
+ //
+ // byte[] keyEnc = key.getOctets();
+ // byte[] x = new byte[32];
+ // byte[] y = new byte[32];
+ //
+ // for (int i = 0; i != x.length; i++)
+ // {
+ // x[i] = keyEnc[32 - 1 - i];
+ // }
+ //
+ // for (int i = 0; i != y.length; i++)
+ // {
+ // y[i] = keyEnc[64 - 1 - i];
+ // }
+ //
+ // gostParams = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithmId().getParameters());
+ //
+ // ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+ //
+ // ECCurve curve = spec.getCurve();
+ // EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
+ //
+ // this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
+ //
+ // ecSpec = new ECNamedCurveSpec(
+ // ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+ // ellipticCurve,
+ // new ECPoint(
+ // spec.getG().getX().toBigInteger(),
+ // spec.getG().getY().toBigInteger()),
+ // spec.getN(), spec.getH());
+ //
+ // }
+ // else
+ // END android-removed
+ {
+ X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithmId().getParameters());
+ ECCurve curve;
+ EllipticCurve ellipticCurve;
+
+ if (params.isNamedCurve())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
+ X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
+
+ curve = ecP.getCurve();
+ ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
+
+ ecSpec = new ECNamedCurveSpec(
+ ECUtil.getCurveName(oid),
+ ellipticCurve,
+ new ECPoint(
+ ecP.getG().getX().toBigInteger(),
+ ecP.getG().getY().toBigInteger()),
+ ecP.getN(),
+ ecP.getH());
+ }
+ else if (params.isImplicitlyCA())
+ {
+ ecSpec = null;
+ curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve();
+ }
+ else
+ {
+ X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+
+ curve = ecP.getCurve();
+ ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
+
+ this.ecSpec = new ECParameterSpec(
+ ellipticCurve,
+ new ECPoint(
+ ecP.getG().getX().toBigInteger(),
+ ecP.getG().getY().toBigInteger()),
+ ecP.getN(),
+ ecP.getH().intValue());
+ }
+
+ DERBitString bits = info.getPublicKeyData();
+ byte[] data = bits.getBytes();
+ ASN1OctetString key = new DEROctetString(data);
+
+ //
+ // extra octet string - one of our old certs...
+ //
+ if (data[0] == 0x04 && data[1] == data.length - 2
+ && (data[2] == 0x02 || data[2] == 0x03))
+ {
+ int qLength = new X9IntegerConverter().getByteLength(curve);
+
+ if (qLength >= data.length - 3)
+ {
+ try
+ {
+ key = (ASN1OctetString) ASN1Primitive.fromByteArray(data);
+ }
+ catch (IOException ex)
+ {
+ throw new IllegalArgumentException("error recovering public key");
+ }
+ }
+ }
+ X9ECPoint derQ = new X9ECPoint(curve, key);
+
+ this.q = derQ.getPoint();
+ }
+ }
+
+ public String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public String getFormat()
+ {
+ return "X.509";
+ }
+
+ public byte[] getEncoded()
+ {
+ ASN1Encodable params;
+ SubjectPublicKeyInfo info;
+
+ // BEGIN android-removed
+ // if (algorithm.equals("ECGOST3410"))
+ // {
+ // if (gostParams != null)
+ // {
+ // params = gostParams;
+ // }
+ // else
+ // {
+ // if (ecSpec instanceof ECNamedCurveSpec)
+ // {
+ // params = new GOST3410PublicKeyAlgParameters(
+ // ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+ // CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet);
+ // }
+ // else
+ // { // strictly speaking this may not be applicable...
+ // ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
+ //
+ // X9ECParameters ecP = new X9ECParameters(
+ // curve,
+ // EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
+ // ecSpec.getOrder(),
+ // BigInteger.valueOf(ecSpec.getCofactor()),
+ // ecSpec.getCurve().getSeed());
+ //
+ // params = new X962Parameters(ecP);
+ // }
+ // }
+ //
+ // BigInteger bX = this.q.getX().toBigInteger();
+ // BigInteger bY = this.q.getY().toBigInteger();
+ // byte[] encKey = new byte[64];
+ //
+ // extractBytes(encKey, 0, bX);
+ // extractBytes(encKey, 32, bY);
+ //
+ // info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey));
+ // }
+ // else
+ // END android-removed
+ {
+ if (ecSpec instanceof ECNamedCurveSpec)
+ {
+ ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
+ if (curveOid == null)
+ {
+ curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+ }
+ params = new X962Parameters(curveOid);
+ }
+ else if (ecSpec == null)
+ {
+ params = new X962Parameters(DERNull.INSTANCE);
+ }
+ else
+ {
+ ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
+
+ X9ECParameters ecP = new X9ECParameters(
+ curve,
+ EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
+ ecSpec.getOrder(),
+ BigInteger.valueOf(ecSpec.getCofactor()),
+ ecSpec.getCurve().getSeed());
+
+ params = new X962Parameters(ecP);
+ }
+
+ ECCurve curve = this.engineGetQ().getCurve();
+ ASN1OctetString p = (ASN1OctetString)
+ new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
+
+ info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
+ }
+
+ return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
+ }
+
+ private void extractBytes(byte[] encKey, int offSet, BigInteger bI)
+ {
+ byte[] val = bI.toByteArray();
+ if (val.length < 32)
+ {
+ byte[] tmp = new byte[32];
+ System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
+ val = tmp;
+ }
+
+ for (int i = 0; i != 32; i++)
+ {
+ encKey[offSet + i] = val[val.length - 1 - i];
+ }
+ }
+
+ public ECParameterSpec getParams()
+ {
+ return ecSpec;
+ }
+
+ public org.bouncycastle.jce.spec.ECParameterSpec getParameters()
+ {
+ if (ecSpec == null) // implictlyCA
+ {
+ return null;
+ }
+
+ return EC5Util.convertSpec(ecSpec, withCompression);
+ }
+
+ public ECPoint getW()
+ {
+ return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
+ }
+
+ public org.bouncycastle.math.ec.ECPoint getQ()
+ {
+ if (ecSpec == null)
+ {
+ if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
+ {
+ return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
+ }
+ else
+ {
+ return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
+ }
+ }
+
+ return q;
+ }
+
+ public org.bouncycastle.math.ec.ECPoint engineGetQ()
+ {
+ return q;
+ }
+
+ org.bouncycastle.jce.spec.ECParameterSpec engineGetSpec()
+ {
+ if (ecSpec != null)
+ {
+ return EC5Util.convertSpec(ecSpec, withCompression);
+ }
+
+ return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append("EC Public Key").append(nl);
+ buf.append(" X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
+ buf.append(" Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
+
+ return buf.toString();
+
+ }
+
+ public void setPointFormat(String style)
+ {
+ withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof JCEECPublicKey))
+ {
+ return false;
+ }
+
+ JCEECPublicKey other = (JCEECPublicKey)o;
+
+ return engineGetQ().equals(other.engineGetQ()) && (engineGetSpec().equals(other.engineGetSpec()));
+ }
+
+ public int hashCode()
+ {
+ return engineGetQ().hashCode() ^ engineGetSpec().hashCode();
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ byte[] enc = (byte[])in.readObject();
+
+ populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc)));
+
+ this.algorithm = (String)in.readObject();
+ this.withCompression = in.readBoolean();
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(this.getEncoded());
+ out.writeObject(algorithm);
+ out.writeBoolean(withCompression);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEMac.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEMac.java
new file mode 100644
index 0000000..6a3df68
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEMac.java
@@ -0,0 +1,455 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.MacSpi;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.MD2Digest;
+// import org.bouncycastle.crypto.digests.MD4Digest;
+// import org.bouncycastle.crypto.digests.MD5Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+// 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.crypto.digests.TigerDigest;
+// END android-removed
+// BEGIN android-added
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
+// END android-added
+import org.bouncycastle.crypto.engines.DESEngine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.RC2Engine;
+// END android-removed
+import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
+// END android-removed
+import org.bouncycastle.crypto.macs.HMac;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
+// import org.bouncycastle.crypto.macs.OldHMac;
+// END android-removed
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
+
+public class JCEMac
+ extends MacSpi implements PBE
+{
+ private Mac macEngine;
+
+ private int pbeType = PKCS12;
+ private int pbeHash = SHA1;
+ private int keySize = 160;
+
+ protected JCEMac(
+ Mac macEngine)
+ {
+ this.macEngine = macEngine;
+ }
+
+ protected JCEMac(
+ Mac macEngine,
+ int pbeType,
+ int pbeHash,
+ int keySize)
+ {
+ this.macEngine = macEngine;
+ this.pbeType = pbeType;
+ this.pbeHash = pbeHash;
+ this.keySize = keySize;
+ }
+
+ protected void engineInit(
+ Key key,
+ AlgorithmParameterSpec params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ CipherParameters param;
+
+ if (key == null)
+ {
+ throw new InvalidKeyException("key is null");
+ }
+
+ if (key instanceof BCPBEKey)
+ {
+ BCPBEKey k = (BCPBEKey)key;
+
+ if (k.getParam() != null)
+ {
+ param = k.getParam();
+ }
+ else if (params instanceof PBEParameterSpec)
+ {
+ param = PBE.Util.makePBEMacParameters(k, params);
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
+ }
+ }
+ else if (params instanceof IvParameterSpec)
+ {
+ param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
+ }
+ else if (params == null)
+ {
+ param = new KeyParameter(key.getEncoded());
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("unknown parameter type.");
+ }
+
+ macEngine.init(param);
+ }
+
+ protected int engineGetMacLength()
+ {
+ return macEngine.getMacSize();
+ }
+
+ protected void engineReset()
+ {
+ macEngine.reset();
+ }
+
+ protected void engineUpdate(
+ byte input)
+ {
+ macEngine.update(input);
+ }
+
+ protected void engineUpdate(
+ byte[] input,
+ int offset,
+ int len)
+ {
+ macEngine.update(input, offset, len);
+ }
+
+ protected byte[] engineDoFinal()
+ {
+ byte[] out = new byte[engineGetMacLength()];
+
+ macEngine.doFinal(out, 0);
+
+ return out;
+ }
+
+ /**
+ * the classes that extend directly off us.
+ */
+
+ // BEGIN android-removed
+ // /**
+ // * DES
+ // */
+ // public static class DES
+ // extends JCEMac
+ // {
+ // public DES()
+ // {
+ // super(new CBCBlockCipherMac(new DESEngine()));
+ // }
+ // }
+ //
+ // /**
+ // * DES 64 bit MAC
+ // */
+ // public static class DES64
+ // extends JCEMac
+ // {
+ // public DES64()
+ // {
+ // super(new CBCBlockCipherMac(new DESEngine(), 64));
+ // }
+ // }
+ //
+ // /**
+ // * RC2
+ // */
+ // public static class RC2
+ // extends JCEMac
+ // {
+ // public RC2()
+ // {
+ // super(new CBCBlockCipherMac(new RC2Engine()));
+ // }
+ // }
+ //
+ //
+ //
+ //
+ // /**
+ // * DES
+ // */
+ // public static class DESCFB8
+ // extends JCEMac
+ // {
+ // public DESCFB8()
+ // {
+ // super(new CFBBlockCipherMac(new DESEngine()));
+ // }
+ // }
+ //
+ // /**
+ // * RC2CFB8
+ // */
+ //
+ //
+ // /**
+ // * DES9797Alg3with7816-4Padding
+ // */
+ // public static class DES9797Alg3with7816d4
+ // extends JCEMac
+ // {
+ // public DES9797Alg3with7816d4()
+ // {
+ // super(new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding()));
+ // }
+ // }
+ //
+ // /**
+ // * DES9797Alg3
+ // */
+ // public static class DES9797Alg3
+ // extends JCEMac
+ // {
+ // public DES9797Alg3()
+ // {
+ // super(new ISO9797Alg3Mac(new DESEngine()));
+ // }
+ // }
+ //
+ // /**
+ // * MD2 HMac
+ // */
+ // public static class MD2
+ // extends JCEMac
+ // {
+ // public MD2()
+ // {
+ // super(new HMac(new MD2Digest()));
+ // }
+ // }
+ //
+ // /**
+ // * MD4 HMac
+ // */
+ // public static class MD4
+ // extends JCEMac
+ // {
+ // public MD4()
+ // {
+ // super(new HMac(new MD4Digest()));
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * MD5 HMac
+ */
+ public static class MD5
+ extends JCEMac
+ {
+ public MD5()
+ {
+ // BEGIN android-changed
+ super(new HMac(AndroidDigestFactory.getMD5()));
+ // END android-changed
+ }
+ }
+
+ /**
+ * SHA1 HMac
+ */
+ public static class SHA1
+ extends JCEMac
+ {
+ public SHA1()
+ {
+ // BEGIN android-changed
+ super(new HMac(AndroidDigestFactory.getSHA1()));
+ // END android-changed
+ }
+ }
+
+ // BEGIN android-removed
+ // /**
+ // * SHA-224 HMac
+ // */
+ // public static class SHA224
+ // extends JCEMac
+ // {
+ // public SHA224()
+ // {
+ // super(new HMac(new SHA224Digest()));
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * SHA-256 HMac
+ */
+ public static class SHA256
+ extends JCEMac
+ {
+ public SHA256()
+ {
+ // BEGIN android-changed
+ super(new HMac(AndroidDigestFactory.getSHA256()));
+ // END android-changed
+ }
+ }
+
+ /**
+ * SHA-384 HMac
+ */
+ public static class SHA384
+ extends JCEMac
+ {
+ public SHA384()
+ {
+ // BEGIN android-changed
+ super(new HMac(AndroidDigestFactory.getSHA384()));
+ // END android-changed
+ }
+ }
+
+ // BEGIN android-removed
+ // public static class OldSHA384
+ // extends JCEMac
+ // {
+ // public OldSHA384()
+ // {
+ // super(new OldHMac(new SHA384Digest()));
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * SHA-512 HMac
+ */
+ public static class SHA512
+ extends JCEMac
+ {
+ public SHA512()
+ {
+ // BEGIN android-changed
+ super(new HMac(AndroidDigestFactory.getSHA512()));
+ // END android-changed
+ }
+ }
+
+
+ // BEGIN android-removed
+ // /**
+ // * SHA-512 HMac
+ // */
+ // public static class OldSHA512
+ // extends JCEMac
+ // {
+ // public OldSHA512()
+ // {
+ // super(new OldHMac(new SHA512Digest()));
+ // }
+ // }
+ //
+ // /**
+ // * RIPEMD128 HMac
+ // */
+ // public static class RIPEMD128
+ // extends JCEMac
+ // {
+ // public RIPEMD128()
+ // {
+ // super(new HMac(new RIPEMD128Digest()));
+ // }
+ // }
+ //
+ // /**
+ // * RIPEMD160 HMac
+ // */
+ // public static class RIPEMD160
+ // extends JCEMac
+ // {
+ // public RIPEMD160()
+ // {
+ // super(new HMac(new RIPEMD160Digest()));
+ // }
+ // }
+ //
+ // /**
+ // * Tiger HMac
+ // */
+ // public static class Tiger
+ // extends JCEMac
+ // {
+ // public Tiger()
+ // {
+ // super(new HMac(new TigerDigest()));
+ // }
+ // }
+ //
+ // //
+ // // PKCS12 states that the same algorithm should be used
+ // // for the key generation as is used in the HMAC, so that
+ // // is what we do here.
+ // //
+ //
+ // /**
+ // * PBEWithHmacRIPEMD160
+ // */
+ // public static class PBEWithRIPEMD160
+ // extends JCEMac
+ // {
+ // public PBEWithRIPEMD160()
+ // {
+ // super(new HMac(new RIPEMD160Digest()), PKCS12, RIPEMD160, 160);
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * PBEWithHmacSHA
+ */
+ public static class PBEWithSHA
+ extends JCEMac
+ {
+ public PBEWithSHA()
+ {
+ // BEGIN android-changed
+ super(new HMac(AndroidDigestFactory.getSHA1()), PKCS12, SHA1, 160);
+ // END android-changed
+ }
+ }
+
+ // BEGIN android-removed
+ // /**
+ // * PBEWithHmacTiger
+ // */
+ // public static class PBEWithTiger
+ // extends JCEMac
+ // {
+ // public PBEWithTiger()
+ // {
+ // super(new HMac(new TigerDigest()), PKCS12, TIGER, 192);
+ // }
+ // }
+ // END android-removed
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java
new file mode 100644
index 0000000..c4c5b61
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java
@@ -0,0 +1,243 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+
+/**
+ * A provider representation for a RSA private key, with CRT factors included.
+ */
+public class JCERSAPrivateCrtKey
+ extends JCERSAPrivateKey
+ implements RSAPrivateCrtKey
+{
+ static final long serialVersionUID = 7834723820638524718L;
+
+ private BigInteger publicExponent;
+ private BigInteger primeP;
+ private BigInteger primeQ;
+ private BigInteger primeExponentP;
+ private BigInteger primeExponentQ;
+ private BigInteger crtCoefficient;
+
+ /**
+ * construct a private key from it's org.bouncycastle.crypto equivalent.
+ *
+ * @param key the parameters object representing the private key.
+ */
+ JCERSAPrivateCrtKey(
+ RSAPrivateCrtKeyParameters key)
+ {
+ super(key);
+
+ this.publicExponent = key.getPublicExponent();
+ this.primeP = key.getP();
+ this.primeQ = key.getQ();
+ this.primeExponentP = key.getDP();
+ this.primeExponentQ = key.getDQ();
+ this.crtCoefficient = key.getQInv();
+ }
+
+ /**
+ * construct a private key from an RSAPrivateCrtKeySpec
+ *
+ * @param spec the spec to be used in construction.
+ */
+ JCERSAPrivateCrtKey(
+ RSAPrivateCrtKeySpec spec)
+ {
+ this.modulus = spec.getModulus();
+ this.publicExponent = spec.getPublicExponent();
+ this.privateExponent = spec.getPrivateExponent();
+ this.primeP = spec.getPrimeP();
+ this.primeQ = spec.getPrimeQ();
+ this.primeExponentP = spec.getPrimeExponentP();
+ this.primeExponentQ = spec.getPrimeExponentQ();
+ this.crtCoefficient = spec.getCrtCoefficient();
+ }
+
+ /**
+ * construct a private key from another RSAPrivateCrtKey.
+ *
+ * @param key the object implementing the RSAPrivateCrtKey interface.
+ */
+ JCERSAPrivateCrtKey(
+ RSAPrivateCrtKey key)
+ {
+ this.modulus = key.getModulus();
+ this.publicExponent = key.getPublicExponent();
+ this.privateExponent = key.getPrivateExponent();
+ this.primeP = key.getPrimeP();
+ this.primeQ = key.getPrimeQ();
+ this.primeExponentP = key.getPrimeExponentP();
+ this.primeExponentQ = key.getPrimeExponentQ();
+ this.crtCoefficient = key.getCrtCoefficient();
+ }
+
+ /**
+ * construct an RSA key from a private key info object.
+ */
+ JCERSAPrivateCrtKey(
+ PrivateKeyInfo info)
+ throws IOException
+ {
+ this(org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(info.parsePrivateKey()));
+ }
+
+ /**
+ * construct an RSA key from a ASN.1 RSA private key object.
+ */
+ JCERSAPrivateCrtKey(
+ RSAPrivateKey key)
+ {
+ this.modulus = key.getModulus();
+ this.publicExponent = key.getPublicExponent();
+ this.privateExponent = key.getPrivateExponent();
+ this.primeP = key.getPrime1();
+ this.primeQ = key.getPrime2();
+ this.primeExponentP = key.getExponent1();
+ this.primeExponentQ = key.getExponent2();
+ this.crtCoefficient = key.getCoefficient();
+ }
+
+ /**
+ * return the encoding format we produce in getEncoded().
+ *
+ * @return the encoding format we produce in getEncoded().
+ */
+ public String getFormat()
+ {
+ return "PKCS#8";
+ }
+
+ /**
+ * Return a PKCS8 representation of the key. The sequence returned
+ * represents a full PrivateKeyInfo object.
+ *
+ * @return a PKCS8 representation of the key.
+ */
+ public byte[] getEncoded()
+ {
+ // BEGIN android-changed
+ return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKey(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient()));
+ // END android-changed
+ }
+
+ /**
+ * return the public exponent.
+ *
+ * @return the public exponent.
+ */
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * return the prime P.
+ *
+ * @return the prime P.
+ */
+ public BigInteger getPrimeP()
+ {
+ return primeP;
+ }
+
+ /**
+ * return the prime Q.
+ *
+ * @return the prime Q.
+ */
+ public BigInteger getPrimeQ()
+ {
+ return primeQ;
+ }
+
+ /**
+ * return the prime exponent for P.
+ *
+ * @return the prime exponent for P.
+ */
+ public BigInteger getPrimeExponentP()
+ {
+ return primeExponentP;
+ }
+
+ /**
+ * return the prime exponent for Q.
+ *
+ * @return the prime exponent for Q.
+ */
+ public BigInteger getPrimeExponentQ()
+ {
+ return primeExponentQ;
+ }
+
+ /**
+ * return the CRT coefficient.
+ *
+ * @return the CRT coefficient.
+ */
+ public BigInteger getCrtCoefficient()
+ {
+ return crtCoefficient;
+ }
+
+ public int hashCode()
+ {
+ return this.getModulus().hashCode()
+ ^ this.getPublicExponent().hashCode()
+ ^ this.getPrivateExponent().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof RSAPrivateCrtKey))
+ {
+ return false;
+ }
+
+ RSAPrivateCrtKey key = (RSAPrivateCrtKey)o;
+
+ return this.getModulus().equals(key.getModulus())
+ && this.getPublicExponent().equals(key.getPublicExponent())
+ && this.getPrivateExponent().equals(key.getPrivateExponent())
+ && this.getPrimeP().equals(key.getPrimeP())
+ && this.getPrimeQ().equals(key.getPrimeQ())
+ && this.getPrimeExponentP().equals(key.getPrimeExponentP())
+ && this.getPrimeExponentQ().equals(key.getPrimeExponentQ())
+ && this.getCrtCoefficient().equals(key.getCrtCoefficient());
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append("RSA Private CRT Key").append(nl);
+ buf.append(" modulus: ").append(this.getModulus().toString(16)).append(nl);
+ buf.append(" public exponent: ").append(this.getPublicExponent().toString(16)).append(nl);
+ buf.append(" private exponent: ").append(this.getPrivateExponent().toString(16)).append(nl);
+ buf.append(" primeP: ").append(this.getPrimeP().toString(16)).append(nl);
+ buf.append(" primeQ: ").append(this.getPrimeQ().toString(16)).append(nl);
+ buf.append(" primeExponentP: ").append(this.getPrimeExponentP().toString(16)).append(nl);
+ buf.append(" primeExponentQ: ").append(this.getPrimeExponentQ().toString(16)).append(nl);
+ buf.append(" crtCoefficient: ").append(this.getCrtCoefficient().toString(16)).append(nl);
+
+ return buf.toString();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
new file mode 100644
index 0000000..6277415
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java
@@ -0,0 +1,149 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.RSAPrivateKeySpec;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+
+public class JCERSAPrivateKey
+ implements RSAPrivateKey, PKCS12BagAttributeCarrier
+{
+ static final long serialVersionUID = 5110188922551353628L;
+
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+
+ protected BigInteger modulus;
+ protected BigInteger privateExponent;
+
+ private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ protected JCERSAPrivateKey()
+ {
+ }
+
+ JCERSAPrivateKey(
+ RSAKeyParameters key)
+ {
+ this.modulus = key.getModulus();
+ this.privateExponent = key.getExponent();
+ }
+
+ JCERSAPrivateKey(
+ RSAPrivateKeySpec spec)
+ {
+ this.modulus = spec.getModulus();
+ this.privateExponent = spec.getPrivateExponent();
+ }
+
+ JCERSAPrivateKey(
+ RSAPrivateKey key)
+ {
+ this.modulus = key.getModulus();
+ this.privateExponent = key.getPrivateExponent();
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPrivateExponent()
+ {
+ return privateExponent;
+ }
+
+ public String getAlgorithm()
+ {
+ return "RSA";
+ }
+
+ public String getFormat()
+ {
+ return "PKCS#8";
+ }
+
+ public byte[] getEncoded()
+ {
+ // BEGIN android-changed
+ return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new org.bouncycastle.asn1.pkcs.RSAPrivateKey(getModulus(), ZERO, getPrivateExponent(), ZERO, ZERO, ZERO, ZERO, ZERO));
+ // END android-changed
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof RSAPrivateKey))
+ {
+ return false;
+ }
+
+ if (o == this)
+ {
+ return true;
+ }
+
+ RSAPrivateKey key = (RSAPrivateKey)o;
+
+ return getModulus().equals(key.getModulus())
+ && getPrivateExponent().equals(key.getPrivateExponent());
+ }
+
+ public int hashCode()
+ {
+ return getModulus().hashCode() ^ getPrivateExponent().hashCode();
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ DERObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ this.modulus = (BigInteger)in.readObject();
+ this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ attrCarrier.readObject(in);
+
+ this.privateExponent = (BigInteger)in.readObject();
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(modulus);
+
+ attrCarrier.writeObject(out);
+
+ out.writeObject(privateExponent);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java
new file mode 100644
index 0000000..8d74351
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java
@@ -0,0 +1,133 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
+
+public class JCERSAPublicKey
+ implements RSAPublicKey
+{
+ static final long serialVersionUID = 2675817738516720772L;
+
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+
+ JCERSAPublicKey(
+ RSAKeyParameters key)
+ {
+ this.modulus = key.getModulus();
+ this.publicExponent = key.getExponent();
+ }
+
+ JCERSAPublicKey(
+ RSAPublicKeySpec spec)
+ {
+ this.modulus = spec.getModulus();
+ this.publicExponent = spec.getPublicExponent();
+ }
+
+ JCERSAPublicKey(
+ RSAPublicKey key)
+ {
+ this.modulus = key.getModulus();
+ this.publicExponent = key.getPublicExponent();
+ }
+
+ JCERSAPublicKey(
+ SubjectPublicKeyInfo info)
+ {
+ try
+ {
+ RSAPublicKeyStructure pubKey = new RSAPublicKeyStructure((ASN1Sequence)info.parsePublicKey());
+
+ this.modulus = pubKey.getModulus();
+ this.publicExponent = pubKey.getPublicExponent();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("invalid info structure in RSA public key");
+ }
+ }
+
+ /**
+ * return the modulus.
+ *
+ * @return the modulus.
+ */
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ /**
+ * return the public exponent.
+ *
+ * @return the public exponent.
+ */
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ public String getAlgorithm()
+ {
+ return "RSA";
+ }
+
+ public String getFormat()
+ {
+ return "X.509";
+ }
+
+ public byte[] getEncoded()
+ {
+ // BEGIN android-changed
+ return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKeyStructure(getModulus(), getPublicExponent()));
+ // END android-changed
+ }
+
+ public int hashCode()
+ {
+ return this.getModulus().hashCode() ^ this.getPublicExponent().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof RSAPublicKey))
+ {
+ return false;
+ }
+
+ RSAPublicKey key = (RSAPublicKey)o;
+
+ return getModulus().equals(key.getModulus())
+ && getPublicExponent().equals(key.getPublicExponent());
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append("RSA Public Key").append(nl);
+ buf.append(" modulus: ").append(this.getModulus().toString(16)).append(nl);
+ buf.append(" public exponent: ").append(this.getPublicExponent().toString(16)).append(nl);
+
+ return buf.toString();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java
new file mode 100644
index 0000000..7d70734
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCESecretKeyFactory.java
@@ -0,0 +1,612 @@
+package org.bouncycastle.jce.provider;
+
+import java.lang.reflect.Constructor;
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+import javax.crypto.spec.DESKeySpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.DESParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
+
+public class JCESecretKeyFactory
+ extends SecretKeyFactorySpi
+ implements PBE
+{
+ protected String algName;
+ protected DERObjectIdentifier algOid;
+
+ protected JCESecretKeyFactory(
+ String algName,
+ DERObjectIdentifier algOid)
+ {
+ this.algName = algName;
+ this.algOid = algOid;
+ }
+
+ protected SecretKey engineGenerateSecret(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof SecretKeySpec)
+ {
+ return (SecretKey)keySpec;
+ }
+
+ throw new InvalidKeySpecException("Invalid KeySpec");
+ }
+
+ protected KeySpec engineGetKeySpec(
+ SecretKey key,
+ Class keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec == null)
+ {
+ throw new InvalidKeySpecException("keySpec parameter is null");
+ }
+ if (key == null)
+ {
+ throw new InvalidKeySpecException("key parameter is null");
+ }
+
+ if (SecretKeySpec.class.isAssignableFrom(keySpec))
+ {
+ return new SecretKeySpec(key.getEncoded(), algName);
+ }
+
+ try
+ {
+ Class[] parameters = { byte[].class };
+
+ Constructor c = keySpec.getConstructor(parameters);
+ Object[] p = new Object[1];
+
+ p[0] = key.getEncoded();
+
+ return (KeySpec)c.newInstance(p);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidKeySpecException(e.toString());
+ }
+ }
+
+ protected SecretKey engineTranslateKey(
+ SecretKey key)
+ throws InvalidKeyException
+ {
+ if (key == null)
+ {
+ throw new InvalidKeyException("key parameter is null");
+ }
+
+ if (!key.getAlgorithm().equalsIgnoreCase(algName))
+ {
+ throw new InvalidKeyException("Key not of type " + algName + ".");
+ }
+
+ return new SecretKeySpec(key.getEncoded(), algName);
+ }
+
+ /*
+ * classes that inherit from us
+ */
+
+ static public class PBEKeyFactory
+ extends JCESecretKeyFactory
+ {
+ private boolean forCipher;
+ private int scheme;
+ private int digest;
+ private int keySize;
+ private int ivSize;
+
+ public PBEKeyFactory(
+ String algorithm,
+ DERObjectIdentifier oid,
+ boolean forCipher,
+ int scheme,
+ int digest,
+ int keySize,
+ int ivSize)
+ {
+ super(algorithm, oid);
+
+ this.forCipher = forCipher;
+ this.scheme = scheme;
+ this.digest = digest;
+ this.keySize = keySize;
+ this.ivSize = ivSize;
+ }
+
+ protected SecretKey engineGenerateSecret(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof PBEKeySpec)
+ {
+ PBEKeySpec pbeSpec = (PBEKeySpec)keySpec;
+ CipherParameters param;
+
+ if (pbeSpec.getSalt() == null)
+ {
+ return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, null);
+ }
+
+ if (forCipher)
+ {
+ param = Util.makePBEParameters(pbeSpec, scheme, digest, keySize, ivSize);
+ }
+ else
+ {
+ param = Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize);
+ }
+
+ return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param);
+ }
+
+ throw new InvalidKeySpecException("Invalid KeySpec");
+ }
+ }
+
+ static public class DESPBEKeyFactory
+ extends JCESecretKeyFactory
+ {
+ private boolean forCipher;
+ private int scheme;
+ private int digest;
+ private int keySize;
+ private int ivSize;
+
+ public DESPBEKeyFactory(
+ String algorithm,
+ DERObjectIdentifier oid,
+ boolean forCipher,
+ int scheme,
+ int digest,
+ int keySize,
+ int ivSize)
+ {
+ super(algorithm, oid);
+
+ this.forCipher = forCipher;
+ this.scheme = scheme;
+ this.digest = digest;
+ this.keySize = keySize;
+ this.ivSize = ivSize;
+ }
+
+ protected SecretKey engineGenerateSecret(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof PBEKeySpec)
+ {
+ PBEKeySpec pbeSpec = (PBEKeySpec)keySpec;
+ CipherParameters param;
+
+ if (pbeSpec.getSalt() == null)
+ {
+ return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, null);
+ }
+
+ if (forCipher)
+ {
+ param = Util.makePBEParameters(pbeSpec, scheme, digest, keySize, ivSize);
+ }
+ else
+ {
+ param = Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize);
+ }
+
+ KeyParameter kParam;
+ if (param instanceof ParametersWithIV)
+ {
+ kParam = (KeyParameter)((ParametersWithIV)param).getParameters();
+ }
+ else
+ {
+ kParam = (KeyParameter)param;
+ }
+
+ DESParameters.setOddParity(kParam.getKey());
+
+ return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param);
+ }
+
+ throw new InvalidKeySpecException("Invalid KeySpec");
+ }
+ }
+
+ static public class DES
+ extends JCESecretKeyFactory
+ {
+ public DES()
+ {
+ super("DES", null);
+ }
+
+ protected SecretKey engineGenerateSecret(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof DESKeySpec)
+ {
+ DESKeySpec desKeySpec = (DESKeySpec)keySpec;
+ return new SecretKeySpec(desKeySpec.getKey(), "DES");
+ }
+
+ return super.engineGenerateSecret(keySpec);
+ }
+ }
+
+ // BEGIN android-removed
+ // /**
+ // * PBEWithMD2AndDES
+ // */
+ // static public class PBEWithMD2AndDES
+ // extends DESPBEKeyFactory
+ // {
+ // public PBEWithMD2AndDES()
+ // {
+ // super("PBEwithMD2andDES", PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC, true, PKCS5S1, MD2, 64, 64);
+ // }
+ // }
+ //
+ // /**
+ // * PBEWithMD2AndRC2
+ // */
+ // static public class PBEWithMD2AndRC2
+ // extends PBEKeyFactory
+ // {
+ // public PBEWithMD2AndRC2()
+ // {
+ // super("PBEwithMD2andRC2", PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC, true, PKCS5S1, MD2, 64, 64);
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * PBEWithMD5AndDES
+ */
+ static public class PBEWithMD5AndDES
+ extends DESPBEKeyFactory
+ {
+ public PBEWithMD5AndDES()
+ {
+ super("PBEwithMD5andDES", PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, true, PKCS5S1, MD5, 64, 64);
+ }
+ }
+
+ /**
+ * PBEWithMD5AndRC2
+ */
+ static public class PBEWithMD5AndRC2
+ extends PBEKeyFactory
+ {
+ public PBEWithMD5AndRC2()
+ {
+ super("PBEwithMD5andRC2", PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC, true, PKCS5S1, MD5, 64, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHA1AndDES
+ */
+ static public class PBEWithSHA1AndDES
+ extends DESPBEKeyFactory
+ {
+ public PBEWithSHA1AndDES()
+ {
+ super("PBEwithSHA1andDES", PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, true, PKCS5S1, SHA1, 64, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHA1AndRC2
+ */
+ static public class PBEWithSHA1AndRC2
+ extends PBEKeyFactory
+ {
+ public PBEWithSHA1AndRC2()
+ {
+ super("PBEwithSHA1andRC2", PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC, true, PKCS5S1, SHA1, 64, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd3-KeyTripleDES-CBC
+ */
+ static public class PBEWithSHAAndDES3Key
+ extends DESPBEKeyFactory
+ {
+ public PBEWithSHAAndDES3Key()
+ {
+ super("PBEwithSHAandDES3Key-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, true, PKCS12, SHA1, 192, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd2-KeyTripleDES-CBC
+ */
+ static public class PBEWithSHAAndDES2Key
+ extends DESPBEKeyFactory
+ {
+ public PBEWithSHAAndDES2Key()
+ {
+ super("PBEwithSHAandDES2Key-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, true, PKCS12, SHA1, 128, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd128BitRC2-CBC
+ */
+ static public class PBEWithSHAAnd128BitRC2
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd128BitRC2()
+ {
+ super("PBEwithSHAand128BitRC2-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, true, PKCS12, SHA1, 128, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd40BitRC2-CBC
+ */
+ static public class PBEWithSHAAnd40BitRC2
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd40BitRC2()
+ {
+ super("PBEwithSHAand40BitRC2-CBC", PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, true, PKCS12, SHA1, 40, 64);
+ }
+ }
+
+ /**
+ * PBEWithSHAAndTwofish-CBC
+ */
+ static public class PBEWithSHAAndTwofish
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAndTwofish()
+ {
+ super("PBEwithSHAandTwofish-CBC", null, true, PKCS12, SHA1, 256, 128);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd128BitRC4
+ */
+ static public class PBEWithSHAAnd128BitRC4
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd128BitRC4()
+ {
+ super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 128, 0);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd40BitRC4
+ */
+ static public class PBEWithSHAAnd40BitRC4
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd40BitRC4()
+ {
+ super("PBEWithSHAAnd128BitRC4", PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, true, PKCS12, SHA1, 40, 0);
+ }
+ }
+
+ // BEGIN android-removed
+ // /**
+ // * PBEWithHmacRIPEMD160
+ // */
+ // public static class PBEWithRIPEMD160
+ // extends PBEKeyFactory
+ // {
+ // public PBEWithRIPEMD160()
+ // {
+ // super("PBEwithHmacRIPEMD160", null, false, PKCS12, RIPEMD160, 160, 0);
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * PBEWithHmacSHA
+ */
+ public static class PBEWithSHA
+ extends PBEKeyFactory
+ {
+ public PBEWithSHA()
+ {
+ super("PBEwithHmacSHA", null, false, PKCS12, SHA1, 160, 0);
+ }
+ }
+
+ // BEGIN android-removed
+ // /**
+ // * PBEWithHmacTiger
+ // */
+ // public static class PBEWithTiger
+ // extends PBEKeyFactory
+ // {
+ // public PBEWithTiger()
+ // {
+ // super("PBEwithHmacTiger", null, false, PKCS12, TIGER, 192, 0);
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * PBEWithSHA1And128BitAES-BC
+ */
+ static public class PBEWithSHAAnd128BitAESBC
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd128BitAESBC()
+ {
+ super("PBEWithSHA1And128BitAES-CBC-BC", null, true, PKCS12, SHA1, 128, 128);
+ }
+ }
+
+ /**
+ * PBEWithSHA1And192BitAES-BC
+ */
+ static public class PBEWithSHAAnd192BitAESBC
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd192BitAESBC()
+ {
+ super("PBEWithSHA1And192BitAES-CBC-BC", null, true, PKCS12, SHA1, 192, 128);
+ }
+ }
+
+ /**
+ * PBEWithSHA1And256BitAES-BC
+ */
+ static public class PBEWithSHAAnd256BitAESBC
+ extends PBEKeyFactory
+ {
+ public PBEWithSHAAnd256BitAESBC()
+ {
+ super("PBEWithSHA1And256BitAES-CBC-BC", null, true, PKCS12, SHA1, 256, 128);
+ }
+ }
+
+ /**
+ * PBEWithSHA256And128BitAES-BC
+ */
+ static public class PBEWithSHA256And128BitAESBC
+ extends PBEKeyFactory
+ {
+ public PBEWithSHA256And128BitAESBC()
+ {
+ super("PBEWithSHA256And128BitAES-CBC-BC", null, true, PKCS12, SHA256, 128, 128);
+ }
+ }
+
+ /**
+ * PBEWithSHA256And192BitAES-BC
+ */
+ static public class PBEWithSHA256And192BitAESBC
+ extends PBEKeyFactory
+ {
+ public PBEWithSHA256And192BitAESBC()
+ {
+ super("PBEWithSHA256And192BitAES-CBC-BC", null, true, PKCS12, SHA256, 192, 128);
+ }
+ }
+
+ /**
+ * PBEWithSHA256And256BitAES-BC
+ */
+ static public class PBEWithSHA256And256BitAESBC
+ extends PBEKeyFactory
+ {
+ public PBEWithSHA256And256BitAESBC()
+ {
+ super("PBEWithSHA256And256BitAES-CBC-BC", null, true, PKCS12, SHA256, 256, 128);
+ }
+ }
+
+ /**
+ * PBEWithMD5And128BitAES-OpenSSL
+ */
+ static public class PBEWithMD5And128BitAESCBCOpenSSL
+ extends PBEKeyFactory
+ {
+ public PBEWithMD5And128BitAESCBCOpenSSL()
+ {
+ super("PBEWithMD5And128BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 128, 128);
+ }
+ }
+
+ /**
+ * PBEWithMD5And192BitAES-OpenSSL
+ */
+ static public class PBEWithMD5And192BitAESCBCOpenSSL
+ extends PBEKeyFactory
+ {
+ public PBEWithMD5And192BitAESCBCOpenSSL()
+ {
+ super("PBEWithMD5And192BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 192, 128);
+ }
+ }
+
+ /**
+ * PBEWithMD5And256BitAES-OpenSSL
+ */
+ static public class PBEWithMD5And256BitAESCBCOpenSSL
+ extends PBEKeyFactory
+ {
+ public PBEWithMD5And256BitAESCBCOpenSSL()
+ {
+ super("PBEWithMD5And256BitAES-CBC-OpenSSL", null, true, OPENSSL, MD5, 256, 128);
+ }
+ }
+ // BEGIN android-added
+ static public class PBKDF2WithHmacSHA1
+ extends JCESecretKeyFactory
+ {
+ public PBKDF2WithHmacSHA1()
+ {
+ super("PBKDF2WithHmacSHA1", PKCSObjectIdentifiers.id_PBKDF2);
+ }
+
+ protected SecretKey engineGenerateSecret(
+ KeySpec keySpec)
+ throws InvalidKeySpecException
+ {
+ if (keySpec instanceof PBEKeySpec)
+ {
+ PBEKeySpec pbeSpec = (PBEKeySpec)keySpec;
+
+ if (pbeSpec.getSalt() == null)
+ {
+ throw new InvalidKeySpecException("missing required salt");
+ }
+
+ if (pbeSpec.getIterationCount() <= 0)
+ {
+ throw new InvalidKeySpecException("positive iteration count required: "
+ + pbeSpec.getIterationCount());
+ }
+
+ if (pbeSpec.getKeyLength() <= 0)
+ {
+ throw new InvalidKeySpecException("positive key length required: "
+ + pbeSpec.getKeyLength());
+ }
+
+ if (pbeSpec.getPassword().length == 0)
+ {
+ throw new IllegalArgumentException("password empty");
+ }
+
+ int scheme = PKCS5S2;
+ int digest = SHA1;
+ int keySize = pbeSpec.getKeyLength();
+ int ivSize = -1;
+ CipherParameters param = Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize);
+
+ return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param);
+ }
+
+ throw new InvalidKeySpecException("Invalid KeySpec");
+ }
+ }
+ // END android-added
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
new file mode 100644
index 0000000..16a14ec
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
@@ -0,0 +1,532 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+// BEGIN android-removed
+// import javax.crypto.spec.RC2ParameterSpec;
+// import javax.crypto.spec.RC5ParameterSpec;
+// END android-removed
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.StreamCipher;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.BlowfishEngine;
+// import org.bouncycastle.crypto.engines.DESEngine;
+// import org.bouncycastle.crypto.engines.DESedeEngine;
+// END android-removed
+import org.bouncycastle.crypto.engines.RC4Engine;
+// BEGIN android-removed
+// import org.bouncycastle.crypto.engines.SkipjackEngine;
+// import org.bouncycastle.crypto.engines.TwofishEngine;
+// END android-removed
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
+
+public class JCEStreamCipher
+ extends CipherSpi
+ implements PBE
+{
+ //
+ // specs we can handle.
+ //
+ private Class[] availableSpecs =
+ {
+ // BEGIN android-removed
+ // RC2ParameterSpec.class,
+ // RC5ParameterSpec.class,
+ // END android-removed
+ IvParameterSpec.class,
+ PBEParameterSpec.class
+ };
+
+ private StreamCipher cipher;
+ private ParametersWithIV ivParam;
+
+ private int ivLength = 0;
+
+ private PBEParameterSpec pbeSpec = null;
+ private String pbeAlgorithm = null;
+
+ private AlgorithmParameters engineParams;
+
+ protected JCEStreamCipher(
+ StreamCipher engine,
+ int ivLength)
+ {
+ cipher = engine;
+ this.ivLength = ivLength;
+ }
+
+ protected JCEStreamCipher(
+ BlockCipher engine,
+ int ivLength)
+ {
+ this.ivLength = ivLength;
+
+ cipher = new StreamBlockCipher(engine);
+ }
+
+ protected int engineGetBlockSize()
+ {
+ return 0;
+ }
+
+ protected byte[] engineGetIV()
+ {
+ return (ivParam != null) ? ivParam.getIV() : null;
+ }
+
+ protected int engineGetKeySize(
+ Key key)
+ {
+ return key.getEncoded().length * 8;
+ }
+
+ protected int engineGetOutputSize(
+ int inputLen)
+ {
+ return inputLen;
+ }
+
+ protected AlgorithmParameters engineGetParameters()
+ {
+ if (engineParams == null)
+ {
+ if (pbeSpec != null)
+ {
+ try
+ {
+ AlgorithmParameters engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
+ engineParams.init(pbeSpec);
+
+ return engineParams;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ }
+
+ return engineParams;
+ }
+
+ /**
+ * should never be called.
+ */
+ protected void engineSetMode(
+ String mode)
+ {
+ if (!mode.equalsIgnoreCase("ECB"))
+ {
+ throw new IllegalArgumentException("can't support mode " + mode);
+ }
+ }
+
+ /**
+ * should never be called.
+ */
+ protected void engineSetPadding(
+ String padding)
+ throws NoSuchPaddingException
+ {
+ if (!padding.equalsIgnoreCase("NoPadding"))
+ {
+ throw new NoSuchPaddingException("Padding " + padding + " unknown.");
+ }
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ CipherParameters param;
+
+ this.pbeSpec = null;
+ this.pbeAlgorithm = null;
+
+ this.engineParams = null;
+
+ //
+ // basic key check
+ //
+ if (!(key instanceof SecretKey))
+ {
+ throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption.");
+ }
+
+ if (key instanceof BCPBEKey)
+ {
+ BCPBEKey k = (BCPBEKey)key;
+
+ if (k.getOID() != null)
+ {
+ pbeAlgorithm = k.getOID().getId();
+ }
+ else
+ {
+ pbeAlgorithm = k.getAlgorithm();
+ }
+
+ if (k.getParam() != null)
+ {
+ param = k.getParam();
+ pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount());
+ }
+ else if (params instanceof PBEParameterSpec)
+ {
+ param = PBE.Util.makePBEParameters(k, params, cipher.getAlgorithmName());
+ pbeSpec = (PBEParameterSpec)params;
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
+ }
+
+ if (k.getIvSize() != 0)
+ {
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else if (params == null)
+ {
+ param = new KeyParameter(key.getEncoded());
+ }
+ else if (params instanceof IvParameterSpec)
+ {
+ param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ else
+ {
+ throw new IllegalArgumentException("unknown parameter type.");
+ }
+
+ if ((ivLength != 0) && !(param instanceof ParametersWithIV))
+ {
+ SecureRandom ivRandom = random;
+
+ if (ivRandom == null)
+ {
+ ivRandom = new SecureRandom();
+ }
+
+ if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
+ {
+ byte[] iv = new byte[ivLength];
+
+ ivRandom.nextBytes(iv);
+ param = new ParametersWithIV(param, iv);
+ ivParam = (ParametersWithIV)param;
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("no IV set when one expected");
+ }
+ }
+
+ switch (opmode)
+ {
+ case Cipher.ENCRYPT_MODE:
+ case Cipher.WRAP_MODE:
+ cipher.init(true, param);
+ break;
+ case Cipher.DECRYPT_MODE:
+ case Cipher.UNWRAP_MODE:
+ cipher.init(false, param);
+ break;
+ default:
+ System.out.println("eeek!");
+ }
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ AlgorithmParameterSpec paramSpec = null;
+
+ if (params != null)
+ {
+ for (int i = 0; i != availableSpecs.length; i++)
+ {
+ try
+ {
+ paramSpec = params.getParameterSpec(availableSpecs[i]);
+ break;
+ }
+ catch (Exception e)
+ {
+ continue;
+ }
+ }
+
+ if (paramSpec == null)
+ {
+ throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
+ }
+ }
+
+ engineInit(opmode, key, paramSpec, random);
+ engineParams = params;
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ try
+ {
+ engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw new InvalidKeyException(e.getMessage());
+ }
+ }
+
+ protected byte[] engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ {
+ byte[] out = new byte[inputLen];
+
+ cipher.processBytes(input, inputOffset, inputLen, out, 0);
+
+ return out;
+ }
+
+ protected int engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ throws ShortBufferException
+ {
+ try
+ {
+ cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+
+ return inputLen;
+ }
+ catch (DataLengthException e)
+ {
+ throw new ShortBufferException(e.getMessage());
+ }
+ }
+
+ protected byte[] engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ {
+ if (inputLen != 0)
+ {
+ byte[] out = engineUpdate(input, inputOffset, inputLen);
+
+ cipher.reset();
+
+ return out;
+ }
+
+ cipher.reset();
+
+ return new byte[0];
+ }
+
+ protected int engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ {
+ if (inputLen != 0)
+ {
+ cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+ }
+
+ cipher.reset();
+
+ return inputLen;
+ }
+
+ /*
+ * The ciphers that inherit from us.
+ */
+
+ // BEGIN android-removed
+ // /**
+ // * DES
+ // */
+ // static public class DES_CFB8
+ // extends JCEStreamCipher
+ // {
+ // public DES_CFB8()
+ // {
+ // super(new CFBBlockCipher(new DESEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * DESede
+ // */
+ // static public class DESede_CFB8
+ // extends JCEStreamCipher
+ // {
+ // public DESede_CFB8()
+ // {
+ // super(new CFBBlockCipher(new DESedeEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * SKIPJACK
+ // */
+ // static public class Skipjack_CFB8
+ // extends JCEStreamCipher
+ // {
+ // public Skipjack_CFB8()
+ // {
+ // super(new CFBBlockCipher(new SkipjackEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * Blowfish
+ // */
+ // static public class Blowfish_CFB8
+ // extends JCEStreamCipher
+ // {
+ // public Blowfish_CFB8()
+ // {
+ // super(new CFBBlockCipher(new BlowfishEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * Twofish
+ // */
+ // static public class Twofish_CFB8
+ // extends JCEStreamCipher
+ // {
+ // public Twofish_CFB8()
+ // {
+ // super(new CFBBlockCipher(new TwofishEngine(), 8), 128);
+ // }
+ // }
+ //
+ // /**
+ // * DES
+ // */
+ // static public class DES_OFB8
+ // extends JCEStreamCipher
+ // {
+ // public DES_OFB8()
+ // {
+ // super(new OFBBlockCipher(new DESEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * DESede
+ // */
+ // static public class DESede_OFB8
+ // extends JCEStreamCipher
+ // {
+ // public DESede_OFB8()
+ // {
+ // super(new OFBBlockCipher(new DESedeEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * SKIPJACK
+ // */
+ // static public class Skipjack_OFB8
+ // extends JCEStreamCipher
+ // {
+ // public Skipjack_OFB8()
+ // {
+ // super(new OFBBlockCipher(new SkipjackEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * Blowfish
+ // */
+ // static public class Blowfish_OFB8
+ // extends JCEStreamCipher
+ // {
+ // public Blowfish_OFB8()
+ // {
+ // super(new OFBBlockCipher(new BlowfishEngine(), 8), 64);
+ // }
+ // }
+ //
+ // /**
+ // * Twofish
+ // */
+ // static public class Twofish_OFB8
+ // extends JCEStreamCipher
+ // {
+ // public Twofish_OFB8()
+ // {
+ // super(new OFBBlockCipher(new TwofishEngine(), 8), 128);
+ // }
+ // }
+ // END android-removed
+
+ /**
+ * PBEWithSHAAnd128BitRC4
+ */
+ static public class PBEWithSHAAnd128BitRC4
+ extends JCEStreamCipher
+ {
+ public PBEWithSHAAnd128BitRC4()
+ {
+ super(new RC4Engine(), 0);
+ }
+ }
+
+ /**
+ * PBEWithSHAAnd40BitRC4
+ */
+ static public class PBEWithSHAAnd40BitRC4
+ extends JCEStreamCipher
+ {
+ public PBEWithSHAAnd40BitRC4()
+ {
+ super(new RC4Engine(), 0);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java
new file mode 100644
index 0000000..9a8cf9b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKAlgorithmParameters.java
@@ -0,0 +1,320 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.pkcs.PBKDF2Params;
+import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
+// BEGIN android-removed
+// import org.bouncycastle.jce.spec.IESParameterSpec;
+// END android-removed
+
+public abstract class JDKAlgorithmParameters
+ extends AlgorithmParametersSpi
+{
+ protected boolean isASN1FormatString(String format)
+ {
+ return format == null || format.equals("ASN.1");
+ }
+
+ protected AlgorithmParameterSpec engineGetParameterSpec(
+ Class paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (paramSpec == null)
+ {
+ throw new NullPointerException("argument to getParameterSpec must not be null");
+ }
+
+ return localEngineGetParameterSpec(paramSpec);
+ }
+
+ protected abstract AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec)
+ throws InvalidParameterSpecException;
+
+ public static class PBKDF2
+ extends JDKAlgorithmParameters
+ {
+ PBKDF2Params params;
+
+ protected byte[] engineGetEncoded()
+ {
+ try
+ {
+ return params.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Oooops! " + e.toString());
+ }
+ }
+
+ protected byte[] engineGetEncoded(
+ String format)
+ {
+ if (isASN1FormatString(format))
+ {
+ return engineGetEncoded();
+ }
+
+ return null;
+ }
+
+ protected AlgorithmParameterSpec localEngineGetParameterSpec(
+ Class paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (paramSpec == PBEParameterSpec.class)
+ {
+ return new PBEParameterSpec(params.getSalt(),
+ params.getIterationCount().intValue());
+ }
+
+ throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
+ }
+
+ protected void engineInit(
+ AlgorithmParameterSpec paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (!(paramSpec instanceof PBEParameterSpec))
+ {
+ throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
+ }
+
+ PBEParameterSpec pbeSpec = (PBEParameterSpec)paramSpec;
+
+ this.params = new PBKDF2Params(pbeSpec.getSalt(),
+ pbeSpec.getIterationCount());
+ }
+
+ protected void engineInit(
+ byte[] params)
+ throws IOException
+ {
+ this.params = PBKDF2Params.getInstance(ASN1Primitive.fromByteArray(params));
+ }
+
+ protected void engineInit(
+ byte[] params,
+ String format)
+ throws IOException
+ {
+ if (isASN1FormatString(format))
+ {
+ engineInit(params);
+ return;
+ }
+
+ throw new IOException("Unknown parameters format in PWRIKEK parameters object");
+ }
+
+ protected String engineToString()
+ {
+ return "PBKDF2 Parameters";
+ }
+ }
+
+ public static class PKCS12PBE
+ extends JDKAlgorithmParameters
+ {
+ PKCS12PBEParams params;
+
+ protected byte[] engineGetEncoded()
+ {
+ try
+ {
+ return params.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Oooops! " + e.toString());
+ }
+ }
+
+ protected byte[] engineGetEncoded(
+ String format)
+ {
+ if (isASN1FormatString(format))
+ {
+ return engineGetEncoded();
+ }
+
+ return null;
+ }
+
+ protected AlgorithmParameterSpec localEngineGetParameterSpec(
+ Class paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (paramSpec == PBEParameterSpec.class)
+ {
+ return new PBEParameterSpec(params.getIV(),
+ params.getIterations().intValue());
+ }
+
+ throw new InvalidParameterSpecException("unknown parameter spec passed to PKCS12 PBE parameters object.");
+ }
+
+ protected void engineInit(
+ AlgorithmParameterSpec paramSpec)
+ throws InvalidParameterSpecException
+ {
+ if (!(paramSpec instanceof PBEParameterSpec))
+ {
+ throw new InvalidParameterSpecException("PBEParameterSpec required to initialise a PKCS12 PBE parameters algorithm parameters object");
+ }
+
+ PBEParameterSpec pbeSpec = (PBEParameterSpec)paramSpec;
+
+ this.params = new PKCS12PBEParams(pbeSpec.getSalt(),
+ pbeSpec.getIterationCount());
+ }
+
+ protected void engineInit(
+ byte[] params)
+ throws IOException
+ {
+ this.params = PKCS12PBEParams.getInstance(ASN1Primitive.fromByteArray(params));
+ }
+
+ protected void engineInit(
+ byte[] params,
+ String format)
+ throws IOException
+ {
+ if (isASN1FormatString(format))
+ {
+ engineInit(params);
+ return;
+ }
+
+ throw new IOException("Unknown parameters format in PKCS12 PBE parameters object");
+ }
+
+ protected String engineToString()
+ {
+ return "PKCS12 PBE Parameters";
+ }
+ }
+
+ // BEGIN android-removed
+ // public static class IES
+ // extends JDKAlgorithmParameters
+ // {
+ // IESParameterSpec currentSpec;
+ //
+ // /**
+ // * in the absence of a standard way of doing it this will do for
+ // * now...
+ // */
+ // protected byte[] engineGetEncoded()
+ // {
+ // try
+ // {
+ // ASN1EncodableVector v = new ASN1EncodableVector();
+ //
+ // v.add(new DEROctetString(currentSpec.getDerivationV()));
+ // v.add(new DEROctetString(currentSpec.getEncodingV()));
+ // v.add(new DERInteger(currentSpec.getMacKeySize()));
+ //
+ // return new DERSequence(v).getEncoded(ASN1Encoding.DER);
+ // }
+ // catch (IOException e)
+ // {
+ // throw new RuntimeException("Error encoding IESParameters");
+ // }
+ // }
+ //
+ // protected byte[] engineGetEncoded(
+ // String format)
+ // {
+ // if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+ // {
+ // return engineGetEncoded();
+ // }
+ //
+ // return null;
+ // }
+ //
+ // protected AlgorithmParameterSpec localEngineGetParameterSpec(
+ // Class paramSpec)
+ // throws InvalidParameterSpecException
+ // {
+ // if (paramSpec == IESParameterSpec.class)
+ // {
+ // return currentSpec;
+ // }
+ //
+ // throw new InvalidParameterSpecException("unknown parameter spec passed to ElGamal parameters object.");
+ // }
+ //
+ // protected void engineInit(
+ // AlgorithmParameterSpec paramSpec)
+ // throws InvalidParameterSpecException
+ // {
+ // if (!(paramSpec instanceof IESParameterSpec))
+ // {
+ // throw new InvalidParameterSpecException("IESParameterSpec required to initialise a IES algorithm parameters object");
+ // }
+ //
+ // this.currentSpec = (IESParameterSpec)paramSpec;
+ // }
+ //
+ // protected void engineInit(
+ // byte[] params)
+ // throws IOException
+ // {
+ // try
+ // {
+ // ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(params);
+ //
+ // this.currentSpec = new IESParameterSpec(
+ // ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
+ // ((ASN1OctetString)s.getObjectAt(0)).getOctets(),
+ // ((DERInteger)s.getObjectAt(0)).getValue().intValue());
+ // }
+ // catch (ClassCastException e)
+ // {
+ // throw new IOException("Not a valid IES Parameter encoding.");
+ // }
+ // catch (ArrayIndexOutOfBoundsException e)
+ // {
+ // throw new IOException("Not a valid IES Parameter encoding.");
+ // }
+ // }
+ //
+ // protected void engineInit(
+ // byte[] params,
+ // String format)
+ // throws IOException
+ // {
+ // if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509"))
+ // {
+ // engineInit(params);
+ // }
+ // else
+ // {
+ // throw new IOException("Unknown parameter format " + format);
+ // }
+ // }
+ //
+ // protected String engineToString()
+ // {
+ // return "IES Parameters";
+ // }
+ // }
+ // END android-removed
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java
new file mode 100644
index 0000000..379120e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.DSAPrivateKeySpec;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+
+public class JDKDSAPrivateKey
+ implements DSAPrivateKey, PKCS12BagAttributeCarrier
+{
+ private static final long serialVersionUID = -4677259546958385734L;
+
+ BigInteger x;
+ DSAParams dsaSpec;
+
+ private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ protected JDKDSAPrivateKey()
+ {
+ }
+
+ JDKDSAPrivateKey(
+ DSAPrivateKey key)
+ {
+ this.x = key.getX();
+ this.dsaSpec = key.getParams();
+ }
+
+ JDKDSAPrivateKey(
+ DSAPrivateKeySpec spec)
+ {
+ this.x = spec.getX();
+ this.dsaSpec = new DSAParameterSpec(spec.getP(), spec.getQ(), spec.getG());
+ }
+
+ JDKDSAPrivateKey(
+ PrivateKeyInfo info)
+ throws IOException
+ {
+ DSAParameter params = new DSAParameter((ASN1Sequence)info.getAlgorithmId().getParameters());
+ DERInteger derX = ASN1Integer.getInstance(info.parsePrivateKey());
+
+ this.x = derX.getValue();
+ this.dsaSpec = new DSAParameterSpec(params.getP(), params.getQ(), params.getG());
+ }
+
+ JDKDSAPrivateKey(
+ DSAPrivateKeyParameters params)
+ {
+ this.x = params.getX();
+ this.dsaSpec = new DSAParameterSpec(params.getParameters().getP(), params.getParameters().getQ(), params.getParameters().getG());
+ }
+
+ public String getAlgorithm()
+ {
+ return "DSA";
+ }
+
+ /**
+ * return the encoding format we produce in getEncoded().
+ *
+ * @return the string "PKCS#8"
+ */
+ public String getFormat()
+ {
+ return "PKCS#8";
+ }
+
+ /**
+ * Return a PKCS8 representation of the key. The sequence returned
+ * represents a full PrivateKeyInfo object.
+ *
+ * @return a PKCS8 representation of the key.
+ */
+ public byte[] getEncoded()
+ {
+ try
+ {
+ PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(dsaSpec.getP(), dsaSpec.getQ(), dsaSpec.getG())), new DERInteger(getX()));
+
+ return info.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public DSAParams getParams()
+ {
+ return dsaSpec;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DSAPrivateKey))
+ {
+ return false;
+ }
+
+ DSAPrivateKey other = (DSAPrivateKey)o;
+
+ return this.getX().equals(other.getX())
+ && this.getParams().getG().equals(other.getParams().getG())
+ && this.getParams().getP().equals(other.getParams().getP())
+ && this.getParams().getQ().equals(other.getParams().getQ());
+ }
+
+ public int hashCode()
+ {
+ return this.getX().hashCode() ^ this.getParams().getG().hashCode()
+ ^ this.getParams().getP().hashCode() ^ this.getParams().getQ().hashCode();
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ DERObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ this.x = (BigInteger)in.readObject();
+ this.dsaSpec = new DSAParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject());
+ this.attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ attrCarrier.readObject(in);
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(x);
+ out.writeObject(dsaSpec.getP());
+ out.writeObject(dsaSpec.getQ());
+ out.writeObject(dsaSpec.getG());
+
+ attrCarrier.writeObject(out);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java
new file mode 100644
index 0000000..16a964d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.DSAPublicKeySpec;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+
+public class JDKDSAPublicKey
+ implements DSAPublicKey
+{
+ private static final long serialVersionUID = 1752452449903495175L;
+
+ private BigInteger y;
+ private DSAParams dsaSpec;
+
+ JDKDSAPublicKey(
+ DSAPublicKeySpec spec)
+ {
+ this.y = spec.getY();
+ this.dsaSpec = new DSAParameterSpec(spec.getP(), spec.getQ(), spec.getG());
+ }
+
+ JDKDSAPublicKey(
+ DSAPublicKey key)
+ {
+ this.y = key.getY();
+ this.dsaSpec = key.getParams();
+ }
+
+ JDKDSAPublicKey(
+ DSAPublicKeyParameters params)
+ {
+ this.y = params.getY();
+ this.dsaSpec = new DSAParameterSpec(params.getParameters().getP(), params.getParameters().getQ(), params.getParameters().getG());
+ }
+
+ JDKDSAPublicKey(
+ BigInteger y,
+ DSAParameterSpec dsaSpec)
+ {
+ this.y = y;
+ this.dsaSpec = dsaSpec;
+ }
+
+ JDKDSAPublicKey(
+ SubjectPublicKeyInfo info)
+ {
+
+ DERInteger derY;
+
+ try
+ {
+ derY = (DERInteger)info.parsePublicKey();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("invalid info structure in DSA public key");
+ }
+
+ this.y = derY.getValue();
+
+ if (isNotNull(info.getAlgorithmId().getParameters()))
+ {
+ DSAParameter params = new DSAParameter((ASN1Sequence)info.getAlgorithmId().getParameters());
+
+ this.dsaSpec = new DSAParameterSpec(params.getP(), params.getQ(), params.getG());
+ }
+ }
+
+ private boolean isNotNull(ASN1Encodable parameters)
+ {
+ return parameters != null && !DERNull.INSTANCE.equals(parameters);
+ }
+
+ public String getAlgorithm()
+ {
+ return "DSA";
+ }
+
+ public String getFormat()
+ {
+ return "X.509";
+ }
+
+ public byte[] getEncoded()
+ {
+ try
+ {
+ if (dsaSpec == null)
+ {
+ return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa), new DERInteger(y)).getEncoded(ASN1Encoding.DER);
+ }
+
+ return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(dsaSpec.getP(), dsaSpec.getQ(), dsaSpec.getG())), new DERInteger(y)).getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public DSAParams getParams()
+ {
+ return dsaSpec;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append("DSA Public Key").append(nl);
+ buf.append(" y: ").append(this.getY().toString(16)).append(nl);
+
+ return buf.toString();
+ }
+
+ public int hashCode()
+ {
+ return this.getY().hashCode() ^ this.getParams().getG().hashCode()
+ ^ this.getParams().getP().hashCode() ^ this.getParams().getQ().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DSAPublicKey))
+ {
+ return false;
+ }
+
+ DSAPublicKey other = (DSAPublicKey)o;
+
+ return this.getY().equals(other.getY())
+ && this.getParams().getG().equals(other.getParams().getG())
+ && this.getParams().getP().equals(other.getParams().getP())
+ && this.getParams().getQ().equals(other.getParams().getQ());
+ }
+
+ private void readObject(
+ ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ this.y = (BigInteger)in.readObject();
+ this.dsaSpec = new DSAParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), (BigInteger)in.readObject());
+ }
+
+ private void writeObject(
+ ObjectOutputStream out)
+ throws IOException
+ {
+ out.writeObject(y);
+ out.writeObject(dsaSpec.getP());
+ out.writeObject(dsaSpec.getQ());
+ out.writeObject(dsaSpec.getG());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java
new file mode 100644
index 0000000..2c9c012
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKKeyStore.java
@@ -0,0 +1,1048 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+// BEGIN android-added
+import org.bouncycastle.crypto.digests.AndroidDigestFactory;
+// END android-added
+// BEGIN android-removed
+// import org.bouncycastle.crypto.digests.SHA1Digest;
+// END android-removed
+import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.bouncycastle.crypto.io.DigestInputStream;
+import org.bouncycastle.crypto.io.DigestOutputStream;
+import org.bouncycastle.crypto.io.MacInputStream;
+import org.bouncycastle.crypto.io.MacOutputStream;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.jce.interfaces.BCKeyStore;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+import org.bouncycastle.util.io.TeeOutputStream;
+
+public class JDKKeyStore
+ extends KeyStoreSpi
+ implements BCKeyStore
+{
+ private static final int STORE_VERSION = 2;
+
+ private static final int STORE_SALT_SIZE = 20;
+ private static final String STORE_CIPHER = "PBEWithSHAAndTwofish-CBC";
+
+ private static final int KEY_SALT_SIZE = 20;
+ private static final int MIN_ITERATIONS = 1024;
+
+ private static final String KEY_CIPHER = "PBEWithSHAAnd3-KeyTripleDES-CBC";
+
+ //
+ // generic object types
+ //
+ static final int NULL = 0;
+ static final int CERTIFICATE = 1;
+ static final int KEY = 2;
+ static final int SECRET = 3;
+ static final int SEALED = 4;
+
+ //
+ // key types
+ //
+ static final int KEY_PRIVATE = 0;
+ static final int KEY_PUBLIC = 1;
+ static final int KEY_SECRET = 2;
+
+ protected Hashtable table = new Hashtable();
+
+ protected SecureRandom random = new SecureRandom();
+
+ public JDKKeyStore()
+ {
+ }
+
+ private class StoreEntry
+ {
+ int type;
+ String alias;
+ Object obj;
+ Certificate[] certChain;
+ Date date = new Date();
+
+ StoreEntry(
+ String alias,
+ Certificate obj)
+ {
+ this.type = CERTIFICATE;
+ this.alias = alias;
+ this.obj = obj;
+ this.certChain = null;
+ }
+
+ StoreEntry(
+ String alias,
+ byte[] obj,
+ Certificate[] certChain)
+ {
+ this.type = SECRET;
+ this.alias = alias;
+ this.obj = obj;
+ this.certChain = certChain;
+ }
+
+ StoreEntry(
+ String alias,
+ Key key,
+ char[] password,
+ Certificate[] certChain)
+ throws Exception
+ {
+ this.type = SEALED;
+ this.alias = alias;
+ this.certChain = certChain;
+
+ byte[] salt = new byte[KEY_SALT_SIZE];
+
+ random.setSeed(System.currentTimeMillis());
+ random.nextBytes(salt);
+
+ int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
+
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DataOutputStream dOut = new DataOutputStream(bOut);
+
+ dOut.writeInt(salt.length);
+ dOut.write(salt);
+ dOut.writeInt(iterationCount);
+
+ Cipher cipher = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
+ CipherOutputStream cOut = new CipherOutputStream(dOut, cipher);
+
+ dOut = new DataOutputStream(cOut);
+
+ encodeKey(key, dOut);
+
+ dOut.close();
+
+ obj = bOut.toByteArray();
+ }
+
+ StoreEntry(
+ String alias,
+ Date date,
+ int type,
+ Object obj)
+ {
+ this.alias = alias;
+ this.date = date;
+ this.type = type;
+ this.obj = obj;
+ }
+
+ StoreEntry(
+ String alias,
+ Date date,
+ int type,
+ Object obj,
+ Certificate[] certChain)
+ {
+ this.alias = alias;
+ this.date = date;
+ this.type = type;
+ this.obj = obj;
+ this.certChain = certChain;
+ }
+
+ int getType()
+ {
+ return type;
+ }
+
+ String getAlias()
+ {
+ return alias;
+ }
+
+ Object getObject()
+ {
+ return obj;
+ }
+
+ Object getObject(
+ char[] password)
+ throws NoSuchAlgorithmException, UnrecoverableKeyException
+ {
+ if (password == null || password.length == 0)
+ {
+ if (obj instanceof Key)
+ {
+ return obj;
+ }
+ }
+
+ if (type == SEALED)
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream((byte[])obj);
+ DataInputStream dIn = new DataInputStream(bIn);
+
+ try
+ {
+ byte[] salt = new byte[dIn.readInt()];
+
+ dIn.readFully(salt);
+
+ int iterationCount = dIn.readInt();
+
+ Cipher cipher = makePBECipher(KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+
+ CipherInputStream cIn = new CipherInputStream(dIn, cipher);
+
+ try
+ {
+ return decodeKey(new DataInputStream(cIn));
+ }
+ catch (Exception x)
+ {
+ bIn = new ByteArrayInputStream((byte[])obj);
+ dIn = new DataInputStream(bIn);
+
+ salt = new byte[dIn.readInt()];
+
+ dIn.readFully(salt);
+
+ iterationCount = dIn.readInt();
+
+ cipher = makePBECipher("Broken" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+
+ cIn = new CipherInputStream(dIn, cipher);
+
+ Key k = null;
+
+ try
+ {
+ k = decodeKey(new DataInputStream(cIn));
+ }
+ catch (Exception y)
+ {
+ bIn = new ByteArrayInputStream((byte[])obj);
+ dIn = new DataInputStream(bIn);
+
+ salt = new byte[dIn.readInt()];
+
+ dIn.readFully(salt);
+
+ iterationCount = dIn.readInt();
+
+ cipher = makePBECipher("Old" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+
+ cIn = new CipherInputStream(dIn, cipher);
+
+ k = decodeKey(new DataInputStream(cIn));
+ }
+
+ //
+ // reencrypt key with correct cipher.
+ //
+ if (k != null)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DataOutputStream dOut = new DataOutputStream(bOut);
+
+ dOut.writeInt(salt.length);
+ dOut.write(salt);
+ dOut.writeInt(iterationCount);
+
+ Cipher out = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
+ CipherOutputStream cOut = new CipherOutputStream(dOut, out);
+
+ dOut = new DataOutputStream(cOut);
+
+ encodeKey(k, dOut);
+
+ dOut.close();
+
+ obj = bOut.toByteArray();
+
+ return k;
+ }
+ else
+ {
+ throw new UnrecoverableKeyException("no match");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new UnrecoverableKeyException("no match");
+ }
+ }
+ else
+ {
+ throw new RuntimeException("forget something!");
+ // TODO
+ // if we get to here key was saved as byte data, which
+ // according to the docs means it must be a private key
+ // in EncryptedPrivateKeyInfo (PKCS8 format), later...
+ //
+ }
+ }
+
+ Certificate[] getCertificateChain()
+ {
+ return certChain;
+ }
+
+ Date getDate()
+ {
+ return date;
+ }
+ }
+
+ private void encodeCertificate(
+ Certificate cert,
+ DataOutputStream dOut)
+ throws IOException
+ {
+ try
+ {
+ byte[] cEnc = cert.getEncoded();
+
+ dOut.writeUTF(cert.getType());
+ dOut.writeInt(cEnc.length);
+ dOut.write(cEnc);
+ }
+ catch (CertificateEncodingException ex)
+ {
+ throw new IOException(ex.toString());
+ }
+ }
+
+ private Certificate decodeCertificate(
+ DataInputStream dIn)
+ throws IOException
+ {
+ String type = dIn.readUTF();
+ byte[] cEnc = new byte[dIn.readInt()];
+
+ dIn.readFully(cEnc);
+
+ try
+ {
+ CertificateFactory cFact = CertificateFactory.getInstance(type, BouncyCastleProvider.PROVIDER_NAME);
+ ByteArrayInputStream bIn = new ByteArrayInputStream(cEnc);
+
+ return cFact.generateCertificate(bIn);
+ }
+ catch (NoSuchProviderException ex)
+ {
+ throw new IOException(ex.toString());
+ }
+ catch (CertificateException ex)
+ {
+ throw new IOException(ex.toString());
+ }
+ }
+
+ private void encodeKey(
+ Key key,
+ DataOutputStream dOut)
+ throws IOException
+ {
+ byte[] enc = key.getEncoded();
+
+ if (key instanceof PrivateKey)
+ {
+ dOut.write(KEY_PRIVATE);
+ }
+ else if (key instanceof PublicKey)
+ {
+ dOut.write(KEY_PUBLIC);
+ }
+ else
+ {
+ dOut.write(KEY_SECRET);
+ }
+
+ dOut.writeUTF(key.getFormat());
+ dOut.writeUTF(key.getAlgorithm());
+ dOut.writeInt(enc.length);
+ dOut.write(enc);
+ }
+
+ private Key decodeKey(
+ DataInputStream dIn)
+ throws IOException
+ {
+ int keyType = dIn.read();
+ String format = dIn.readUTF();
+ String algorithm = dIn.readUTF();
+ byte[] enc = new byte[dIn.readInt()];
+ KeySpec spec;
+
+ dIn.readFully(enc);
+
+ if (format.equals("PKCS#8") || format.equals("PKCS8"))
+ {
+ spec = new PKCS8EncodedKeySpec(enc);
+ }
+ else if (format.equals("X.509") || format.equals("X509"))
+ {
+ spec = new X509EncodedKeySpec(enc);
+ }
+ else if (format.equals("RAW"))
+ {
+ return new SecretKeySpec(enc, algorithm);
+ }
+ else
+ {
+ throw new IOException("Key format " + format + " not recognised!");
+ }
+
+ try
+ {
+ switch (keyType)
+ {
+ case KEY_PRIVATE:
+ return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePrivate(spec);
+ case KEY_PUBLIC:
+ return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePublic(spec);
+ case KEY_SECRET:
+ return SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generateSecret(spec);
+ default:
+ throw new IOException("Key type " + keyType + " not recognised!");
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Exception creating key: " + e.toString());
+ }
+ }
+
+ protected Cipher makePBECipher(
+ String algorithm,
+ int mode,
+ char[] password,
+ byte[] salt,
+ int iterationCount)
+ throws IOException
+ {
+ try
+ {
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
+ PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount);
+
+ Cipher cipher = Cipher.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
+
+ cipher.init(mode, keyFact.generateSecret(pbeSpec), defParams);
+
+ return cipher;
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error initialising store of key store: " + e);
+ }
+ }
+
+ public void setRandom(
+ SecureRandom rand)
+ {
+ this.random = rand;
+ }
+
+ public Enumeration engineAliases()
+ {
+ return table.keys();
+ }
+
+ public boolean engineContainsAlias(
+ String alias)
+ {
+ return (table.get(alias) != null);
+ }
+
+ public void engineDeleteEntry(
+ String alias)
+ throws KeyStoreException
+ {
+ Object entry = table.get(alias);
+
+ if (entry == null)
+ {
+ // BEGIN android-removed
+ // Only throw if there is a problem removing, not if missing
+ // throw new KeyStoreException("no such entry as " + alias);
+ // END android-removed
+ // BEGIN android-added
+ return;
+ // END android-added
+ }
+
+ table.remove(alias);
+ }
+
+ public Certificate engineGetCertificate(
+ String alias)
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry != null)
+ {
+ if (entry.getType() == CERTIFICATE)
+ {
+ return (Certificate)entry.getObject();
+ }
+ else
+ {
+ Certificate[] chain = entry.getCertificateChain();
+
+ if (chain != null)
+ {
+ return chain[0];
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public String engineGetCertificateAlias(
+ Certificate cert)
+ {
+ Enumeration e = table.elements();
+ while (e.hasMoreElements())
+ {
+ StoreEntry entry = (StoreEntry)e.nextElement();
+
+ if (entry.getObject() instanceof Certificate)
+ {
+ Certificate c = (Certificate)entry.getObject();
+
+ if (c.equals(cert))
+ {
+ return entry.getAlias();
+ }
+ }
+ else
+ {
+ Certificate[] chain = entry.getCertificateChain();
+
+ if (chain != null && chain[0].equals(cert))
+ {
+ return entry.getAlias();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Certificate[] engineGetCertificateChain(
+ String alias)
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry != null)
+ {
+ return entry.getCertificateChain();
+ }
+
+ return null;
+ }
+
+ public Date engineGetCreationDate(String alias)
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry != null)
+ {
+ return entry.getDate();
+ }
+
+ return null;
+ }
+
+ public Key engineGetKey(
+ String alias,
+ char[] password)
+ throws NoSuchAlgorithmException, UnrecoverableKeyException
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry == null || entry.getType() == CERTIFICATE)
+ {
+ return null;
+ }
+
+ return (Key)entry.getObject(password);
+ }
+
+ public boolean engineIsCertificateEntry(
+ String alias)
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry != null && entry.getType() == CERTIFICATE)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean engineIsKeyEntry(
+ String alias)
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry != null && entry.getType() != CERTIFICATE)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public void engineSetCertificateEntry(
+ String alias,
+ Certificate cert)
+ throws KeyStoreException
+ {
+ StoreEntry entry = (StoreEntry)table.get(alias);
+
+ if (entry != null && entry.getType() != CERTIFICATE)
+ {
+ throw new KeyStoreException("key store already has a key entry with alias " + alias);
+ }
+
+ table.put(alias, new StoreEntry(alias, cert));
+ }
+
+ public void engineSetKeyEntry(
+ String alias,
+ byte[] key,
+ Certificate[] chain)
+ throws KeyStoreException
+ {
+ table.put(alias, new StoreEntry(alias, key, chain));
+ }
+
+ public void engineSetKeyEntry(
+ String alias,
+ Key key,
+ char[] password,
+ Certificate[] chain)
+ throws KeyStoreException
+ {
+ if ((key instanceof PrivateKey) && (chain == null))
+ {
+ throw new KeyStoreException("no certificate chain for private key");
+ }
+
+ try
+ {
+ table.put(alias, new StoreEntry(alias, key, password, chain));
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreException(e.toString());
+ }
+ }
+
+ public int engineSize()
+ {
+ return table.size();
+ }
+
+ protected void loadStore(
+ InputStream in)
+ throws IOException
+ {
+ DataInputStream dIn = new DataInputStream(in);
+ int type = dIn.read();
+
+ while (type > NULL)
+ {
+ String alias = dIn.readUTF();
+ Date date = new Date(dIn.readLong());
+ int chainLength = dIn.readInt();
+ Certificate[] chain = null;
+
+ if (chainLength != 0)
+ {
+ chain = new Certificate[chainLength];
+
+ for (int i = 0; i != chainLength; i++)
+ {
+ chain[i] = decodeCertificate(dIn);
+ }
+ }
+
+ switch (type)
+ {
+ case CERTIFICATE:
+ Certificate cert = decodeCertificate(dIn);
+
+ table.put(alias, new StoreEntry(alias, date, CERTIFICATE, cert));
+ break;
+ case KEY:
+ Key key = decodeKey(dIn);
+ table.put(alias, new StoreEntry(alias, date, KEY, key, chain));
+ break;
+ case SECRET:
+ case SEALED:
+ byte[] b = new byte[dIn.readInt()];
+
+ dIn.readFully(b);
+ table.put(alias, new StoreEntry(alias, date, type, b, chain));
+ break;
+ default:
+ throw new RuntimeException("Unknown object type in store.");
+ }
+
+ type = dIn.read();
+ }
+ }
+
+ protected void saveStore(
+ OutputStream out)
+ throws IOException
+ {
+ Enumeration e = table.elements();
+ DataOutputStream dOut = new DataOutputStream(out);
+
+ while (e.hasMoreElements())
+ {
+ StoreEntry entry = (StoreEntry)e.nextElement();
+
+ dOut.write(entry.getType());
+ dOut.writeUTF(entry.getAlias());
+ dOut.writeLong(entry.getDate().getTime());
+
+ Certificate[] chain = entry.getCertificateChain();
+ if (chain == null)
+ {
+ dOut.writeInt(0);
+ }
+ else
+ {
+ dOut.writeInt(chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ encodeCertificate(chain[i], dOut);
+ }
+ }
+
+ switch (entry.getType())
+ {
+ case CERTIFICATE:
+ encodeCertificate((Certificate)entry.getObject(), dOut);
+ break;
+ case KEY:
+ encodeKey((Key)entry.getObject(), dOut);
+ break;
+ case SEALED:
+ case SECRET:
+ byte[] b = (byte[])entry.getObject();
+
+ dOut.writeInt(b.length);
+ dOut.write(b);
+ break;
+ default:
+ throw new RuntimeException("Unknown object type in store.");
+ }
+ }
+
+ dOut.write(NULL);
+ }
+
+ public void engineLoad(
+ InputStream stream,
+ char[] password)
+ throws IOException
+ {
+ table.clear();
+
+ if (stream == null) // just initialising
+ {
+ return;
+ }
+
+ DataInputStream dIn = new DataInputStream(stream);
+ int version = dIn.readInt();
+
+ if (version != STORE_VERSION)
+ {
+ if (version != 0 && version != 1)
+ {
+ throw new IOException("Wrong version of key store.");
+ }
+ }
+
+ int saltLength = dIn.readInt();
+ if (saltLength <= 0)
+ {
+ throw new IOException("Invalid salt detected");
+ }
+
+ byte[] salt = new byte[saltLength];
+
+ dIn.readFully(salt);
+
+ int iterationCount = dIn.readInt();
+
+ //
+ // we only do an integrity check if the password is provided.
+ //
+ // BEGIN android-changed
+ HMac hMac = new HMac(AndroidDigestFactory.getSHA1());
+ // END android-changed
+ if (password != null && password.length != 0)
+ {
+ byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
+
+ // BEGIN android-changed
+ PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
+ // END android-changed
+ pbeGen.init(passKey, salt, iterationCount);
+
+ CipherParameters macParams;
+
+ if (version != 2)
+ {
+ macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize());
+ }
+ else
+ {
+ macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize() * 8);
+ }
+
+ Arrays.fill(passKey, (byte)0);
+
+ hMac.init(macParams);
+ MacInputStream mIn = new MacInputStream(dIn, hMac);
+
+ loadStore(mIn);
+
+ // Finalise our mac calculation
+ byte[] mac = new byte[hMac.getMacSize()];
+ hMac.doFinal(mac, 0);
+
+ // TODO Should this actually be reading the remainder of the stream?
+ // Read the original mac from the stream
+ byte[] oldMac = new byte[hMac.getMacSize()];
+ dIn.readFully(oldMac);
+
+ if (!Arrays.constantTimeAreEqual(mac, oldMac))
+ {
+ table.clear();
+ throw new IOException("KeyStore integrity check failed.");
+ }
+ }
+ else
+ {
+ loadStore(dIn);
+
+ // TODO Should this actually be reading the remainder of the stream?
+ // Parse the original mac from the stream too
+ byte[] oldMac = new byte[hMac.getMacSize()];
+ dIn.readFully(oldMac);
+ }
+ }
+
+
+ public void engineStore(OutputStream stream, char[] password)
+ throws IOException
+ {
+ DataOutputStream dOut = new DataOutputStream(stream);
+ byte[] salt = new byte[STORE_SALT_SIZE];
+ int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
+
+ random.nextBytes(salt);
+
+ dOut.writeInt(STORE_VERSION);
+ dOut.writeInt(salt.length);
+ dOut.write(salt);
+ dOut.writeInt(iterationCount);
+
+ // BEGIN android-changed
+ HMac hMac = new HMac(AndroidDigestFactory.getSHA1());
+ MacOutputStream mOut = new MacOutputStream(hMac);
+ PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
+ // END android-changed
+ byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
+
+ pbeGen.init(passKey, salt, iterationCount);
+
+ hMac.init(pbeGen.generateDerivedMacParameters(hMac.getMacSize() * 8));
+
+ for (int i = 0; i != passKey.length; i++)
+ {
+ passKey[i] = 0;
+ }
+
+ saveStore(new TeeOutputStream(dOut, mOut));
+
+ byte[] mac = new byte[hMac.getMacSize()];
+
+ hMac.doFinal(mac, 0);
+
+ dOut.write(mac);
+
+ dOut.close();
+ }
+
+ /**
+ * the BouncyCastle store. This wont work with the key tool as the
+ * store is stored encrypted on disk, so the password is mandatory,
+ * however if you hard drive is in a bad part of town and you absolutely,
+ * positively, don't want nobody peeking at your things, this is the
+ * one to use, no problem! After all in a Bouncy Castle nothing can
+ * touch you.
+ *
+ * Also referred to by the alias UBER.
+ */
+ public static class BouncyCastleStore
+ extends JDKKeyStore
+ {
+ public void engineLoad(
+ InputStream stream,
+ char[] password)
+ throws IOException
+ {
+ table.clear();
+
+ if (stream == null) // just initialising
+ {
+ return;
+ }
+
+ DataInputStream dIn = new DataInputStream(stream);
+ int version = dIn.readInt();
+
+ if (version != STORE_VERSION)
+ {
+ if (version != 0 && version != 1)
+ {
+ throw new IOException("Wrong version of key store.");
+ }
+ }
+
+ byte[] salt = new byte[dIn.readInt()];
+
+ if (salt.length != STORE_SALT_SIZE)
+ {
+ throw new IOException("Key store corrupted.");
+ }
+
+ dIn.readFully(salt);
+
+ int iterationCount = dIn.readInt();
+
+ if ((iterationCount < 0) || (iterationCount > 4 * MIN_ITERATIONS))
+ {
+ throw new IOException("Key store corrupted.");
+ }
+
+ String cipherAlg;
+ if (version == 0)
+ {
+ cipherAlg = "Old" + STORE_CIPHER;
+ }
+ else
+ {
+ cipherAlg = STORE_CIPHER;
+ }
+
+ Cipher cipher = this.makePBECipher(cipherAlg, Cipher.DECRYPT_MODE, password, salt, iterationCount);
+ CipherInputStream cIn = new CipherInputStream(dIn, cipher);
+
+ // BEGIN android-changed
+ Digest dig = AndroidDigestFactory.getSHA1();
+ // END android-changed
+ DigestInputStream dgIn = new DigestInputStream(cIn, dig);
+
+ this.loadStore(dgIn);
+
+ // Finalise our digest calculation
+ byte[] hash = new byte[dig.getDigestSize()];
+ dig.doFinal(hash, 0);
+
+ // TODO Should this actually be reading the remainder of the stream?
+ // Read the original digest from the stream
+ byte[] oldHash = new byte[dig.getDigestSize()];
+ Streams.readFully(cIn, oldHash);
+
+ if (!Arrays.constantTimeAreEqual(hash, oldHash))
+ {
+ table.clear();
+ throw new IOException("KeyStore integrity check failed.");
+ }
+ }
+
+ public void engineStore(OutputStream stream, char[] password)
+ throws IOException
+ {
+ Cipher cipher;
+ DataOutputStream dOut = new DataOutputStream(stream);
+ byte[] salt = new byte[STORE_SALT_SIZE];
+ int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
+
+ random.nextBytes(salt);
+
+ dOut.writeInt(STORE_VERSION);
+ dOut.writeInt(salt.length);
+ dOut.write(salt);
+ dOut.writeInt(iterationCount);
+
+ cipher = this.makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
+
+ CipherOutputStream cOut = new CipherOutputStream(dOut, cipher);
+ // BEGIN android-changed
+ DigestOutputStream dgOut = new DigestOutputStream(AndroidDigestFactory.getSHA1());
+ // END android-changed
+
+ this.saveStore(new TeeOutputStream(cOut, dgOut));
+
+ byte[] dig = dgOut.getDigest();
+
+ cOut.write(dig);
+
+ cOut.close();
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java
new file mode 100644
index 0000000..2d9f683
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12KeyStore.java
@@ -0,0 +1,1658 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStore.LoadStoreParameter;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.BERConstructedOctetString;
+import org.bouncycastle.asn1.BEROutputStream;
+import org.bouncycastle.asn1.DERBMPString;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.pkcs.AuthenticatedSafe;
+import org.bouncycastle.asn1.pkcs.CertBag;
+import org.bouncycastle.asn1.pkcs.ContentInfo;
+import org.bouncycastle.asn1.pkcs.EncryptedData;
+import org.bouncycastle.asn1.pkcs.MacData;
+import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.Pfx;
+import org.bouncycastle.asn1.pkcs.SafeBag;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.bouncycastle.jce.interfaces.BCKeyStore;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+public class JDKPKCS12KeyStore
+ extends KeyStoreSpi
+ implements PKCSObjectIdentifiers, X509ObjectIdentifiers, BCKeyStore
+{
+ private static final int SALT_SIZE = 20;
+ private static final int MIN_ITERATIONS = 1024;
+
+ private static final Provider bcProvider = new BouncyCastleProvider();
+
+ private IgnoresCaseHashtable keys = new IgnoresCaseHashtable();
+ private Hashtable localIds = new Hashtable();
+ private IgnoresCaseHashtable certs = new IgnoresCaseHashtable();
+ private Hashtable chainCerts = new Hashtable();
+ private Hashtable keyCerts = new Hashtable();
+
+ //
+ // generic object types
+ //
+ static final int NULL = 0;
+ static final int CERTIFICATE = 1;
+ static final int KEY = 2;
+ static final int SECRET = 3;
+ static final int SEALED = 4;
+
+ //
+ // key types
+ //
+ static final int KEY_PRIVATE = 0;
+ static final int KEY_PUBLIC = 1;
+ static final int KEY_SECRET = 2;
+
+ protected SecureRandom random = new SecureRandom();
+
+ // use of final causes problems with JDK 1.2 compiler
+ private CertificateFactory certFact;
+ private ASN1ObjectIdentifier keyAlgorithm;
+ private ASN1ObjectIdentifier certAlgorithm;
+
+ private class CertId
+ {
+ byte[] id;
+
+ CertId(
+ PublicKey key)
+ {
+ this.id = createSubjectKeyId(key).getKeyIdentifier();
+ }
+
+ CertId(
+ byte[] id)
+ {
+ this.id = id;
+ }
+
+ public int hashCode()
+ {
+ return Arrays.hashCode(id);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof CertId))
+ {
+ return false;
+ }
+
+ CertId cId = (CertId)o;
+
+ return Arrays.areEqual(id, cId.id);
+ }
+ }
+
+ public JDKPKCS12KeyStore(
+ Provider provider,
+ ASN1ObjectIdentifier keyAlgorithm,
+ ASN1ObjectIdentifier certAlgorithm)
+ {
+ this.keyAlgorithm = keyAlgorithm;
+ this.certAlgorithm = certAlgorithm;
+
+ try
+ {
+ if (provider != null)
+ {
+ certFact = CertificateFactory.getInstance("X.509", provider);
+ }
+ else
+ {
+ certFact = CertificateFactory.getInstance("X.509");
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("can't create cert factory - " + e.toString());
+ }
+ }
+
+ private SubjectKeyIdentifier createSubjectKeyId(
+ PublicKey pubKey)
+ {
+ try
+ {
+ SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
+ (ASN1Sequence) ASN1Primitive.fromByteArray(pubKey.getEncoded()));
+
+ return new SubjectKeyIdentifier(info);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("error creating key");
+ }
+ }
+
+ public void setRandom(
+ SecureRandom rand)
+ {
+ this.random = rand;
+ }
+
+ public Enumeration engineAliases()
+ {
+ Hashtable tab = new Hashtable();
+
+ Enumeration e = certs.keys();
+ while (e.hasMoreElements())
+ {
+ tab.put(e.nextElement(), "cert");
+ }
+
+ e = keys.keys();
+ while (e.hasMoreElements())
+ {
+ String a = (String)e.nextElement();
+ if (tab.get(a) == null)
+ {
+ tab.put(a, "key");
+ }
+ }
+
+ return tab.keys();
+ }
+
+ public boolean engineContainsAlias(
+ String alias)
+ {
+ return (certs.get(alias) != null || keys.get(alias) != null);
+ }
+
+ /**
+ * this is not quite complete - we should follow up on the chain, a bit
+ * tricky if a certificate appears in more than one chain...
+ */
+ public void engineDeleteEntry(
+ String alias)
+ throws KeyStoreException
+ {
+ Key k = (Key)keys.remove(alias);
+
+ Certificate c = (Certificate)certs.remove(alias);
+
+ if (c != null)
+ {
+ chainCerts.remove(new CertId(c.getPublicKey()));
+ }
+
+ if (k != null)
+ {
+ String id = (String)localIds.remove(alias);
+ if (id != null)
+ {
+ c = (Certificate)keyCerts.remove(id);
+ }
+ if (c != null)
+ {
+ chainCerts.remove(new CertId(c.getPublicKey()));
+ }
+ }
+
+ // BEGIN android-removed
+ // Only throw if there is a problem removing, not if missing
+ // if (c == null && k == null)
+ // {
+ // throw new KeyStoreException("no such entry as " + alias);
+ // }
+ // END android-removed
+ }
+
+ /**
+ * simply return the cert for the private key
+ */
+ public Certificate engineGetCertificate(
+ String alias)
+ {
+ if (alias == null)
+ {
+ throw new IllegalArgumentException("null alias passed to getCertificate.");
+ }
+
+ Certificate c = (Certificate)certs.get(alias);
+
+ //
+ // look up the key table - and try the local key id
+ //
+ if (c == null)
+ {
+ String id = (String)localIds.get(alias);
+ if (id != null)
+ {
+ c = (Certificate)keyCerts.get(id);
+ }
+ else
+ {
+ c = (Certificate)keyCerts.get(alias);
+ }
+ }
+
+ return c;
+ }
+
+ public String engineGetCertificateAlias(
+ Certificate cert)
+ {
+ Enumeration c = certs.elements();
+ Enumeration k = certs.keys();
+
+ while (c.hasMoreElements())
+ {
+ Certificate tc = (Certificate)c.nextElement();
+ String ta = (String)k.nextElement();
+
+ if (tc.equals(cert))
+ {
+ return ta;
+ }
+ }
+
+ c = keyCerts.elements();
+ k = keyCerts.keys();
+
+ while (c.hasMoreElements())
+ {
+ Certificate tc = (Certificate)c.nextElement();
+ String ta = (String)k.nextElement();
+
+ if (tc.equals(cert))
+ {
+ return ta;
+ }
+ }
+
+ return null;
+ }
+
+ public Certificate[] engineGetCertificateChain(
+ String alias)
+ {
+ if (alias == null)
+ {
+ throw new IllegalArgumentException("null alias passed to getCertificateChain.");
+ }
+
+ if (!engineIsKeyEntry(alias))
+ {
+ return null;
+ }
+
+ Certificate c = engineGetCertificate(alias);
+
+ if (c != null)
+ {
+ Vector cs = new Vector();
+
+ while (c != null)
+ {
+ X509Certificate x509c = (X509Certificate)c;
+ Certificate nextC = null;
+
+ byte[] bytes = x509c.getExtensionValue(X509Extensions.AuthorityKeyIdentifier.getId());
+ if (bytes != null)
+ {
+ try
+ {
+ ASN1InputStream aIn = new ASN1InputStream(bytes);
+
+ byte[] authBytes = ((ASN1OctetString)aIn.readObject()).getOctets();
+ aIn = new ASN1InputStream(authBytes);
+
+ AuthorityKeyIdentifier id = AuthorityKeyIdentifier.getInstance((ASN1Sequence)aIn.readObject());
+ if (id.getKeyIdentifier() != null)
+ {
+ nextC = (Certificate)chainCerts.get(new CertId(id.getKeyIdentifier()));
+ }
+
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ if (nextC == null)
+ {
+ //
+ // no authority key id, try the Issuer DN
+ //
+ Principal i = x509c.getIssuerDN();
+ Principal s = x509c.getSubjectDN();
+
+ if (!i.equals(s))
+ {
+ Enumeration e = chainCerts.keys();
+
+ while (e.hasMoreElements())
+ {
+ X509Certificate crt = (X509Certificate)chainCerts.get(e.nextElement());
+ Principal sub = crt.getSubjectDN();
+ if (sub.equals(i))
+ {
+ try
+ {
+ x509c.verify(crt.getPublicKey());
+ nextC = crt;
+ break;
+ }
+ catch (Exception ex)
+ {
+ // continue
+ }
+ }
+ }
+ }
+ }
+
+ cs.addElement(c);
+ if (nextC != c) // self signed - end of the chain
+ {
+ c = nextC;
+ }
+ else
+ {
+ c = null;
+ }
+ }
+
+ Certificate[] certChain = new Certificate[cs.size()];
+
+ for (int i = 0; i != certChain.length; i++)
+ {
+ certChain[i] = (Certificate)cs.elementAt(i);
+ }
+
+ return certChain;
+ }
+
+ return null;
+ }
+
+ public Date engineGetCreationDate(String alias)
+ {
+ // BEGIN android-added
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+ if (keys.get(alias) == null && certs.get(alias) == null) {
+ return null;
+ }
+ // END android-added
+ return new Date();
+ }
+
+ public Key engineGetKey(
+ String alias,
+ char[] password)
+ throws NoSuchAlgorithmException, UnrecoverableKeyException
+ {
+ if (alias == null)
+ {
+ throw new IllegalArgumentException("null alias passed to getKey.");
+ }
+
+ return (Key)keys.get(alias);
+ }
+
+ public boolean engineIsCertificateEntry(
+ String alias)
+ {
+ return (certs.get(alias) != null && keys.get(alias) == null);
+ }
+
+ public boolean engineIsKeyEntry(
+ String alias)
+ {
+ return (keys.get(alias) != null);
+ }
+
+ public void engineSetCertificateEntry(
+ String alias,
+ Certificate cert)
+ throws KeyStoreException
+ {
+ if (keys.get(alias) != null)
+ {
+ throw new KeyStoreException("There is a key entry with the name " + alias + ".");
+ }
+
+ certs.put(alias, cert);
+ chainCerts.put(new CertId(cert.getPublicKey()), cert);
+ }
+
+ public void engineSetKeyEntry(
+ String alias,
+ byte[] key,
+ Certificate[] chain)
+ throws KeyStoreException
+ {
+ throw new RuntimeException("operation not supported");
+ }
+
+ public void engineSetKeyEntry(
+ String alias,
+ Key key,
+ char[] password,
+ Certificate[] chain)
+ throws KeyStoreException
+ {
+ // BEGIN android-added
+ if (!(key instanceof PrivateKey)) {
+ throw new KeyStoreException("PKCS12 does not support non-PrivateKeys");
+ }
+ // END android-added
+ if ((key instanceof PrivateKey) && (chain == null))
+ {
+ throw new KeyStoreException("no certificate chain for private key");
+ }
+
+ if (keys.get(alias) != null)
+ {
+ engineDeleteEntry(alias);
+ }
+
+ keys.put(alias, key);
+ // BEGIN android-added
+ if (chain != null) {
+ // END android-added
+ certs.put(alias, chain[0]);
+
+ for (int i = 0; i != chain.length; i++)
+ {
+ chainCerts.put(new CertId(chain[i].getPublicKey()), chain[i]);
+ }
+ // BEGIN android-added
+ }
+ // END android-added
+ }
+
+ public int engineSize()
+ {
+ Hashtable tab = new Hashtable();
+
+ Enumeration e = certs.keys();
+ while (e.hasMoreElements())
+ {
+ tab.put(e.nextElement(), "cert");
+ }
+
+ e = keys.keys();
+ while (e.hasMoreElements())
+ {
+ String a = (String)e.nextElement();
+ if (tab.get(a) == null)
+ {
+ tab.put(a, "key");
+ }
+ }
+
+ return tab.size();
+ }
+
+ protected PrivateKey unwrapKey(
+ AlgorithmIdentifier algId,
+ byte[] data,
+ char[] password,
+ boolean wrongPKCS12Zero)
+ throws IOException
+ {
+ String algorithm = algId.getAlgorithm().getId();
+ PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters());
+
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ PrivateKey out;
+
+ try
+ {
+ SecretKeyFactory keyFact = SecretKeyFactory.getInstance(
+ algorithm, bcProvider);
+ PBEParameterSpec defParams = new PBEParameterSpec(
+ pbeParams.getIV(),
+ pbeParams.getIterations().intValue());
+
+ SecretKey k = keyFact.generateSecret(pbeSpec);
+
+ ((BCPBEKey)k).setTryWrongPKCS12Zero(wrongPKCS12Zero);
+
+ Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
+
+ cipher.init(Cipher.UNWRAP_MODE, k, defParams);
+
+ // we pass "" as the key algorithm type as it is unknown at this point
+ out = (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("exception unwrapping private key - " + e.toString());
+ }
+
+ return out;
+ }
+
+ protected byte[] wrapKey(
+ String algorithm,
+ Key key,
+ PKCS12PBEParams pbeParams,
+ char[] password)
+ throws IOException
+ {
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ byte[] out;
+
+ try
+ {
+ SecretKeyFactory keyFact = SecretKeyFactory.getInstance(
+ algorithm, bcProvider);
+ PBEParameterSpec defParams = new PBEParameterSpec(
+ pbeParams.getIV(),
+ pbeParams.getIterations().intValue());
+
+ Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
+
+ cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), defParams);
+
+ out = cipher.wrap(key);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("exception encrypting data - " + e.toString());
+ }
+
+ return out;
+ }
+
+ protected byte[] cryptData(
+ boolean forEncryption,
+ AlgorithmIdentifier algId,
+ char[] password,
+ boolean wrongPKCS12Zero,
+ byte[] data)
+ throws IOException
+ {
+ String algorithm = algId.getAlgorithm().getId();
+ PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters());
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+
+ try
+ {
+ SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, bcProvider);
+ PBEParameterSpec defParams = new PBEParameterSpec(
+ pbeParams.getIV(),
+ pbeParams.getIterations().intValue());
+ BCPBEKey key = (BCPBEKey) keyFact.generateSecret(pbeSpec);
+
+ key.setTryWrongPKCS12Zero(wrongPKCS12Zero);
+
+ Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
+ int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+ cipher.init(mode, key, defParams);
+ return cipher.doFinal(data);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("exception decrypting data - " + e.toString());
+ }
+ }
+
+ public void engineLoad(
+ InputStream stream,
+ char[] password)
+ throws IOException
+ {
+ if (stream == null) // just initialising
+ {
+ return;
+ }
+
+ if (password == null)
+ {
+ throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
+ }
+
+ BufferedInputStream bufIn = new BufferedInputStream(stream);
+
+ bufIn.mark(10);
+
+ int head = bufIn.read();
+
+ if (head != 0x30)
+ {
+ throw new IOException("stream does not represent a PKCS12 key store");
+ }
+
+ bufIn.reset();
+
+ ASN1InputStream bIn = new ASN1InputStream(bufIn);
+ ASN1Sequence obj = (ASN1Sequence)bIn.readObject();
+ Pfx bag = Pfx.getInstance(obj);
+ ContentInfo info = bag.getAuthSafe();
+ Vector chain = new Vector();
+ boolean unmarkedKey = false;
+ boolean wrongPKCS12Zero = false;
+
+ if (bag.getMacData() != null) // check the mac code
+ {
+ MacData mData = bag.getMacData();
+ DigestInfo dInfo = mData.getMac();
+ AlgorithmIdentifier algId = dInfo.getAlgorithmId();
+ byte[] salt = mData.getSalt();
+ int itCount = mData.getIterationCount().intValue();
+
+ byte[] data = ((ASN1OctetString)info.getContent()).getOctets();
+
+ try
+ {
+ byte[] res = calculatePbeMac(algId.getObjectId(), salt, itCount, password, false, data);
+ byte[] dig = dInfo.getDigest();
+
+ if (!Arrays.constantTimeAreEqual(res, dig))
+ {
+ if (password.length > 0)
+ {
+ throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
+ }
+
+ // Try with incorrect zero length password
+ res = calculatePbeMac(algId.getObjectId(), salt, itCount, password, true, data);
+
+ if (!Arrays.constantTimeAreEqual(res, dig))
+ {
+ throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file.");
+ }
+
+ wrongPKCS12Zero = true;
+ }
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new IOException("error constructing MAC: " + e.toString());
+ }
+ }
+
+ keys = new IgnoresCaseHashtable();
+ localIds = new Hashtable();
+
+ if (info.getContentType().equals(data))
+ {
+ bIn = new ASN1InputStream(((ASN1OctetString)info.getContent()).getOctets());
+
+ AuthenticatedSafe authSafe = AuthenticatedSafe.getInstance(bIn.readObject());
+ ContentInfo[] c = authSafe.getContentInfo();
+
+ for (int i = 0; i != c.length; i++)
+ {
+ if (c[i].getContentType().equals(data))
+ {
+ ASN1InputStream dIn = new ASN1InputStream(((ASN1OctetString)c[i].getContent()).getOctets());
+ ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
+
+ for (int j = 0; j != seq.size(); j++)
+ {
+ SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
+ if (b.getBagId().equals(pkcs8ShroudedKeyBag))
+ {
+ org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
+ PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
+
+ //
+ // set the attributes on the key
+ //
+ PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+ String alias = null;
+ ASN1OctetString localId = null;
+
+ if (b.getBagAttributes() != null)
+ {
+ Enumeration e = b.getBagAttributes().getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+ ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+ ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
+ ASN1Primitive attr = null;
+
+ if (attrSet.size() > 0)
+ {
+ attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+ ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+ if (existing != null)
+ {
+ // OK, but the value has to be the same
+ if (!existing.toASN1Primitive().equals(attr))
+ {
+ throw new IOException(
+ "attempt to add existing attribute with different value");
+ }
+ }
+ else
+ {
+ bagAttr.setBagAttribute(aOid, attr);
+ }
+ }
+
+ if (aOid.equals(pkcs_9_at_friendlyName))
+ {
+ alias = ((DERBMPString)attr).getString();
+ keys.put(alias, privKey);
+ }
+ else if (aOid.equals(pkcs_9_at_localKeyId))
+ {
+ localId = (ASN1OctetString)attr;
+ }
+ }
+ }
+
+ if (localId != null)
+ {
+ String name = new String(Hex.encode(localId.getOctets()));
+
+ if (alias == null)
+ {
+ keys.put(name, privKey);
+ }
+ else
+ {
+ localIds.put(alias, name);
+ }
+ }
+ else
+ {
+ unmarkedKey = true;
+ keys.put("unmarked", privKey);
+ }
+ }
+ else if (b.getBagId().equals(certBag))
+ {
+ chain.addElement(b);
+ }
+ else
+ {
+ System.out.println("extra in data " + b.getBagId());
+ System.out.println(ASN1Dump.dumpAsString(b));
+ }
+ }
+ }
+ else if (c[i].getContentType().equals(encryptedData))
+ {
+ EncryptedData d = EncryptedData.getInstance(c[i].getContent());
+ byte[] octets = cryptData(false, d.getEncryptionAlgorithm(),
+ password, wrongPKCS12Zero, d.getContent().getOctets());
+ ASN1Sequence seq = (ASN1Sequence) ASN1Primitive.fromByteArray(octets);
+
+ for (int j = 0; j != seq.size(); j++)
+ {
+ SafeBag b = SafeBag.getInstance(seq.getObjectAt(j));
+
+ if (b.getBagId().equals(certBag))
+ {
+ chain.addElement(b);
+ }
+ else if (b.getBagId().equals(pkcs8ShroudedKeyBag))
+ {
+ org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue());
+ PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero);
+
+ //
+ // set the attributes on the key
+ //
+ PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+ String alias = null;
+ ASN1OctetString localId = null;
+
+ Enumeration e = b.getBagAttributes().getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+ ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+ ASN1Set attrSet= (ASN1Set)sq.getObjectAt(1);
+ ASN1Primitive attr = null;
+
+ if (attrSet.size() > 0)
+ {
+ attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+ ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+ if (existing != null)
+ {
+ // OK, but the value has to be the same
+ if (!existing.toASN1Primitive().equals(attr))
+ {
+ throw new IOException(
+ "attempt to add existing attribute with different value");
+ }
+ }
+ else
+ {
+ bagAttr.setBagAttribute(aOid, attr);
+ }
+ }
+
+ if (aOid.equals(pkcs_9_at_friendlyName))
+ {
+ alias = ((DERBMPString)attr).getString();
+ keys.put(alias, privKey);
+ }
+ else if (aOid.equals(pkcs_9_at_localKeyId))
+ {
+ localId = (ASN1OctetString)attr;
+ }
+ }
+
+ String name = new String(Hex.encode(localId.getOctets()));
+
+ if (alias == null)
+ {
+ keys.put(name, privKey);
+ }
+ else
+ {
+ localIds.put(alias, name);
+ }
+ }
+ else if (b.getBagId().equals(keyBag))
+ {
+ org.bouncycastle.asn1.pkcs.PrivateKeyInfo kInfo = new org.bouncycastle.asn1.pkcs.PrivateKeyInfo((ASN1Sequence)b.getBagValue());
+ PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo);
+
+ //
+ // set the attributes on the key
+ //
+ PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey;
+ String alias = null;
+ ASN1OctetString localId = null;
+
+ Enumeration e = b.getBagAttributes().getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+ ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+ ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1);
+ ASN1Primitive attr = null;
+
+ if (attrSet.size() > 0)
+ {
+ attr = (ASN1Primitive)attrSet.getObjectAt(0);
+
+ ASN1Encodable existing = bagAttr.getBagAttribute(aOid);
+ if (existing != null)
+ {
+ // OK, but the value has to be the same
+ if (!existing.toASN1Primitive().equals(attr))
+ {
+ throw new IOException(
+ "attempt to add existing attribute with different value");
+ }
+ }
+ else
+ {
+ bagAttr.setBagAttribute(aOid, attr);
+ }
+ }
+
+ if (aOid.equals(pkcs_9_at_friendlyName))
+ {
+ alias = ((DERBMPString)attr).getString();
+ keys.put(alias, privKey);
+ }
+ else if (aOid.equals(pkcs_9_at_localKeyId))
+ {
+ localId = (ASN1OctetString)attr;
+ }
+ }
+
+ String name = new String(Hex.encode(localId.getOctets()));
+
+ if (alias == null)
+ {
+ keys.put(name, privKey);
+ }
+ else
+ {
+ localIds.put(alias, name);
+ }
+ }
+ else
+ {
+ System.out.println("extra in encryptedData " + b.getBagId());
+ System.out.println(ASN1Dump.dumpAsString(b));
+ }
+ }
+ }
+ else
+ {
+ System.out.println("extra " + c[i].getContentType().getId());
+ System.out.println("extra " + ASN1Dump.dumpAsString(c[i].getContent()));
+ }
+ }
+ }
+
+ certs = new IgnoresCaseHashtable();
+ chainCerts = new Hashtable();
+ keyCerts = new Hashtable();
+
+ for (int i = 0; i != chain.size(); i++)
+ {
+ SafeBag b = (SafeBag)chain.elementAt(i);
+ CertBag cb = CertBag.getInstance(b.getBagValue());
+
+ if (!cb.getCertId().equals(x509Certificate))
+ {
+ throw new RuntimeException("Unsupported certificate type: " + cb.getCertId());
+ }
+
+ Certificate cert;
+
+ try
+ {
+ ByteArrayInputStream cIn = new ByteArrayInputStream(
+ ((ASN1OctetString)cb.getCertValue()).getOctets());
+ cert = certFact.generateCertificate(cIn);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+
+ //
+ // set the attributes
+ //
+ ASN1OctetString localId = null;
+ String alias = null;
+
+ if (b.getBagAttributes() != null)
+ {
+ Enumeration e = b.getBagAttributes().getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence sq = (ASN1Sequence)e.nextElement();
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)sq.getObjectAt(0);
+ ASN1Primitive attr = (ASN1Primitive)((ASN1Set)sq.getObjectAt(1)).getObjectAt(0);
+ PKCS12BagAttributeCarrier bagAttr = null;
+
+ if (cert instanceof PKCS12BagAttributeCarrier)
+ {
+ bagAttr = (PKCS12BagAttributeCarrier)cert;
+
+ ASN1Encodable existing = bagAttr.getBagAttribute(oid);
+ if (existing != null)
+ {
+ // OK, but the value has to be the same
+ if (!existing.toASN1Primitive().equals(attr))
+ {
+ throw new IOException(
+ "attempt to add existing attribute with different value");
+ }
+ }
+ else
+ {
+ bagAttr.setBagAttribute(oid, attr);
+ }
+ }
+
+ if (oid.equals(pkcs_9_at_friendlyName))
+ {
+ alias = ((DERBMPString)attr).getString();
+ }
+ else if (oid.equals(pkcs_9_at_localKeyId))
+ {
+ localId = (ASN1OctetString)attr;
+ }
+ }
+ }
+
+ chainCerts.put(new CertId(cert.getPublicKey()), cert);
+
+ if (unmarkedKey)
+ {
+ if (keyCerts.isEmpty())
+ {
+ String name = new String(Hex.encode(createSubjectKeyId(cert.getPublicKey()).getKeyIdentifier()));
+
+ keyCerts.put(name, cert);
+ keys.put(name, keys.remove("unmarked"));
+ }
+ }
+ else
+ {
+ //
+ // the local key id needs to override the friendly name
+ //
+ if (localId != null)
+ {
+ String name = new String(Hex.encode(localId.getOctets()));
+
+ keyCerts.put(name, cert);
+ }
+ if (alias != null)
+ {
+ certs.put(alias, cert);
+ }
+ }
+ }
+ }
+
+ public void engineStore(LoadStoreParameter param) throws IOException,
+ NoSuchAlgorithmException, CertificateException
+ {
+ if (param == null)
+ {
+ throw new IllegalArgumentException("'param' arg cannot be null");
+ }
+
+ if (!(param instanceof JDKPKCS12StoreParameter))
+ {
+ throw new IllegalArgumentException(
+ "No support for 'param' of type " + param.getClass().getName());
+ }
+
+ JDKPKCS12StoreParameter bcParam = (JDKPKCS12StoreParameter)param;
+
+ char[] password;
+ ProtectionParameter protParam = param.getProtectionParameter();
+ if (protParam == null)
+ {
+ password = null;
+ }
+ else if (protParam instanceof KeyStore.PasswordProtection)
+ {
+ password = ((KeyStore.PasswordProtection)protParam).getPassword();
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "No support for protection parameter of type " + protParam.getClass().getName());
+ }
+
+ doStore(bcParam.getOutputStream(), password, bcParam.isUseDEREncoding());
+ }
+
+ public void engineStore(OutputStream stream, char[] password)
+ throws IOException
+ {
+ doStore(stream, password, false);
+ }
+
+ private void doStore(OutputStream stream, char[] password, boolean useDEREncoding)
+ throws IOException
+ {
+ if (password == null)
+ {
+ throw new NullPointerException("No password supplied for PKCS#12 KeyStore.");
+ }
+
+ //
+ // handle the key
+ //
+ ASN1EncodableVector keyS = new ASN1EncodableVector();
+
+
+ Enumeration ks = keys.keys();
+
+ while (ks.hasMoreElements())
+ {
+ byte[] kSalt = new byte[SALT_SIZE];
+
+ random.nextBytes(kSalt);
+
+ String name = (String)ks.nextElement();
+ PrivateKey privKey = (PrivateKey)keys.get(name);
+ PKCS12PBEParams kParams = new PKCS12PBEParams(kSalt, MIN_ITERATIONS);
+ byte[] kBytes = wrapKey(keyAlgorithm.getId(), privKey, kParams, password);
+ AlgorithmIdentifier kAlgId = new AlgorithmIdentifier(keyAlgorithm, kParams.toASN1Primitive());
+ org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo kInfo = new org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo(kAlgId, kBytes);
+ boolean attrSet = false;
+ ASN1EncodableVector kName = new ASN1EncodableVector();
+
+ if (privKey instanceof PKCS12BagAttributeCarrier)
+ {
+ PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)privKey;
+ //
+ // make sure we are using the local alias on store
+ //
+ DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+ if (nm == null || !nm.getString().equals(name))
+ {
+ bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
+ }
+
+ //
+ // make sure we have a local key-id
+ //
+ if (bagAttrs.getBagAttribute(pkcs_9_at_localKeyId) == null)
+ {
+ Certificate ct = engineGetCertificate(name);
+
+ bagAttrs.setBagAttribute(pkcs_9_at_localKeyId, createSubjectKeyId(ct.getPublicKey()));
+ }
+
+ Enumeration e = bagAttrs.getBagAttributeKeys();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ ASN1EncodableVector kSeq = new ASN1EncodableVector();
+
+ kSeq.add(oid);
+ kSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
+
+ attrSet = true;
+
+ kName.add(new DERSequence(kSeq));
+ }
+ }
+
+ if (!attrSet)
+ {
+ //
+ // set a default friendly name (from the key id) and local id
+ //
+ ASN1EncodableVector kSeq = new ASN1EncodableVector();
+ Certificate ct = engineGetCertificate(name);
+
+ kSeq.add(pkcs_9_at_localKeyId);
+ kSeq.add(new DERSet(createSubjectKeyId(ct.getPublicKey())));
+
+ kName.add(new DERSequence(kSeq));
+
+ kSeq = new ASN1EncodableVector();
+
+ kSeq.add(pkcs_9_at_friendlyName);
+ kSeq.add(new DERSet(new DERBMPString(name)));
+
+ kName.add(new DERSequence(kSeq));
+ }
+
+ SafeBag kBag = new SafeBag(pkcs8ShroudedKeyBag, kInfo.toASN1Primitive(), new DERSet(kName));
+ keyS.add(kBag);
+ }
+
+ byte[] keySEncoded = new DERSequence(keyS).getEncoded(ASN1Encoding.DER);
+ BERConstructedOctetString keyString = new BERConstructedOctetString(keySEncoded);
+
+ //
+ // certificate processing
+ //
+ byte[] cSalt = new byte[SALT_SIZE];
+
+ random.nextBytes(cSalt);
+
+ ASN1EncodableVector certSeq = new ASN1EncodableVector();
+ PKCS12PBEParams cParams = new PKCS12PBEParams(cSalt, MIN_ITERATIONS);
+ AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.toASN1Primitive());
+ Hashtable doneCerts = new Hashtable();
+
+ Enumeration cs = keys.keys();
+ while (cs.hasMoreElements())
+ {
+ try
+ {
+ String name = (String)cs.nextElement();
+ Certificate cert = engineGetCertificate(name);
+ boolean cAttrSet = false;
+ CertBag cBag = new CertBag(
+ x509Certificate,
+ new DEROctetString(cert.getEncoded()));
+ ASN1EncodableVector fName = new ASN1EncodableVector();
+
+ if (cert instanceof PKCS12BagAttributeCarrier)
+ {
+ PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert;
+ //
+ // make sure we are using the local alias on store
+ //
+ DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+ if (nm == null || !nm.getString().equals(name))
+ {
+ bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name));
+ }
+
+ //
+ // make sure we have a local key-id
+ //
+ if (bagAttrs.getBagAttribute(pkcs_9_at_localKeyId) == null)
+ {
+ bagAttrs.setBagAttribute(pkcs_9_at_localKeyId, createSubjectKeyId(cert.getPublicKey()));
+ }
+
+ Enumeration e = bagAttrs.getBagAttributeKeys();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+ fSeq.add(oid);
+ fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
+ fName.add(new DERSequence(fSeq));
+
+ cAttrSet = true;
+ }
+ }
+
+ if (!cAttrSet)
+ {
+ ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+ fSeq.add(pkcs_9_at_localKeyId);
+ fSeq.add(new DERSet(createSubjectKeyId(cert.getPublicKey())));
+ fName.add(new DERSequence(fSeq));
+
+ fSeq = new ASN1EncodableVector();
+
+ fSeq.add(pkcs_9_at_friendlyName);
+ fSeq.add(new DERSet(new DERBMPString(name)));
+
+ fName.add(new DERSequence(fSeq));
+ }
+
+ SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
+
+ certSeq.add(sBag);
+
+ doneCerts.put(cert, cert);
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new IOException("Error encoding certificate: " + e.toString());
+ }
+ }
+
+ cs = certs.keys();
+ while (cs.hasMoreElements())
+ {
+ try
+ {
+ String certId = (String)cs.nextElement();
+ Certificate cert = (Certificate)certs.get(certId);
+ boolean cAttrSet = false;
+
+ if (keys.get(certId) != null)
+ {
+ continue;
+ }
+
+ CertBag cBag = new CertBag(
+ x509Certificate,
+ new DEROctetString(cert.getEncoded()));
+ ASN1EncodableVector fName = new ASN1EncodableVector();
+
+ if (cert instanceof PKCS12BagAttributeCarrier)
+ {
+ PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert;
+ //
+ // make sure we are using the local alias on store
+ //
+ DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName);
+ if (nm == null || !nm.getString().equals(certId))
+ {
+ bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(certId));
+ }
+
+ Enumeration e = bagAttrs.getBagAttributeKeys();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+
+ // a certificate not immediately linked to a key doesn't require
+ // a localKeyID and will confuse some PKCS12 implementations.
+ //
+ // If we find one, we'll prune it out.
+ if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId))
+ {
+ continue;
+ }
+
+ ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+ fSeq.add(oid);
+ fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
+ fName.add(new DERSequence(fSeq));
+
+ cAttrSet = true;
+ }
+ }
+
+ if (!cAttrSet)
+ {
+ ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+ fSeq.add(pkcs_9_at_friendlyName);
+ fSeq.add(new DERSet(new DERBMPString(certId)));
+
+ fName.add(new DERSequence(fSeq));
+ }
+
+ SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
+
+ certSeq.add(sBag);
+
+ doneCerts.put(cert, cert);
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new IOException("Error encoding certificate: " + e.toString());
+ }
+ }
+
+ cs = chainCerts.keys();
+ while (cs.hasMoreElements())
+ {
+ try
+ {
+ CertId certId = (CertId)cs.nextElement();
+ Certificate cert = (Certificate)chainCerts.get(certId);
+
+ if (doneCerts.get(cert) != null)
+ {
+ continue;
+ }
+
+ CertBag cBag = new CertBag(
+ x509Certificate,
+ new DEROctetString(cert.getEncoded()));
+ ASN1EncodableVector fName = new ASN1EncodableVector();
+
+ if (cert instanceof PKCS12BagAttributeCarrier)
+ {
+ PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert;
+ Enumeration e = bagAttrs.getBagAttributeKeys();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+
+ // a certificate not immediately linked to a key doesn't require
+ // a localKeyID and will confuse some PKCS12 implementations.
+ //
+ // If we find one, we'll prune it out.
+ if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId))
+ {
+ continue;
+ }
+
+ ASN1EncodableVector fSeq = new ASN1EncodableVector();
+
+ fSeq.add(oid);
+ fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid)));
+ fName.add(new DERSequence(fSeq));
+ }
+ }
+
+ SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName));
+
+ certSeq.add(sBag);
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new IOException("Error encoding certificate: " + e.toString());
+ }
+ }
+
+ byte[] certSeqEncoded = new DERSequence(certSeq).getEncoded(ASN1Encoding.DER);
+ byte[] certBytes = cryptData(true, cAlgId, password, false, certSeqEncoded);
+ EncryptedData cInfo = new EncryptedData(data, cAlgId, new BERConstructedOctetString(certBytes));
+
+ ContentInfo[] info = new ContentInfo[]
+ {
+ new ContentInfo(data, keyString),
+ new ContentInfo(encryptedData, cInfo.toASN1Primitive())
+ };
+
+ AuthenticatedSafe auth = new AuthenticatedSafe(info);
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream asn1Out;
+ if (useDEREncoding)
+ {
+ asn1Out = new DEROutputStream(bOut);
+ }
+ else
+ {
+ asn1Out = new BEROutputStream(bOut);
+ }
+
+ asn1Out.writeObject(auth);
+
+ byte[] pkg = bOut.toByteArray();
+
+ ContentInfo mainInfo = new ContentInfo(data, new BERConstructedOctetString(pkg));
+
+ //
+ // create the mac
+ //
+ byte[] mSalt = new byte[20];
+ int itCount = MIN_ITERATIONS;
+
+ random.nextBytes(mSalt);
+
+ byte[] data = ((ASN1OctetString)mainInfo.getContent()).getOctets();
+
+ MacData mData;
+
+ try
+ {
+ byte[] res = calculatePbeMac(id_SHA1, mSalt, itCount, password, false, data);
+
+ // BEGIN android-changed
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(id_SHA1, DERNull.INSTANCE);
+ // END android-changed
+ DigestInfo dInfo = new DigestInfo(algId, res);
+
+ mData = new MacData(dInfo, mSalt, itCount);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("error constructing MAC: " + e.toString());
+ }
+
+ //
+ // output the Pfx
+ //
+ Pfx pfx = new Pfx(mainInfo, mData);
+
+ if (useDEREncoding)
+ {
+ asn1Out = new DEROutputStream(stream);
+ }
+ else
+ {
+ asn1Out = new BEROutputStream(stream);
+ }
+
+ asn1Out.writeObject(pfx);
+ }
+
+ private static byte[] calculatePbeMac(
+ ASN1ObjectIdentifier oid,
+ byte[] salt,
+ int itCount,
+ char[] password,
+ boolean wrongPkcs12Zero,
+ byte[] data)
+ throws Exception
+ {
+ SecretKeyFactory keyFact = SecretKeyFactory.getInstance(oid.getId(), bcProvider);
+ PBEParameterSpec defParams = new PBEParameterSpec(salt, itCount);
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ BCPBEKey key = (BCPBEKey) keyFact.generateSecret(pbeSpec);
+ key.setTryWrongPKCS12Zero(wrongPkcs12Zero);
+
+ Mac mac = Mac.getInstance(oid.getId(), bcProvider);
+ mac.init(key, defParams);
+ mac.update(data);
+ return mac.doFinal();
+ }
+
+ public static class BCPKCS12KeyStore
+ extends JDKPKCS12KeyStore
+ {
+ public BCPKCS12KeyStore()
+ {
+ super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+ }
+ }
+
+ // BEGIN android-removed
+ // public static class BCPKCS12KeyStore3DES
+ // extends JDKPKCS12KeyStore
+ // {
+ // public BCPKCS12KeyStore3DES()
+ // {
+ // super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+ // }
+ // }
+ //
+ // public static class DefPKCS12KeyStore
+ // extends JDKPKCS12KeyStore
+ // {
+ // public DefPKCS12KeyStore()
+ // {
+ // super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC);
+ // }
+ // }
+ //
+ // public static class DefPKCS12KeyStore3DES
+ // extends JDKPKCS12KeyStore
+ // {
+ // public DefPKCS12KeyStore3DES()
+ // {
+ // super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC);
+ // }
+ // }
+ // END android-removed
+
+ private static class IgnoresCaseHashtable
+ {
+ private Hashtable orig = new Hashtable();
+ private Hashtable keys = new Hashtable();
+
+ public void put(String key, Object value)
+ {
+ // BEGIN android-changed
+ String lower = (key == null) ? null : Strings.toLowerCase(key);
+ // END android-changed
+ String k = (String)keys.get(lower);
+ if (k != null)
+ {
+ orig.remove(k);
+ }
+
+ keys.put(lower, key);
+ orig.put(key, value);
+ }
+
+ public Enumeration keys()
+ {
+ return orig.keys();
+ }
+
+ public Object remove(String alias)
+ {
+ // BEGIN android-changed
+ String k = (String)keys.remove(alias == null ? null : Strings.toLowerCase(alias));
+ // END android-changed
+ if (k == null)
+ {
+ return null;
+ }
+
+ return orig.remove(k);
+ }
+
+ public Object get(String alias)
+ {
+ // BEGIN android-changed
+ String k = (String)keys.get(alias == null ? null : Strings.toLowerCase(alias));
+ // END android-changed
+ if (k == null)
+ {
+ return null;
+ }
+
+ return orig.get(k);
+ }
+
+ public Enumeration elements()
+ {
+ return orig.elements();
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java
new file mode 100644
index 0000000..865481f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.OutputStream;
+import java.security.KeyStore;
+import java.security.KeyStore.LoadStoreParameter;
+import java.security.KeyStore.ProtectionParameter;
+
+public class JDKPKCS12StoreParameter implements LoadStoreParameter
+{
+ private OutputStream outputStream;
+ private ProtectionParameter protectionParameter;
+ private boolean useDEREncoding;
+
+ public OutputStream getOutputStream()
+ {
+ return outputStream;
+ }
+
+ public ProtectionParameter getProtectionParameter()
+ {
+ return protectionParameter;
+ }
+
+ public boolean isUseDEREncoding()
+ {
+ return useDEREncoding;
+ }
+
+ public void setOutputStream(OutputStream outputStream)
+ {
+ this.outputStream = outputStream;
+ }
+
+ public void setPassword(char[] password)
+ {
+ this.protectionParameter = new KeyStore.PasswordProtection(password);
+ }
+
+ public void setProtectionParameter(ProtectionParameter protectionParameter)
+ {
+ this.protectionParameter = protectionParameter;
+ }
+
+ public void setUseDEREncoding(boolean useDEREncoding)
+ {
+ this.useDEREncoding = useDEREncoding;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java
new file mode 100644
index 0000000..04718ef
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java
@@ -0,0 +1,94 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.util.encoders.Base64;
+
+public class PEMUtil
+{
+ private final String _header1;
+ private final String _header2;
+ private final String _footer1;
+ private final String _footer2;
+
+ PEMUtil(
+ String type)
+ {
+ _header1 = "-----BEGIN " + type + "-----";
+ _header2 = "-----BEGIN X509 " + type + "-----";
+ _footer1 = "-----END " + type + "-----";
+ _footer2 = "-----END X509 " + type + "-----";
+ }
+
+ private String readLine(
+ InputStream in)
+ throws IOException
+ {
+ int c;
+ StringBuffer l = new StringBuffer();
+
+ do
+ {
+ while (((c = in.read()) != '\r') && c != '\n' && (c >= 0))
+ {
+ if (c == '\r')
+ {
+ continue;
+ }
+
+ l.append((char)c);
+ }
+ }
+ while (c >= 0 && l.length() == 0);
+
+ if (c < 0)
+ {
+ return null;
+ }
+
+ return l.toString();
+ }
+
+ ASN1Sequence readPEMObject(
+ InputStream in)
+ throws IOException
+ {
+ String line;
+ StringBuffer pemBuf = new StringBuffer();
+
+ while ((line = readLine(in)) != null)
+ {
+ if (line.startsWith(_header1) || line.startsWith(_header2))
+ {
+ break;
+ }
+ }
+
+ while ((line = readLine(in)) != null)
+ {
+ if (line.startsWith(_footer1) || line.startsWith(_footer2))
+ {
+ break;
+ }
+
+ pemBuf.append(line);
+ }
+
+ if (pemBuf.length() != 0)
+ {
+ ASN1Primitive o = new ASN1InputStream(Base64.decode(pemBuf.toString())).readObject();
+ if (!(o instanceof ASN1Sequence))
+ {
+ throw new IOException("malformed PEM data encountered");
+ }
+
+ return (ASN1Sequence)o;
+ }
+
+ return null;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java
new file mode 100644
index 0000000..c94016d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java
@@ -0,0 +1,155 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.PKIXParameters;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.util.StoreException;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
+import org.bouncycastle.x509.X509CRLStoreSelector;
+import org.bouncycastle.x509.X509Store;
+
+public class PKIXCRLUtil
+{
+ public Set findCRLs(X509CRLStoreSelector crlselect, ExtendedPKIXParameters paramsPKIX, Date currentDate)
+ throws AnnotatedException
+ {
+ Set initialSet = new HashSet();
+
+ // get complete CRL(s)
+ try
+ {
+ initialSet.addAll(findCRLs(crlselect, paramsPKIX.getAdditionalStores()));
+ initialSet.addAll(findCRLs(crlselect, paramsPKIX.getStores()));
+ initialSet.addAll(findCRLs(crlselect, paramsPKIX.getCertStores()));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Exception obtaining complete CRLs.", e);
+ }
+
+ Set finalSet = new HashSet();
+ Date validityDate = currentDate;
+
+ if (paramsPKIX.getDate() != null)
+ {
+ validityDate = paramsPKIX.getDate();
+ }
+
+ // based on RFC 5280 6.3.3
+ for (Iterator it = initialSet.iterator(); it.hasNext();)
+ {
+ X509CRL crl = (X509CRL)it.next();
+
+ if (crl.getNextUpdate().after(validityDate))
+ {
+ X509Certificate cert = crlselect.getCertificateChecking();
+
+ if (cert != null)
+ {
+ if (crl.getThisUpdate().before(cert.getNotAfter()))
+ {
+ finalSet.add(crl);
+ }
+ }
+ else
+ {
+ finalSet.add(crl);
+ }
+ }
+ }
+
+ return finalSet;
+ }
+
+ public Set findCRLs(X509CRLStoreSelector crlselect, PKIXParameters paramsPKIX)
+ throws AnnotatedException
+ {
+ Set completeSet = new HashSet();
+
+ // get complete CRL(s)
+ try
+ {
+ completeSet.addAll(findCRLs(crlselect, paramsPKIX.getCertStores()));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Exception obtaining complete CRLs.", e);
+ }
+
+ return completeSet;
+ }
+
+/**
+ * Return a Collection of all CRLs found in the X509Store's that are
+ * matching the crlSelect criteriums.
+ *
+ * @param crlSelect a {@link X509CRLStoreSelector} object that will be used
+ * to select the CRLs
+ * @param crlStores a List containing only
+ * {@link org.bouncycastle.x509.X509Store X509Store} objects.
+ * These are used to search for CRLs
+ *
+ * @return a Collection of all found {@link java.security.cert.X509CRL X509CRL} objects. May be
+ * empty but never <code>null</code>.
+ */
+ private final Collection findCRLs(X509CRLStoreSelector crlSelect,
+ List crlStores) throws AnnotatedException
+ {
+ Set crls = new HashSet();
+ Iterator iter = crlStores.iterator();
+
+ AnnotatedException lastException = null;
+ boolean foundValidStore = false;
+
+ while (iter.hasNext())
+ {
+ Object obj = iter.next();
+
+ if (obj instanceof X509Store)
+ {
+ X509Store store = (X509Store)obj;
+
+ try
+ {
+ crls.addAll(store.getMatches(crlSelect));
+ foundValidStore = true;
+ }
+ catch (StoreException e)
+ {
+ lastException = new AnnotatedException(
+ "Exception searching in X.509 CRL store.", e);
+ }
+ }
+ else
+ {
+ CertStore store = (CertStore)obj;
+
+ try
+ {
+ crls.addAll(store.getCRLs(crlSelect));
+ foundValidStore = true;
+ }
+ catch (CertStoreException e)
+ {
+ lastException = new AnnotatedException(
+ "Exception searching in X.509 CRL store.", e);
+ }
+ }
+ }
+ if (!foundValidStore && lastException != null)
+ {
+ throw lastException;
+ }
+ return crls;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
new file mode 100644
index 0000000..384eb86
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java
@@ -0,0 +1,261 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathBuilderResult;
+import java.security.cert.CertPathBuilderSpi;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathBuilderResult;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.jce.exception.ExtCertPathBuilderException;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+import org.bouncycastle.x509.X509CertStoreSelector;
+
+/**
+ * Implements the PKIX CertPathBuilding algorithm for BouncyCastle.
+ *
+ * @see CertPathBuilderSpi
+ */
+public class PKIXCertPathBuilderSpi
+ extends CertPathBuilderSpi
+{
+ /**
+ * Build and validate a CertPath using the given parameter.
+ *
+ * @param params PKIXBuilderParameters object containing all information to
+ * build the CertPath
+ */
+ public CertPathBuilderResult engineBuild(CertPathParameters params)
+ throws CertPathBuilderException, InvalidAlgorithmParameterException
+ {
+ if (!(params instanceof PKIXBuilderParameters)
+ && !(params instanceof ExtendedPKIXBuilderParameters))
+ {
+ throw new InvalidAlgorithmParameterException(
+ "Parameters must be an instance of "
+ + PKIXBuilderParameters.class.getName() + " or "
+ + ExtendedPKIXBuilderParameters.class.getName() + ".");
+ }
+
+ ExtendedPKIXBuilderParameters pkixParams = null;
+ if (params instanceof ExtendedPKIXBuilderParameters)
+ {
+ pkixParams = (ExtendedPKIXBuilderParameters) params;
+ }
+ else
+ {
+ pkixParams = (ExtendedPKIXBuilderParameters) ExtendedPKIXBuilderParameters
+ .getInstance((PKIXBuilderParameters) params);
+ }
+
+ Collection targets;
+ Iterator targetIter;
+ List certPathList = new ArrayList();
+ X509Certificate cert;
+
+ // search target certificates
+
+ Selector certSelect = pkixParams.getTargetConstraints();
+ if (!(certSelect instanceof X509CertStoreSelector))
+ {
+ throw new CertPathBuilderException(
+ "TargetConstraints must be an instance of "
+ + X509CertStoreSelector.class.getName() + " for "
+ + this.getClass().getName() + " class.");
+ }
+
+ try
+ {
+ targets = CertPathValidatorUtilities.findCertificates((X509CertStoreSelector)certSelect, pkixParams.getStores());
+ targets.addAll(CertPathValidatorUtilities.findCertificates((X509CertStoreSelector)certSelect, pkixParams.getCertStores()));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new ExtCertPathBuilderException(
+ "Error finding target certificate.", e);
+ }
+
+ if (targets.isEmpty())
+ {
+
+ throw new CertPathBuilderException(
+ "No certificate found matching targetContraints.");
+ }
+
+ CertPathBuilderResult result = null;
+
+ // check all potential target certificates
+ targetIter = targets.iterator();
+ while (targetIter.hasNext() && result == null)
+ {
+ cert = (X509Certificate) targetIter.next();
+ result = build(cert, pkixParams, certPathList);
+ }
+
+ if (result == null && certPathException != null)
+ {
+ if (certPathException instanceof AnnotatedException)
+ {
+ throw new CertPathBuilderException(certPathException.getMessage(), certPathException.getCause());
+ }
+ throw new CertPathBuilderException(
+ "Possible certificate chain could not be validated.",
+ certPathException);
+ }
+
+ if (result == null && certPathException == null)
+ {
+ throw new CertPathBuilderException(
+ "Unable to find certificate chain.");
+ }
+
+ return result;
+ }
+
+ private Exception certPathException;
+
+ protected CertPathBuilderResult build(X509Certificate tbvCert,
+ ExtendedPKIXBuilderParameters pkixParams, List tbvPath)
+ {
+ // If tbvCert is readily present in tbvPath, it indicates having run
+ // into a cycle in the
+ // PKI graph.
+ if (tbvPath.contains(tbvCert))
+ {
+ return null;
+ }
+ // step out, the certificate is not allowed to appear in a certification
+ // chain.
+ if (pkixParams.getExcludedCerts().contains(tbvCert))
+ {
+ return null;
+ }
+ // test if certificate path exceeds maximum length
+ if (pkixParams.getMaxPathLength() != -1)
+ {
+ if (tbvPath.size() - 1 > pkixParams.getMaxPathLength())
+ {
+ return null;
+ }
+ }
+
+ tbvPath.add(tbvCert);
+
+ CertificateFactory cFact;
+ CertPathValidator validator;
+ CertPathBuilderResult builderResult = null;
+
+ try
+ {
+ cFact = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
+ validator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME);
+ }
+ catch (Exception e)
+ {
+ // cannot happen
+ throw new RuntimeException("Exception creating support classes.");
+ }
+
+ try
+ {
+ // check whether the issuer of <tbvCert> is a TrustAnchor
+ if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getTrustAnchors(),
+ pkixParams.getSigProvider()) != null)
+ {
+ // exception message from possibly later tried certification
+ // chains
+ CertPath certPath = null;
+ PKIXCertPathValidatorResult result = null;
+ try
+ {
+ certPath = cFact.generateCertPath(tbvPath);
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "Certification path could not be constructed from certificate list.",
+ e);
+ }
+
+ try
+ {
+ result = (PKIXCertPathValidatorResult) validator.validate(
+ certPath, pkixParams);
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "Certification path could not be validated.", e);
+ }
+
+ return new PKIXCertPathBuilderResult(certPath, result
+ .getTrustAnchor(), result.getPolicyTree(), result
+ .getPublicKey());
+
+ }
+ else
+ {
+ // add additional X.509 stores from locations in certificate
+ try
+ {
+ CertPathValidatorUtilities.addAdditionalStoresFromAltNames(
+ tbvCert, pkixParams);
+ }
+ catch (CertificateParsingException e)
+ {
+ throw new AnnotatedException(
+ "No additiontal X.509 stores can be added from certificate locations.",
+ e);
+ }
+ Collection issuers = new HashSet();
+ // try to get the issuer certificate from one
+ // of the stores
+ try
+ {
+ issuers.addAll(CertPathValidatorUtilities.findIssuerCerts(tbvCert, pkixParams));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException(
+ "Cannot find issuer certificate for certificate in certification path.",
+ e);
+ }
+ if (issuers.isEmpty())
+ {
+ throw new AnnotatedException(
+ "No issuer certificate for certificate in certification path found.");
+ }
+ Iterator it = issuers.iterator();
+
+ while (it.hasNext() && builderResult == null)
+ {
+ X509Certificate issuer = (X509Certificate) it.next();
+ builderResult = build(issuer, pkixParams, tbvPath);
+ }
+ }
+ }
+ catch (AnnotatedException e)
+ {
+ certPathException = e;
+ }
+ if (builderResult == null)
+ {
+ tbvPath.remove(tbvCert);
+ }
+ return builderResult;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
new file mode 100644
index 0000000..af764f3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java
@@ -0,0 +1,462 @@
+package org.bouncycastle.jce.provider;
+
+// BEGIN android-added
+import java.math.BigInteger;
+// END android-added
+import java.security.InvalidAlgorithmParameterException;
+import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorResult;
+import java.security.cert.CertPathValidatorSpi;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
+
+/**
+ * CertPathValidatorSpi implementation for X.509 Certificate validation � la RFC
+ * 3280.
+ */
+public class PKIXCertPathValidatorSpi
+ extends CertPathValidatorSpi
+{
+ // BEGIN android-added
+ private final static CertBlacklist blacklist = new CertBlacklist();
+ // END android-added
+
+ public CertPathValidatorResult engineValidate(
+ CertPath certPath,
+ CertPathParameters params)
+ throws CertPathValidatorException,
+ InvalidAlgorithmParameterException
+ {
+ if (!(params instanceof PKIXParameters))
+ {
+ throw new InvalidAlgorithmParameterException("Parameters must be a " + PKIXParameters.class.getName()
+ + " instance.");
+ }
+
+ ExtendedPKIXParameters paramsPKIX;
+ if (params instanceof ExtendedPKIXParameters)
+ {
+ paramsPKIX = (ExtendedPKIXParameters)params;
+ }
+ else
+ {
+ paramsPKIX = ExtendedPKIXParameters.getInstance((PKIXParameters)params);
+ }
+ if (paramsPKIX.getTrustAnchors() == null)
+ {
+ throw new InvalidAlgorithmParameterException(
+ "trustAnchors is null, this is not allowed for certification path validation.");
+ }
+
+ //
+ // 6.1.1 - inputs
+ //
+
+ //
+ // (a)
+ //
+ List certs = certPath.getCertificates();
+ int n = certs.size();
+
+ if (certs.isEmpty())
+ {
+ throw new CertPathValidatorException("Certification path is empty.", null, certPath, 0);
+ }
+ // BEGIN android-added
+ {
+ X509Certificate cert = (X509Certificate) certs.get(0);
+
+ if (cert != null) {
+ BigInteger serial = cert.getSerialNumber();
+ if (blacklist.isSerialNumberBlackListed(serial)) {
+ // emulate CRL exception message in RFC3280CertPathUtilities.checkCRLs
+ String message = "Certificate revocation of serial 0x" + serial.toString(16);
+ System.out.println(message);
+ AnnotatedException e = new AnnotatedException(message);
+ throw new CertPathValidatorException(e.getMessage(), e, certPath, 0);
+ }
+ }
+ }
+ // END android-added
+
+ //
+ // (b)
+ //
+ // Date validDate = CertPathValidatorUtilities.getValidDate(paramsPKIX);
+
+ //
+ // (c)
+ //
+ Set userInitialPolicySet = paramsPKIX.getInitialPolicies();
+
+ //
+ // (d)
+ //
+ TrustAnchor trust;
+ try
+ {
+ trust = CertPathValidatorUtilities.findTrustAnchor((X509Certificate) certs.get(certs.size() - 1),
+ paramsPKIX.getTrustAnchors(), paramsPKIX.getSigProvider());
+ }
+ catch (AnnotatedException e)
+ {
+ throw new CertPathValidatorException(e.getMessage(), e, certPath, certs.size() - 1);
+ }
+
+ if (trust == null)
+ {
+ throw new CertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
+ }
+
+ //
+ // (e), (f), (g) are part of the paramsPKIX object.
+ //
+ Iterator certIter;
+ int index = 0;
+ int i;
+ // Certificate for each interation of the validation loop
+ // Signature information for each iteration of the validation loop
+ //
+ // 6.1.2 - setup
+ //
+
+ //
+ // (a)
+ //
+ List[] policyNodes = new ArrayList[n + 1];
+ for (int j = 0; j < policyNodes.length; j++)
+ {
+ policyNodes[j] = new ArrayList();
+ }
+
+ Set policySet = new HashSet();
+
+ policySet.add(RFC3280CertPathUtilities.ANY_POLICY);
+
+ PKIXPolicyNode validPolicyTree = new PKIXPolicyNode(new ArrayList(), 0, policySet, null, new HashSet(),
+ RFC3280CertPathUtilities.ANY_POLICY, false);
+
+ policyNodes[0].add(validPolicyTree);
+
+ //
+ // (b) and (c)
+ //
+ PKIXNameConstraintValidator nameConstraintValidator = new PKIXNameConstraintValidator();
+
+ // (d)
+ //
+ int explicitPolicy;
+ Set acceptablePolicies = new HashSet();
+
+ if (paramsPKIX.isExplicitPolicyRequired())
+ {
+ explicitPolicy = 0;
+ }
+ else
+ {
+ explicitPolicy = n + 1;
+ }
+
+ //
+ // (e)
+ //
+ int inhibitAnyPolicy;
+
+ if (paramsPKIX.isAnyPolicyInhibited())
+ {
+ inhibitAnyPolicy = 0;
+ }
+ else
+ {
+ inhibitAnyPolicy = n + 1;
+ }
+
+ //
+ // (f)
+ //
+ int policyMapping;
+
+ if (paramsPKIX.isPolicyMappingInhibited())
+ {
+ policyMapping = 0;
+ }
+ else
+ {
+ policyMapping = n + 1;
+ }
+
+ //
+ // (g), (h), (i), (j)
+ //
+ PublicKey workingPublicKey;
+ X500Principal workingIssuerName;
+
+ X509Certificate sign = trust.getTrustedCert();
+ try
+ {
+ if (sign != null)
+ {
+ workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign);
+ workingPublicKey = sign.getPublicKey();
+ }
+ else
+ {
+ workingIssuerName = new X500Principal(trust.getCAName());
+ workingPublicKey = trust.getCAPublicKey();
+ }
+ }
+ catch (IllegalArgumentException ex)
+ {
+ throw new ExtCertPathValidatorException("Subject of trust anchor could not be (re)encoded.", ex, certPath,
+ -1);
+ }
+
+ AlgorithmIdentifier workingAlgId = null;
+ try
+ {
+ workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey);
+ }
+ catch (CertPathValidatorException e)
+ {
+ throw new ExtCertPathValidatorException(
+ "Algorithm identifier of public key of trust anchor could not be read.", e, certPath, -1);
+ }
+ DERObjectIdentifier workingPublicKeyAlgorithm = workingAlgId.getObjectId();
+ ASN1Encodable workingPublicKeyParameters = workingAlgId.getParameters();
+
+ //
+ // (k)
+ //
+ int maxPathLength = n;
+
+ //
+ // 6.1.3
+ //
+
+ if (paramsPKIX.getTargetConstraints() != null
+ && !paramsPKIX.getTargetConstraints().match((X509Certificate) certs.get(0)))
+ {
+ throw new ExtCertPathValidatorException(
+ "Target certificate in certification path does not match targetConstraints.", null, certPath, 0);
+ }
+
+ //
+ // initialize CertPathChecker's
+ //
+ List pathCheckers = paramsPKIX.getCertPathCheckers();
+ certIter = pathCheckers.iterator();
+ while (certIter.hasNext())
+ {
+ ((PKIXCertPathChecker) certIter.next()).init(false);
+ }
+
+ X509Certificate cert = null;
+
+ for (index = certs.size() - 1; index >= 0; index--)
+ {
+ // BEGIN android-added
+ if (blacklist.isPublicKeyBlackListed(workingPublicKey)) {
+ // emulate CRL exception message in RFC3280CertPathUtilities.checkCRLs
+ String message = "Certificate revocation of public key " + workingPublicKey;
+ System.out.println(message);
+ AnnotatedException e = new AnnotatedException(message);
+ throw new CertPathValidatorException(e.getMessage(), e, certPath, index);
+ }
+ // END android-added
+ // try
+ // {
+ //
+ // i as defined in the algorithm description
+ //
+ i = n - index;
+
+ //
+ // set certificate to be checked in this round
+ // sign and workingPublicKey and workingIssuerName are set
+ // at the end of the for loop and initialized the
+ // first time from the TrustAnchor
+ //
+ cert = (X509Certificate) certs.get(index);
+ boolean verificationAlreadyPerformed = (index == certs.size() - 1);
+
+ //
+ // 6.1.3
+ //
+
+ RFC3280CertPathUtilities.processCertA(certPath, paramsPKIX, index, workingPublicKey,
+ verificationAlreadyPerformed, workingIssuerName, sign);
+
+ RFC3280CertPathUtilities.processCertBC(certPath, index, nameConstraintValidator);
+
+ validPolicyTree = RFC3280CertPathUtilities.processCertD(certPath, index, acceptablePolicies,
+ validPolicyTree, policyNodes, inhibitAnyPolicy);
+
+ validPolicyTree = RFC3280CertPathUtilities.processCertE(certPath, index, validPolicyTree);
+
+ RFC3280CertPathUtilities.processCertF(certPath, index, validPolicyTree, explicitPolicy);
+
+ //
+ // 6.1.4
+ //
+
+ if (i != n)
+ {
+ if (cert != null && cert.getVersion() == 1)
+ {
+ throw new CertPathValidatorException("Version 1 certificates can't be used as CA ones.", null,
+ certPath, index);
+ }
+
+ RFC3280CertPathUtilities.prepareNextCertA(certPath, index);
+
+ validPolicyTree = RFC3280CertPathUtilities.prepareCertB(certPath, index, policyNodes, validPolicyTree,
+ policyMapping);
+
+ RFC3280CertPathUtilities.prepareNextCertG(certPath, index, nameConstraintValidator);
+
+ // (h)
+ explicitPolicy = RFC3280CertPathUtilities.prepareNextCertH1(certPath, index, explicitPolicy);
+ policyMapping = RFC3280CertPathUtilities.prepareNextCertH2(certPath, index, policyMapping);
+ inhibitAnyPolicy = RFC3280CertPathUtilities.prepareNextCertH3(certPath, index, inhibitAnyPolicy);
+
+ //
+ // (i)
+ //
+ explicitPolicy = RFC3280CertPathUtilities.prepareNextCertI1(certPath, index, explicitPolicy);
+ policyMapping = RFC3280CertPathUtilities.prepareNextCertI2(certPath, index, policyMapping);
+
+ // (j)
+ inhibitAnyPolicy = RFC3280CertPathUtilities.prepareNextCertJ(certPath, index, inhibitAnyPolicy);
+
+ // (k)
+ RFC3280CertPathUtilities.prepareNextCertK(certPath, index);
+
+ // (l)
+ maxPathLength = RFC3280CertPathUtilities.prepareNextCertL(certPath, index, maxPathLength);
+
+ // (m)
+ maxPathLength = RFC3280CertPathUtilities.prepareNextCertM(certPath, index, maxPathLength);
+
+ // (n)
+ RFC3280CertPathUtilities.prepareNextCertN(certPath, index);
+
+ Set criticalExtensions = cert.getCriticalExtensionOIDs();
+ if (criticalExtensions != null)
+ {
+ criticalExtensions = new HashSet(criticalExtensions);
+
+ // these extensions are handled by the algorithm
+ criticalExtensions.remove(RFC3280CertPathUtilities.KEY_USAGE);
+ criticalExtensions.remove(RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+ criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_MAPPINGS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY);
+ criticalExtensions.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+ criticalExtensions.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+ criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_CONSTRAINTS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.BASIC_CONSTRAINTS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME);
+ criticalExtensions.remove(RFC3280CertPathUtilities.NAME_CONSTRAINTS);
+ }
+ else
+ {
+ criticalExtensions = new HashSet();
+ }
+
+ // (o)
+ RFC3280CertPathUtilities.prepareNextCertO(certPath, index, criticalExtensions, pathCheckers);
+
+ // set signing certificate for next round
+ sign = cert;
+
+ // (c)
+ workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign);
+
+ // (d)
+ try
+ {
+ workingPublicKey = CertPathValidatorUtilities.getNextWorkingKey(certPath.getCertificates(), index);
+ }
+ catch (CertPathValidatorException e)
+ {
+ throw new CertPathValidatorException("Next working key could not be retrieved.", e, certPath, index);
+ }
+
+ workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey);
+ // (f)
+ workingPublicKeyAlgorithm = workingAlgId.getObjectId();
+ // (e)
+ workingPublicKeyParameters = workingAlgId.getParameters();
+ }
+ }
+
+ //
+ // 6.1.5 Wrap-up procedure
+ //
+
+ explicitPolicy = RFC3280CertPathUtilities.wrapupCertA(explicitPolicy, cert);
+
+ explicitPolicy = RFC3280CertPathUtilities.wrapupCertB(certPath, index + 1, explicitPolicy);
+
+ //
+ // (c) (d) and (e) are already done
+ //
+
+ //
+ // (f)
+ //
+ Set criticalExtensions = cert.getCriticalExtensionOIDs();
+
+ if (criticalExtensions != null)
+ {
+ criticalExtensions = new HashSet(criticalExtensions);
+ // these extensions are handled by the algorithm
+ criticalExtensions.remove(RFC3280CertPathUtilities.KEY_USAGE);
+ criticalExtensions.remove(RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+ criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_MAPPINGS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY);
+ criticalExtensions.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+ criticalExtensions.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+ criticalExtensions.remove(RFC3280CertPathUtilities.POLICY_CONSTRAINTS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.BASIC_CONSTRAINTS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME);
+ criticalExtensions.remove(RFC3280CertPathUtilities.NAME_CONSTRAINTS);
+ criticalExtensions.remove(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS);
+ }
+ else
+ {
+ criticalExtensions = new HashSet();
+ }
+
+ RFC3280CertPathUtilities.wrapupCertF(certPath, index + 1, pathCheckers, criticalExtensions);
+
+ PKIXPolicyNode intersection = RFC3280CertPathUtilities.wrapupCertG(certPath, paramsPKIX, userInitialPolicySet,
+ index + 1, policyNodes, validPolicyTree, acceptablePolicies);
+
+ if ((explicitPolicy > 0) || (intersection != null))
+ {
+ return new PKIXCertPathValidatorResult(trust, intersection, cert.getPublicKey());
+ }
+
+ throw new CertPathValidatorException("Path processing failed on policy.", null, certPath, index);
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java
new file mode 100644
index 0000000..ddf7462
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java
@@ -0,0 +1,1924 @@
+package org.bouncycastle.jce.provider;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralSubtree;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public class PKIXNameConstraintValidator
+{
+ private Set excludedSubtreesDN = new HashSet();
+
+ private Set excludedSubtreesDNS = new HashSet();
+
+ private Set excludedSubtreesEmail = new HashSet();
+
+ private Set excludedSubtreesURI = new HashSet();
+
+ private Set excludedSubtreesIP = new HashSet();
+
+ private Set permittedSubtreesDN;
+
+ private Set permittedSubtreesDNS;
+
+ private Set permittedSubtreesEmail;
+
+ private Set permittedSubtreesURI;
+
+ private Set permittedSubtreesIP;
+
+ public PKIXNameConstraintValidator()
+ {
+ }
+
+ private static boolean withinDNSubtree(
+ ASN1Sequence dns,
+ ASN1Sequence subtree)
+ {
+ if (subtree.size() < 1)
+ {
+ return false;
+ }
+
+ if (subtree.size() > dns.size())
+ {
+ return false;
+ }
+
+ for (int j = subtree.size() - 1; j >= 0; j--)
+ {
+ if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void checkPermittedDN(ASN1Sequence dns)
+ throws PKIXNameConstraintValidatorException
+ {
+ checkPermittedDN(permittedSubtreesDN, dns);
+ }
+
+ public void checkExcludedDN(ASN1Sequence dns)
+ throws PKIXNameConstraintValidatorException
+ {
+ checkExcludedDN(excludedSubtreesDN, dns);
+ }
+
+ private void checkPermittedDN(Set permitted, ASN1Sequence dns)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ if (permitted.isEmpty() && dns.size() == 0)
+ {
+ return;
+ }
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+ if (withinDNSubtree(dns, subtree))
+ {
+ return;
+ }
+ }
+
+ throw new PKIXNameConstraintValidatorException(
+ "Subject distinguished name is not from a permitted subtree");
+ }
+
+ private void checkExcludedDN(Set excluded, ASN1Sequence dns)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+ if (withinDNSubtree(dns, subtree))
+ {
+ throw new PKIXNameConstraintValidatorException(
+ "Subject distinguished name is from an excluded subtree");
+ }
+ }
+ }
+
+ private Set intersectDN(Set permitted, Set dns)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = dns.iterator(); it.hasNext();)
+ {
+ ASN1Sequence dn = ASN1Sequence.getInstance(((GeneralSubtree)it
+ .next()).getBase().getName().toASN1Primitive());
+ if (permitted == null)
+ {
+ if (dn != null)
+ {
+ intersect.add(dn);
+ }
+ }
+ else
+ {
+ Iterator _iter = permitted.iterator();
+ while (_iter.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)_iter.next();
+
+ if (withinDNSubtree(dn, subtree))
+ {
+ intersect.add(dn);
+ }
+ else if (withinDNSubtree(subtree, dn))
+ {
+ intersect.add(subtree);
+ }
+ }
+ }
+ }
+ return intersect;
+ }
+
+ private Set unionDN(Set excluded, ASN1Sequence dn)
+ {
+ if (excluded.isEmpty())
+ {
+ if (dn == null)
+ {
+ return excluded;
+ }
+ excluded.add(dn);
+
+ return excluded;
+ }
+ else
+ {
+ Set intersect = new HashSet();
+
+ Iterator it = excluded.iterator();
+ while (it.hasNext())
+ {
+ ASN1Sequence subtree = (ASN1Sequence)it.next();
+
+ if (withinDNSubtree(dn, subtree))
+ {
+ intersect.add(subtree);
+ }
+ else if (withinDNSubtree(subtree, dn))
+ {
+ intersect.add(dn);
+ }
+ else
+ {
+ intersect.add(subtree);
+ intersect.add(dn);
+ }
+ }
+
+ return intersect;
+ }
+ }
+
+ private Set intersectEmail(Set permitted, Set emails)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = emails.iterator(); it.hasNext();)
+ {
+ String email = extractNameAsString(((GeneralSubtree)it.next())
+ .getBase());
+
+ if (permitted == null)
+ {
+ if (email != null)
+ {
+ intersect.add(email);
+ }
+ }
+ else
+ {
+ Iterator it2 = permitted.iterator();
+ while (it2.hasNext())
+ {
+ String _permitted = (String)it2.next();
+
+ intersectEmail(email, _permitted, intersect);
+ }
+ }
+ }
+ return intersect;
+ }
+
+ private Set unionEmail(Set excluded, String email)
+ {
+ if (excluded.isEmpty())
+ {
+ if (email == null)
+ {
+ return excluded;
+ }
+ excluded.add(email);
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator it = excluded.iterator();
+ while (it.hasNext())
+ {
+ String _excluded = (String)it.next();
+
+ unionEmail(_excluded, email, union);
+ }
+
+ return union;
+ }
+ }
+
+ /**
+ * Returns the intersection of the permitted IP ranges in
+ * <code>permitted</code> with <code>ip</code>.
+ *
+ * @param permitted A <code>Set</code> of permitted IP addresses with
+ * their subnet mask as byte arrays.
+ * @param ips The IP address with its subnet mask.
+ * @return The <code>Set</code> of permitted IP ranges intersected with
+ * <code>ip</code>.
+ */
+ private Set intersectIP(Set permitted, Set ips)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = ips.iterator(); it.hasNext();)
+ {
+ byte[] ip = ASN1OctetString.getInstance(
+ ((GeneralSubtree)it.next()).getBase().getName()).getOctets();
+ if (permitted == null)
+ {
+ if (ip != null)
+ {
+ intersect.add(ip);
+ }
+ }
+ else
+ {
+ Iterator it2 = permitted.iterator();
+ while (it2.hasNext())
+ {
+ byte[] _permitted = (byte[])it2.next();
+ intersect.addAll(intersectIPRange(_permitted, ip));
+ }
+ }
+ }
+ return intersect;
+ }
+
+ /**
+ * Returns the union of the excluded IP ranges in <code>excluded</code>
+ * with <code>ip</code>.
+ *
+ * @param excluded A <code>Set</code> of excluded IP addresses with their
+ * subnet mask as byte arrays.
+ * @param ip The IP address with its subnet mask.
+ * @return The <code>Set</code> of excluded IP ranges unified with
+ * <code>ip</code> as byte arrays.
+ */
+ private Set unionIP(Set excluded, byte[] ip)
+ {
+ if (excluded.isEmpty())
+ {
+ if (ip == null)
+ {
+ return excluded;
+ }
+ excluded.add(ip);
+
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator it = excluded.iterator();
+ while (it.hasNext())
+ {
+ byte[] _excluded = (byte[])it.next();
+ union.addAll(unionIPRange(_excluded, ip));
+ }
+
+ return union;
+ }
+ }
+
+ /**
+ * Calculates the union if two IP ranges.
+ *
+ * @param ipWithSubmask1 The first IP address with its subnet mask.
+ * @param ipWithSubmask2 The second IP address with its subnet mask.
+ * @return A <code>Set</code> with the union of both addresses.
+ */
+ private Set unionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+ {
+ Set set = new HashSet();
+
+ // difficult, adding always all IPs is not wrong
+ if (Arrays.areEqual(ipWithSubmask1, ipWithSubmask2))
+ {
+ set.add(ipWithSubmask1);
+ }
+ else
+ {
+ set.add(ipWithSubmask1);
+ set.add(ipWithSubmask2);
+ }
+ return set;
+ }
+
+ /**
+ * Calculates the interesction if two IP ranges.
+ *
+ * @param ipWithSubmask1 The first IP address with its subnet mask.
+ * @param ipWithSubmask2 The second IP address with its subnet mask.
+ * @return A <code>Set</code> with the single IP address with its subnet
+ * mask as a byte array or an empty <code>Set</code>.
+ */
+ private Set intersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+ {
+ if (ipWithSubmask1.length != ipWithSubmask2.length)
+ {
+ return Collections.EMPTY_SET;
+ }
+ byte[][] temp = extractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2);
+ byte ip1[] = temp[0];
+ byte subnetmask1[] = temp[1];
+ byte ip2[] = temp[2];
+ byte subnetmask2[] = temp[3];
+
+ byte minMax[][] = minMaxIPs(ip1, subnetmask1, ip2, subnetmask2);
+ byte[] min;
+ byte[] max;
+ max = min(minMax[1], minMax[3]);
+ min = max(minMax[0], minMax[2]);
+
+ // minimum IP address must be bigger than max
+ if (compareTo(min, max) == 1)
+ {
+ return Collections.EMPTY_SET;
+ }
+ // OR keeps all significant bits
+ byte[] ip = or(minMax[0], minMax[2]);
+ byte[] subnetmask = or(subnetmask1, subnetmask2);
+ return Collections.singleton(ipWithSubnetMask(ip, subnetmask));
+ }
+
+ /**
+ * Concatenates the IP address with its subnet mask.
+ *
+ * @param ip The IP address.
+ * @param subnetMask Its subnet mask.
+ * @return The concatenated IP address with its subnet mask.
+ */
+ private byte[] ipWithSubnetMask(byte[] ip, byte[] subnetMask)
+ {
+ int ipLength = ip.length;
+ byte[] temp = new byte[ipLength * 2];
+ System.arraycopy(ip, 0, temp, 0, ipLength);
+ System.arraycopy(subnetMask, 0, temp, ipLength, ipLength);
+ return temp;
+ }
+
+ /**
+ * Splits the IP addresses and their subnet mask.
+ *
+ * @param ipWithSubmask1 The first IP address with the subnet mask.
+ * @param ipWithSubmask2 The second IP address with the subnet mask.
+ * @return An array with two elements. Each element contains the IP address
+ * and the subnet mask in this order.
+ */
+ private byte[][] extractIPsAndSubnetMasks(
+ byte[] ipWithSubmask1,
+ byte[] ipWithSubmask2)
+ {
+ int ipLength = ipWithSubmask1.length / 2;
+ byte ip1[] = new byte[ipLength];
+ byte subnetmask1[] = new byte[ipLength];
+ System.arraycopy(ipWithSubmask1, 0, ip1, 0, ipLength);
+ System.arraycopy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength);
+
+ byte ip2[] = new byte[ipLength];
+ byte subnetmask2[] = new byte[ipLength];
+ System.arraycopy(ipWithSubmask2, 0, ip2, 0, ipLength);
+ System.arraycopy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength);
+ return new byte[][]
+ {ip1, subnetmask1, ip2, subnetmask2};
+ }
+
+ /**
+ * Based on the two IP addresses and their subnet masks the IP range is
+ * computed for each IP address - subnet mask pair and returned as the
+ * minimum IP address and the maximum address of the range.
+ *
+ * @param ip1 The first IP address.
+ * @param subnetmask1 The subnet mask of the first IP address.
+ * @param ip2 The second IP address.
+ * @param subnetmask2 The subnet mask of the second IP address.
+ * @return A array with two elements. The first/second element contains the
+ * min and max IP address of the first/second IP address and its
+ * subnet mask.
+ */
+ private byte[][] minMaxIPs(
+ byte[] ip1,
+ byte[] subnetmask1,
+ byte[] ip2,
+ byte[] subnetmask2)
+ {
+ int ipLength = ip1.length;
+ byte[] min1 = new byte[ipLength];
+ byte[] max1 = new byte[ipLength];
+
+ byte[] min2 = new byte[ipLength];
+ byte[] max2 = new byte[ipLength];
+
+ for (int i = 0; i < ipLength; i++)
+ {
+ min1[i] = (byte)(ip1[i] & subnetmask1[i]);
+ max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]);
+
+ min2[i] = (byte)(ip2[i] & subnetmask2[i]);
+ max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]);
+ }
+
+ return new byte[][]{min1, max1, min2, max2};
+ }
+
+ private void checkPermittedEmail(Set permitted, String email)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ if (emailIsConstrained(email, str))
+ {
+ return;
+ }
+ }
+
+ if (email.length() == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+
+ throw new PKIXNameConstraintValidatorException(
+ "Subject email address is not from a permitted subtree.");
+ }
+
+ private void checkExcludedEmail(Set excluded, String email)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ String str = (String)it.next();
+
+ if (emailIsConstrained(email, str))
+ {
+ throw new PKIXNameConstraintValidatorException(
+ "Email address is from an excluded subtree.");
+ }
+ }
+ }
+
+ /**
+ * Checks if the IP <code>ip</code> is included in the permitted set
+ * <code>permitted</code>.
+ *
+ * @param permitted A <code>Set</code> of permitted IP addresses with
+ * their subnet mask as byte arrays.
+ * @param ip The IP address.
+ * @throws PKIXNameConstraintValidatorException
+ * if the IP is not permitted.
+ */
+ private void checkPermittedIP(Set permitted, byte[] ip)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ byte[] ipWithSubnet = (byte[])it.next();
+
+ if (isIPConstrained(ip, ipWithSubnet))
+ {
+ return;
+ }
+ }
+ if (ip.length == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+ throw new PKIXNameConstraintValidatorException(
+ "IP is not from a permitted subtree.");
+ }
+
+ /**
+ * Checks if the IP <code>ip</code> is included in the excluded set
+ * <code>excluded</code>.
+ *
+ * @param excluded A <code>Set</code> of excluded IP addresses with their
+ * subnet mask as byte arrays.
+ * @param ip The IP address.
+ * @throws PKIXNameConstraintValidatorException
+ * if the IP is excluded.
+ */
+ private void checkExcludedIP(Set excluded, byte[] ip)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ byte[] ipWithSubnet = (byte[])it.next();
+
+ if (isIPConstrained(ip, ipWithSubnet))
+ {
+ throw new PKIXNameConstraintValidatorException(
+ "IP is from an excluded subtree.");
+ }
+ }
+ }
+
+ /**
+ * Checks if the IP address <code>ip</code> is constrained by
+ * <code>constraint</code>.
+ *
+ * @param ip The IP address.
+ * @param constraint The constraint. This is an IP address concatenated with
+ * its subnetmask.
+ * @return <code>true</code> if constrained, <code>false</code>
+ * otherwise.
+ */
+ private boolean isIPConstrained(byte ip[], byte[] constraint)
+ {
+ int ipLength = ip.length;
+
+ if (ipLength != (constraint.length / 2))
+ {
+ return false;
+ }
+
+ byte[] subnetMask = new byte[ipLength];
+ System.arraycopy(constraint, ipLength, subnetMask, 0, ipLength);
+
+ byte[] permittedSubnetAddress = new byte[ipLength];
+
+ byte[] ipSubnetAddress = new byte[ipLength];
+
+ // the resulting IP address by applying the subnet mask
+ for (int i = 0; i < ipLength; i++)
+ {
+ permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]);
+ ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]);
+ }
+
+ return Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress);
+ }
+
+ private boolean emailIsConstrained(String email, String constraint)
+ {
+ String sub = email.substring(email.indexOf('@') + 1);
+ // a particular mailbox
+ if (constraint.indexOf('@') != -1)
+ {
+ if (email.equalsIgnoreCase(constraint))
+ {
+ return true;
+ }
+ }
+ // on particular host
+ else if (!(constraint.charAt(0) == '.'))
+ {
+ if (sub.equalsIgnoreCase(constraint))
+ {
+ return true;
+ }
+ }
+ // address in sub domain
+ else if (withinDomain(sub, constraint))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean withinDomain(String testDomain, String domain)
+ {
+ String tempDomain = domain;
+ if (tempDomain.startsWith("."))
+ {
+ tempDomain = tempDomain.substring(1);
+ }
+ String[] domainParts = Strings.split(tempDomain, '.');
+ String[] testDomainParts = Strings.split(testDomain, '.');
+ // must have at least one subdomain
+ if (testDomainParts.length <= domainParts.length)
+ {
+ return false;
+ }
+ int d = testDomainParts.length - domainParts.length;
+ for (int i = -1; i < domainParts.length; i++)
+ {
+ if (i == -1)
+ {
+ if (testDomainParts[i + d].equals(""))
+ {
+ return false;
+ }
+ }
+ else if (!domainParts[i].equalsIgnoreCase(testDomainParts[i + d]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void checkPermittedDNS(Set permitted, String dns)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ // is sub domain
+ if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
+ {
+ return;
+ }
+ }
+ if (dns.length() == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+ throw new PKIXNameConstraintValidatorException(
+ "DNS is not from a permitted subtree.");
+ }
+
+ private void checkExcludedDNS(Set excluded, String dns)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ // is sub domain or the same
+ if (withinDomain(dns, str) || dns.equalsIgnoreCase(str))
+ {
+ throw new PKIXNameConstraintValidatorException(
+ "DNS is from an excluded subtree.");
+ }
+ }
+ }
+
+ /**
+ * The common part of <code>email1</code> and <code>email2</code> is
+ * added to the union <code>union</code>. If <code>email1</code> and
+ * <code>email2</code> have nothing in common they are added both.
+ *
+ * @param email1 Email address constraint 1.
+ * @param email2 Email address constraint 2.
+ * @param union The union.
+ */
+ private void unionEmail(String email1, String email2, Set union)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email1 specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ }
+
+ private void unionURI(String email1, String email2, Set union)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email1 specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ union.add(email2);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ // email specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ union.add(email2);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ union.add(email1);
+ }
+ else
+ {
+ union.add(email1);
+ union.add(email2);
+ }
+ }
+ }
+ }
+
+ private Set intersectDNS(Set permitted, Set dnss)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = dnss.iterator(); it.hasNext();)
+ {
+ String dns = extractNameAsString(((GeneralSubtree)it.next())
+ .getBase());
+ if (permitted == null)
+ {
+ if (dns != null)
+ {
+ intersect.add(dns);
+ }
+ }
+ else
+ {
+ Iterator _iter = permitted.iterator();
+ while (_iter.hasNext())
+ {
+ String _permitted = (String)_iter.next();
+
+ if (withinDomain(_permitted, dns))
+ {
+ intersect.add(_permitted);
+ }
+ else if (withinDomain(dns, _permitted))
+ {
+ intersect.add(dns);
+ }
+ }
+ }
+ }
+
+ return intersect;
+ }
+
+ protected Set unionDNS(Set excluded, String dns)
+ {
+ if (excluded.isEmpty())
+ {
+ if (dns == null)
+ {
+ return excluded;
+ }
+ excluded.add(dns);
+
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator _iter = excluded.iterator();
+ while (_iter.hasNext())
+ {
+ String _permitted = (String)_iter.next();
+
+ if (withinDomain(_permitted, dns))
+ {
+ union.add(dns);
+ }
+ else if (withinDomain(dns, _permitted))
+ {
+ union.add(_permitted);
+ }
+ else
+ {
+ union.add(_permitted);
+ union.add(dns);
+ }
+ }
+
+ return union;
+ }
+ }
+
+ /**
+ * The most restricting part from <code>email1</code> and
+ * <code>email2</code> is added to the intersection <code>intersect</code>.
+ *
+ * @param email1 Email address constraint 1.
+ * @param email2 Email address constraint 2.
+ * @param intersect The intersection.
+ */
+ private void intersectEmail(String email1, String email2, Set intersect)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ // email specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ }
+ // email1 specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email2.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ }
+
+ private void checkExcludedURI(Set excluded, String uri)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (excluded.isEmpty())
+ {
+ return;
+ }
+
+ Iterator it = excluded.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ if (isUriConstrained(uri, str))
+ {
+ throw new PKIXNameConstraintValidatorException(
+ "URI is from an excluded subtree.");
+ }
+ }
+ }
+
+ private Set intersectURI(Set permitted, Set uris)
+ {
+ Set intersect = new HashSet();
+ for (Iterator it = uris.iterator(); it.hasNext();)
+ {
+ String uri = extractNameAsString(((GeneralSubtree)it.next())
+ .getBase());
+ if (permitted == null)
+ {
+ if (uri != null)
+ {
+ intersect.add(uri);
+ }
+ }
+ else
+ {
+ Iterator _iter = permitted.iterator();
+ while (_iter.hasNext())
+ {
+ String _permitted = (String)_iter.next();
+ intersectURI(_permitted, uri, intersect);
+ }
+ }
+ }
+ return intersect;
+ }
+
+ private Set unionURI(Set excluded, String uri)
+ {
+ if (excluded.isEmpty())
+ {
+ if (uri == null)
+ {
+ return excluded;
+ }
+ excluded.add(uri);
+
+ return excluded;
+ }
+ else
+ {
+ Set union = new HashSet();
+
+ Iterator _iter = excluded.iterator();
+ while (_iter.hasNext())
+ {
+ String _excluded = (String)_iter.next();
+
+ unionURI(_excluded, uri, union);
+ }
+
+ return union;
+ }
+ }
+
+ private void intersectURI(String email1, String email2, Set intersect)
+ {
+ // email1 is a particular address
+ if (email1.indexOf('@') != -1)
+ {
+ String _sub = email1.substring(email1.indexOf('@') + 1);
+ // both are a particular mailbox
+ if (email2.indexOf('@') != -1)
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(_sub, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (_sub.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ // email specifies a domain
+ else if (email1.startsWith("."))
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email1.indexOf('@') + 1);
+ if (withinDomain(_sub, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2)
+ || email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ else if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ else
+ {
+ if (withinDomain(email2, email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ }
+ // email1 specifies a host
+ else
+ {
+ if (email2.indexOf('@') != -1)
+ {
+ String _sub = email2.substring(email2.indexOf('@') + 1);
+ if (_sub.equalsIgnoreCase(email1))
+ {
+ intersect.add(email2);
+ }
+ }
+ // email2 specifies a domain
+ else if (email2.startsWith("."))
+ {
+ if (withinDomain(email1, email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ // email2 specifies a particular host
+ else
+ {
+ if (email1.equalsIgnoreCase(email2))
+ {
+ intersect.add(email1);
+ }
+ }
+ }
+ }
+
+ private void checkPermittedURI(Set permitted, String uri)
+ throws PKIXNameConstraintValidatorException
+ {
+ if (permitted == null)
+ {
+ return;
+ }
+
+ Iterator it = permitted.iterator();
+
+ while (it.hasNext())
+ {
+ String str = ((String)it.next());
+
+ if (isUriConstrained(uri, str))
+ {
+ return;
+ }
+ }
+ if (uri.length() == 0 && permitted.size() == 0)
+ {
+ return;
+ }
+ throw new PKIXNameConstraintValidatorException(
+ "URI is not from a permitted subtree.");
+ }
+
+ private boolean isUriConstrained(String uri, String constraint)
+ {
+ String host = extractHostFromURL(uri);
+ // a host
+ if (!constraint.startsWith("."))
+ {
+ if (host.equalsIgnoreCase(constraint))
+ {
+ return true;
+ }
+ }
+
+ // in sub domain or domain
+ else if (withinDomain(host, constraint))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static String extractHostFromURL(String url)
+ {
+ // see RFC 1738
+ // remove ':' after protocol, e.g. http:
+ String sub = url.substring(url.indexOf(':') + 1);
+ // extract host from Common Internet Scheme Syntax, e.g. http://
+ if (sub.indexOf("//") != -1)
+ {
+ sub = sub.substring(sub.indexOf("//") + 2);
+ }
+ // first remove port, e.g. http://test.com:21
+ if (sub.lastIndexOf(':') != -1)
+ {
+ sub = sub.substring(0, sub.lastIndexOf(':'));
+ }
+ // remove user and password, e.g. http://john:password@test.com
+ sub = sub.substring(sub.indexOf(':') + 1);
+ sub = sub.substring(sub.indexOf('@') + 1);
+ // remove local parts, e.g. http://test.com/bla
+ if (sub.indexOf('/') != -1)
+ {
+ sub = sub.substring(0, sub.indexOf('/'));
+ }
+ return sub;
+ }
+
+ /**
+ * Checks if the given GeneralName is in the permitted set.
+ *
+ * @param name The GeneralName
+ * @throws PKIXNameConstraintValidatorException
+ * If the <code>name</code>
+ */
+ public void checkPermitted(GeneralName name)
+ throws PKIXNameConstraintValidatorException
+ {
+ switch (name.getTagNo())
+ {
+ case 1:
+ checkPermittedEmail(permittedSubtreesEmail,
+ extractNameAsString(name));
+ break;
+ case 2:
+ checkPermittedDNS(permittedSubtreesDNS, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case 4:
+ checkPermittedDN(ASN1Sequence.getInstance(name.getName()
+ .toASN1Primitive()));
+ break;
+ case 6:
+ checkPermittedURI(permittedSubtreesURI, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case 7:
+ byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
+
+ checkPermittedIP(permittedSubtreesIP, ip);
+ }
+ }
+
+ /**
+ * Check if the given GeneralName is contained in the excluded set.
+ *
+ * @param name The GeneralName.
+ * @throws PKIXNameConstraintValidatorException
+ * If the <code>name</code> is
+ * excluded.
+ */
+ public void checkExcluded(GeneralName name)
+ throws PKIXNameConstraintValidatorException
+ {
+ switch (name.getTagNo())
+ {
+ case 1:
+ checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name));
+ break;
+ case 2:
+ checkExcludedDNS(excludedSubtreesDNS, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case 4:
+ checkExcludedDN(ASN1Sequence.getInstance(name.getName()
+ .toASN1Primitive()));
+ break;
+ case 6:
+ checkExcludedURI(excludedSubtreesURI, DERIA5String.getInstance(
+ name.getName()).getString());
+ break;
+ case 7:
+ byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets();
+
+ checkExcludedIP(excludedSubtreesIP, ip);
+ }
+ }
+
+ /**
+ * Updates the permitted set of these name constraints with the intersection
+ * with the given subtree.
+ *
+ * @param permitted The permitted subtrees
+ */
+
+ public void intersectPermittedSubtree(ASN1Sequence permitted)
+ {
+ Map subtreesMap = new HashMap();
+
+ // group in sets in a map ordered by tag no.
+ for (Enumeration e = permitted.getObjects(); e.hasMoreElements();)
+ {
+ GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement());
+ // BEGIN android-changed
+ Integer tagNo = Integer.valueOf(subtree.getBase().getTagNo());
+ // END android-changed
+ if (subtreesMap.get(tagNo) == null)
+ {
+ subtreesMap.put(tagNo, new HashSet());
+ }
+ ((Set)subtreesMap.get(tagNo)).add(subtree);
+ }
+
+ for (Iterator it = subtreesMap.entrySet().iterator(); it.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry)it.next();
+
+ // go through all subtree groups
+ switch (((Integer)entry.getKey()).intValue())
+ {
+ case 1:
+ permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail,
+ (Set)entry.getValue());
+ break;
+ case 2:
+ permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS,
+ (Set)entry.getValue());
+ break;
+ case 4:
+ permittedSubtreesDN = intersectDN(permittedSubtreesDN,
+ (Set)entry.getValue());
+ break;
+ case 6:
+ permittedSubtreesURI = intersectURI(permittedSubtreesURI,
+ (Set)entry.getValue());
+ break;
+ case 7:
+ permittedSubtreesIP = intersectIP(permittedSubtreesIP,
+ (Set)entry.getValue());
+ }
+ }
+ }
+
+ private String extractNameAsString(GeneralName name)
+ {
+ return DERIA5String.getInstance(name.getName()).getString();
+ }
+
+ public void intersectEmptyPermittedSubtree(int nameType)
+ {
+ switch (nameType)
+ {
+ case 1:
+ permittedSubtreesEmail = new HashSet();
+ break;
+ case 2:
+ permittedSubtreesDNS = new HashSet();
+ break;
+ case 4:
+ permittedSubtreesDN = new HashSet();
+ break;
+ case 6:
+ permittedSubtreesURI = new HashSet();
+ break;
+ case 7:
+ permittedSubtreesIP = new HashSet();
+ }
+ }
+
+ /**
+ * Adds a subtree to the excluded set of these name constraints.
+ *
+ * @param subtree A subtree with an excluded GeneralName.
+ */
+ public void addExcludedSubtree(GeneralSubtree subtree)
+ {
+ GeneralName base = subtree.getBase();
+
+ switch (base.getTagNo())
+ {
+ case 1:
+ excludedSubtreesEmail = unionEmail(excludedSubtreesEmail,
+ extractNameAsString(base));
+ break;
+ case 2:
+ excludedSubtreesDNS = unionDNS(excludedSubtreesDNS,
+ extractNameAsString(base));
+ break;
+ case 4:
+ excludedSubtreesDN = unionDN(excludedSubtreesDN,
+ (ASN1Sequence)base.getName().toASN1Primitive());
+ break;
+ case 6:
+ excludedSubtreesURI = unionURI(excludedSubtreesURI,
+ extractNameAsString(base));
+ break;
+ case 7:
+ excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString
+ .getInstance(base.getName()).getOctets());
+ break;
+ }
+ }
+
+ /**
+ * Returns the maximum IP address.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return The maximum IP address.
+ */
+ private static byte[] max(byte[] ip1, byte[] ip2)
+ {
+ for (int i = 0; i < ip1.length; i++)
+ {
+ if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF))
+ {
+ return ip1;
+ }
+ }
+ return ip2;
+ }
+
+ /**
+ * Returns the minimum IP address.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return The minimum IP address.
+ */
+ private static byte[] min(byte[] ip1, byte[] ip2)
+ {
+ for (int i = 0; i < ip1.length; i++)
+ {
+ if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF))
+ {
+ return ip1;
+ }
+ }
+ return ip2;
+ }
+
+ /**
+ * Compares IP address <code>ip1</code> with <code>ip2</code>. If ip1
+ * is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1
+ * otherwise.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise.
+ */
+ private static int compareTo(byte[] ip1, byte[] ip2)
+ {
+ if (Arrays.areEqual(ip1, ip2))
+ {
+ return 0;
+ }
+ if (Arrays.areEqual(max(ip1, ip2), ip1))
+ {
+ return 1;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the logical OR of the IP addresses <code>ip1</code> and
+ * <code>ip2</code>.
+ *
+ * @param ip1 The first IP address.
+ * @param ip2 The second IP address.
+ * @return The OR of <code>ip1</code> and <code>ip2</code>.
+ */
+ private static byte[] or(byte[] ip1, byte[] ip2)
+ {
+ byte[] temp = new byte[ip1.length];
+ for (int i = 0; i < ip1.length; i++)
+ {
+ temp[i] = (byte)(ip1[i] | ip2[i]);
+ }
+ return temp;
+ }
+
+ public int hashCode()
+ {
+ return hashCollection(excludedSubtreesDN)
+ + hashCollection(excludedSubtreesDNS)
+ + hashCollection(excludedSubtreesEmail)
+ + hashCollection(excludedSubtreesIP)
+ + hashCollection(excludedSubtreesURI)
+ + hashCollection(permittedSubtreesDN)
+ + hashCollection(permittedSubtreesDNS)
+ + hashCollection(permittedSubtreesEmail)
+ + hashCollection(permittedSubtreesIP)
+ + hashCollection(permittedSubtreesURI);
+ }
+
+ private int hashCollection(Collection coll)
+ {
+ if (coll == null)
+ {
+ return 0;
+ }
+ int hash = 0;
+ Iterator it1 = coll.iterator();
+ while (it1.hasNext())
+ {
+ Object o = it1.next();
+ if (o instanceof byte[])
+ {
+ hash += Arrays.hashCode((byte[])o);
+ }
+ else
+ {
+ hash += o.hashCode();
+ }
+ }
+ return hash;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof PKIXNameConstraintValidator))
+ {
+ return false;
+ }
+ PKIXNameConstraintValidator constraintValidator = (PKIXNameConstraintValidator)o;
+ return collectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
+ && collectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
+ && collectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI);
+ }
+
+ private boolean collectionsAreEqual(Collection coll1, Collection coll2)
+ {
+ if (coll1 == coll2)
+ {
+ return true;
+ }
+ if (coll1 == null || coll2 == null)
+ {
+ return false;
+ }
+ if (coll1.size() != coll2.size())
+ {
+ return false;
+ }
+ Iterator it1 = coll1.iterator();
+
+ while (it1.hasNext())
+ {
+ Object a = it1.next();
+ Iterator it2 = coll2.iterator();
+ boolean found = false;
+ while (it2.hasNext())
+ {
+ Object b = it2.next();
+ if (equals(a, b))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean equals(Object o1, Object o2)
+ {
+ if (o1 == o2)
+ {
+ return true;
+ }
+ if (o1 == null || o2 == null)
+ {
+ return false;
+ }
+ if (o1 instanceof byte[] && o2 instanceof byte[])
+ {
+ return Arrays.areEqual((byte[])o1, (byte[])o2);
+ }
+ else
+ {
+ return o1.equals(o2);
+ }
+ }
+
+ /**
+ * Stringifies an IPv4 or v6 address with subnet mask.
+ *
+ * @param ip The IP with subnet mask.
+ * @return The stringified IP address.
+ */
+ private String stringifyIP(byte[] ip)
+ {
+ String temp = "";
+ for (int i = 0; i < ip.length / 2; i++)
+ {
+ temp += Integer.toString(ip[i] & 0x00FF) + ".";
+ }
+ temp = temp.substring(0, temp.length() - 1);
+ temp += "/";
+ for (int i = ip.length / 2; i < ip.length; i++)
+ {
+ temp += Integer.toString(ip[i] & 0x00FF) + ".";
+ }
+ temp = temp.substring(0, temp.length() - 1);
+ return temp;
+ }
+
+ private String stringifyIPCollection(Set ips)
+ {
+ String temp = "";
+ temp += "[";
+ for (Iterator it = ips.iterator(); it.hasNext();)
+ {
+ temp += stringifyIP((byte[])it.next()) + ",";
+ }
+ if (temp.length() > 1)
+ {
+ temp = temp.substring(0, temp.length() - 1);
+ }
+ temp += "]";
+ return temp;
+ }
+
+ public String toString()
+ {
+ String temp = "";
+ temp += "permitted:\n";
+ if (permittedSubtreesDN != null)
+ {
+ temp += "DN:\n";
+ temp += permittedSubtreesDN.toString() + "\n";
+ }
+ if (permittedSubtreesDNS != null)
+ {
+ temp += "DNS:\n";
+ temp += permittedSubtreesDNS.toString() + "\n";
+ }
+ if (permittedSubtreesEmail != null)
+ {
+ temp += "Email:\n";
+ temp += permittedSubtreesEmail.toString() + "\n";
+ }
+ if (permittedSubtreesURI != null)
+ {
+ temp += "URI:\n";
+ temp += permittedSubtreesURI.toString() + "\n";
+ }
+ if (permittedSubtreesIP != null)
+ {
+ temp += "IP:\n";
+ temp += stringifyIPCollection(permittedSubtreesIP) + "\n";
+ }
+ temp += "excluded:\n";
+ if (!excludedSubtreesDN.isEmpty())
+ {
+ temp += "DN:\n";
+ temp += excludedSubtreesDN.toString() + "\n";
+ }
+ if (!excludedSubtreesDNS.isEmpty())
+ {
+ temp += "DNS:\n";
+ temp += excludedSubtreesDNS.toString() + "\n";
+ }
+ if (!excludedSubtreesEmail.isEmpty())
+ {
+ temp += "Email:\n";
+ temp += excludedSubtreesEmail.toString() + "\n";
+ }
+ if (!excludedSubtreesURI.isEmpty())
+ {
+ temp += "URI:\n";
+ temp += excludedSubtreesURI.toString() + "\n";
+ }
+ if (!excludedSubtreesIP.isEmpty())
+ {
+ temp += "IP:\n";
+ temp += stringifyIPCollection(excludedSubtreesIP) + "\n";
+ }
+ return temp;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java
new file mode 100644
index 0000000..b06d5e5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.jce.provider;
+
+public class PKIXNameConstraintValidatorException
+ extends Exception
+{
+ public PKIXNameConstraintValidatorException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java
new file mode 100644
index 0000000..3437605
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java
@@ -0,0 +1,168 @@
+package org.bouncycastle.jce.provider;
+
+import java.security.cert.PolicyNode;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+public class PKIXPolicyNode
+ implements PolicyNode
+{
+ protected List children;
+ protected int depth;
+ protected Set expectedPolicies;
+ protected PolicyNode parent;
+ protected Set policyQualifiers;
+ protected String validPolicy;
+ protected boolean critical;
+
+ /*
+ *
+ * CONSTRUCTORS
+ *
+ */
+
+ public PKIXPolicyNode(
+ List _children,
+ int _depth,
+ Set _expectedPolicies,
+ PolicyNode _parent,
+ Set _policyQualifiers,
+ String _validPolicy,
+ boolean _critical)
+ {
+ children = _children;
+ depth = _depth;
+ expectedPolicies = _expectedPolicies;
+ parent = _parent;
+ policyQualifiers = _policyQualifiers;
+ validPolicy = _validPolicy;
+ critical = _critical;
+ }
+
+ public void addChild(
+ PKIXPolicyNode _child)
+ {
+ children.add(_child);
+ _child.setParent(this);
+ }
+
+ public Iterator getChildren()
+ {
+ return children.iterator();
+ }
+
+ public int getDepth()
+ {
+ return depth;
+ }
+
+ public Set getExpectedPolicies()
+ {
+ return expectedPolicies;
+ }
+
+ public PolicyNode getParent()
+ {
+ return parent;
+ }
+
+ public Set getPolicyQualifiers()
+ {
+ return policyQualifiers;
+ }
+
+ public String getValidPolicy()
+ {
+ return validPolicy;
+ }
+
+ public boolean hasChildren()
+ {
+ return !children.isEmpty();
+ }
+
+ public boolean isCritical()
+ {
+ return critical;
+ }
+
+ public void removeChild(PKIXPolicyNode _child)
+ {
+ children.remove(_child);
+ }
+
+ public void setCritical(boolean _critical)
+ {
+ critical = _critical;
+ }
+
+ public void setParent(PKIXPolicyNode _parent)
+ {
+ parent = _parent;
+ }
+
+ public String toString()
+ {
+ return toString("");
+ }
+
+ public String toString(String _indent)
+ {
+ StringBuffer _buf = new StringBuffer();
+ _buf.append(_indent);
+ _buf.append(validPolicy);
+ _buf.append(" {\n");
+
+ for(int i = 0; i < children.size(); i++)
+ {
+ _buf.append(((PKIXPolicyNode)children.get(i)).toString(_indent + " "));
+ }
+
+ _buf.append(_indent);
+ _buf.append("}\n");
+ return _buf.toString();
+ }
+
+ public Object clone()
+ {
+ return copy();
+ }
+
+ public PKIXPolicyNode copy()
+ {
+ Set _expectedPolicies = new HashSet();
+ Iterator _iter = expectedPolicies.iterator();
+ while (_iter.hasNext())
+ {
+ _expectedPolicies.add(new String((String)_iter.next()));
+ }
+
+ Set _policyQualifiers = new HashSet();
+ _iter = policyQualifiers.iterator();
+ while (_iter.hasNext())
+ {
+ _policyQualifiers.add(new String((String)_iter.next()));
+ }
+
+ PKIXPolicyNode _node = new PKIXPolicyNode(new ArrayList(),
+ depth,
+ _expectedPolicies,
+ null,
+ _policyQualifiers,
+ new String(validPolicy),
+ critical);
+
+ _iter = children.iterator();
+ while (_iter.hasNext())
+ {
+ PKIXPolicyNode _child = ((PKIXPolicyNode)_iter.next()).copy();
+ _child.setParent(_node);
+ _node.addChild(_child);
+ }
+
+ return _node;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
new file mode 100644
index 0000000..7357894
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java
@@ -0,0 +1,2569 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509Extension;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+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 java.util.Vector;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.GeneralSubtree;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.asn1.x509.NameConstraints;
+import org.bouncycastle.asn1.x509.PolicyInformation;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.jce.exception.ExtCertPathValidatorException;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
+import org.bouncycastle.x509.ExtendedPKIXParameters;
+import org.bouncycastle.x509.X509CRLStoreSelector;
+import org.bouncycastle.x509.X509CertStoreSelector;
+
+public class RFC3280CertPathUtilities
+{
+ private static final PKIXCRLUtil CRL_UTIL = new PKIXCRLUtil();
+
+ /**
+ * If the complete CRL includes an issuing distribution point (IDP) CRL
+ * extension check the following:
+ * <p/>
+ * (i) If the distribution point name is present in the IDP CRL extension
+ * and the distribution field is present in the DP, then verify that one of
+ * the names in the IDP matches one of the names in the DP. If the
+ * distribution point name is present in the IDP CRL extension and the
+ * distribution field is omitted from the DP, then verify that one of the
+ * names in the IDP matches one of the names in the cRLIssuer field of the
+ * DP.
+ * </p>
+ * <p/>
+ * (ii) If the onlyContainsUserCerts boolean is asserted in the IDP CRL
+ * extension, verify that the certificate does not include the basic
+ * constraints extension with the cA boolean asserted.
+ * </p>
+ * <p/>
+ * (iii) If the onlyContainsCACerts boolean is asserted in the IDP CRL
+ * extension, verify that the certificate includes the basic constraints
+ * extension with the cA boolean asserted.
+ * </p>
+ * <p/>
+ * (iv) Verify that the onlyContainsAttributeCerts boolean is not asserted.
+ * </p>
+ *
+ * @param dp The distribution point.
+ * @param cert The certificate.
+ * @param crl The CRL.
+ * @throws AnnotatedException if one of the conditions is not met or an error occurs.
+ */
+ protected static void processCRLB2(
+ DistributionPoint dp,
+ Object cert,
+ X509CRL crl)
+ throws AnnotatedException
+ {
+ IssuingDistributionPoint idp = null;
+ try
+ {
+ idp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(crl,
+ RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+ }
+ // (b) (2) (i)
+ // distribution point name is present
+ if (idp != null)
+ {
+ if (idp.getDistributionPoint() != null)
+ {
+ // make list of names
+ DistributionPointName dpName = IssuingDistributionPoint.getInstance(idp).getDistributionPoint();
+ List names = new ArrayList();
+
+ if (dpName.getType() == DistributionPointName.FULL_NAME)
+ {
+ GeneralName[] genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+ for (int j = 0; j < genNames.length; j++)
+ {
+ names.add(genNames[j]);
+ }
+ }
+ if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+ {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ try
+ {
+ Enumeration e = ASN1Sequence.getInstance(
+ ASN1Sequence.fromByteArray(CertPathValidatorUtilities.getIssuerPrincipal(crl)
+ .getEncoded())).getObjects();
+ while (e.hasMoreElements())
+ {
+ vec.add((ASN1Encodable)e.nextElement());
+ }
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException("Could not read CRL issuer.", e);
+ }
+ vec.add(dpName.getName());
+ names.add(new GeneralName(X509Name.getInstance(new DERSequence(vec))));
+ }
+ boolean matches = false;
+ // verify that one of the names in the IDP matches one
+ // of the names in the DP.
+ if (dp.getDistributionPoint() != null)
+ {
+ dpName = dp.getDistributionPoint();
+ GeneralName[] genNames = null;
+ if (dpName.getType() == DistributionPointName.FULL_NAME)
+ {
+ genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+ }
+ if (dpName.getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+ {
+ if (dp.getCRLIssuer() != null)
+ {
+ genNames = dp.getCRLIssuer().getNames();
+ }
+ else
+ {
+ genNames = new GeneralName[1];
+ try
+ {
+ genNames[0] = new GeneralName(new X509Name(
+ (ASN1Sequence)ASN1Sequence.fromByteArray(CertPathValidatorUtilities
+ .getEncodedIssuerPrincipal(cert).getEncoded())));
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException("Could not read certificate issuer.", e);
+ }
+ }
+ for (int j = 0; j < genNames.length; j++)
+ {
+ Enumeration e = ASN1Sequence.getInstance(genNames[j].getName().toASN1Primitive()).getObjects();
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ while (e.hasMoreElements())
+ {
+ vec.add((ASN1Encodable)e.nextElement());
+ }
+ vec.add(dpName.getName());
+ genNames[j] = new GeneralName(new X509Name(new DERSequence(vec)));
+ }
+ }
+ if (genNames != null)
+ {
+ for (int j = 0; j < genNames.length; j++)
+ {
+ if (names.contains(genNames[j]))
+ {
+ matches = true;
+ break;
+ }
+ }
+ }
+ if (!matches)
+ {
+ throw new AnnotatedException(
+ "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+ }
+ }
+ // verify that one of the names in
+ // the IDP matches one of the names in the cRLIssuer field of
+ // the DP
+ else
+ {
+ if (dp.getCRLIssuer() == null)
+ {
+ throw new AnnotatedException("Either the cRLIssuer or the distributionPoint field must "
+ + "be contained in DistributionPoint.");
+ }
+ GeneralName[] genNames = dp.getCRLIssuer().getNames();
+ for (int j = 0; j < genNames.length; j++)
+ {
+ if (names.contains(genNames[j]))
+ {
+ matches = true;
+ break;
+ }
+ }
+ if (!matches)
+ {
+ throw new AnnotatedException(
+ "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+ }
+ }
+ }
+ BasicConstraints bc = null;
+ try
+ {
+ bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue((X509Extension)cert,
+ BASIC_CONSTRAINTS));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Basic constraints extension could not be decoded.", e);
+ }
+
+ if (cert instanceof X509Certificate)
+ {
+ // (b) (2) (ii)
+ if (idp.onlyContainsUserCerts() && (bc != null && bc.isCA()))
+ {
+ throw new AnnotatedException("CA Cert CRL only contains user certificates.");
+ }
+
+ // (b) (2) (iii)
+ if (idp.onlyContainsCACerts() && (bc == null || !bc.isCA()))
+ {
+ throw new AnnotatedException("End CRL only contains CA certificates.");
+ }
+ }
+
+ // (b) (2) (iv)
+ if (idp.onlyContainsAttributeCerts())
+ {
+ throw new AnnotatedException("onlyContainsAttributeCerts boolean is asserted.");
+ }
+ }
+ }
+
+ /**
+ * If the DP includes cRLIssuer, then verify that the issuer field in the
+ * complete CRL matches cRLIssuer in the DP and that the complete CRL
+ * contains an issuing distribution point extension with the indirectCRL
+ * boolean asserted. Otherwise, verify that the CRL issuer matches the
+ * certificate issuer.
+ *
+ * @param dp The distribution point.
+ * @param cert The certificate ot attribute certificate.
+ * @param crl The CRL for <code>cert</code>.
+ * @throws AnnotatedException if one of the above conditions does not apply or an error
+ * occurs.
+ */
+ protected static void processCRLB1(
+ DistributionPoint dp,
+ Object cert,
+ X509CRL crl)
+ throws AnnotatedException
+ {
+ ASN1Primitive idp = CertPathValidatorUtilities.getExtensionValue(crl, ISSUING_DISTRIBUTION_POINT);
+ boolean isIndirect = false;
+ if (idp != null)
+ {
+ if (IssuingDistributionPoint.getInstance(idp).isIndirectCRL())
+ {
+ isIndirect = true;
+ }
+ }
+ byte[] issuerBytes = CertPathValidatorUtilities.getIssuerPrincipal(crl).getEncoded();
+
+ boolean matchIssuer = false;
+ if (dp.getCRLIssuer() != null)
+ {
+ GeneralName genNames[] = dp.getCRLIssuer().getNames();
+ for (int j = 0; j < genNames.length; j++)
+ {
+ if (genNames[j].getTagNo() == GeneralName.directoryName)
+ {
+ try
+ {
+ if (Arrays.areEqual(genNames[j].getName().toASN1Primitive().getEncoded(), issuerBytes))
+ {
+ matchIssuer = true;
+ }
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException(
+ "CRL issuer information from distribution point cannot be decoded.", e);
+ }
+ }
+ }
+ if (matchIssuer && !isIndirect)
+ {
+ throw new AnnotatedException("Distribution point contains cRLIssuer field but CRL is not indirect.");
+ }
+ if (!matchIssuer)
+ {
+ throw new AnnotatedException("CRL issuer of CRL does not match CRL issuer of distribution point.");
+ }
+ }
+ else
+ {
+ if (CertPathValidatorUtilities.getIssuerPrincipal(crl).equals(
+ CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert)))
+ {
+ matchIssuer = true;
+ }
+ }
+ if (!matchIssuer)
+ {
+ throw new AnnotatedException("Cannot find matching CRL issuer for certificate.");
+ }
+ }
+
+ protected static ReasonsMask processCRLD(
+ X509CRL crl,
+ DistributionPoint dp)
+ throws AnnotatedException
+ {
+ IssuingDistributionPoint idp = null;
+ try
+ {
+ idp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(crl,
+ RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+ }
+ // (d) (1)
+ if (idp != null && idp.getOnlySomeReasons() != null && dp.getReasons() != null)
+ {
+ return new ReasonsMask(dp.getReasons()).intersect(new ReasonsMask(idp.getOnlySomeReasons()));
+ }
+ // (d) (4)
+ if ((idp == null || idp.getOnlySomeReasons() == null) && dp.getReasons() == null)
+ {
+ return ReasonsMask.allReasons;
+ }
+ // (d) (2) and (d)(3)
+ return (dp.getReasons() == null
+ ? ReasonsMask.allReasons
+ : new ReasonsMask(dp.getReasons())).intersect(idp == null
+ ? ReasonsMask.allReasons
+ : new ReasonsMask(idp.getOnlySomeReasons()));
+
+ }
+
+ protected static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId();
+
+ protected static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId();
+
+ protected static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId();
+
+ protected static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId();
+
+ protected static final String FRESHEST_CRL = X509Extensions.FreshestCRL.getId();
+
+ protected static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId();
+
+ protected static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId();
+
+ protected static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId();
+
+ protected static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints.getId();
+
+ protected static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId();
+
+ protected static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId();
+
+ protected static final String AUTHORITY_KEY_IDENTIFIER = X509Extensions.AuthorityKeyIdentifier.getId();
+
+ protected static final String KEY_USAGE = X509Extensions.KeyUsage.getId();
+
+ protected static final String CRL_NUMBER = X509Extensions.CRLNumber.getId();
+
+ protected static final String ANY_POLICY = "2.5.29.32.0";
+
+ /*
+ * key usage bits
+ */
+ protected static final int KEY_CERT_SIGN = 5;
+
+ protected static final int CRL_SIGN = 6;
+
+ /**
+ * Obtain and validate the certification path for the complete CRL issuer.
+ * If a key usage extension is present in the CRL issuer's certificate,
+ * verify that the cRLSign bit is set.
+ *
+ * @param crl CRL which contains revocation information for the certificate
+ * <code>cert</code>.
+ * @param cert The attribute certificate or certificate to check if it is
+ * revoked.
+ * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+ * @param defaultCRLSignKey The public key of the issuer certificate
+ * <code>defaultCRLSignCert</code>.
+ * @param paramsPKIX paramsPKIX PKIX parameters.
+ * @param certPathCerts The certificates on the certification path.
+ * @return A <code>Set</code> with all keys of possible CRL issuer
+ * certificates.
+ * @throws AnnotatedException if the CRL is not valid or the status cannot be checked or
+ * some error occurs.
+ */
+ protected static Set processCRLF(
+ X509CRL crl,
+ Object cert,
+ X509Certificate defaultCRLSignCert,
+ PublicKey defaultCRLSignKey,
+ ExtendedPKIXParameters paramsPKIX,
+ List certPathCerts)
+ throws AnnotatedException
+ {
+ // (f)
+
+ // get issuer from CRL
+ X509CertStoreSelector selector = new X509CertStoreSelector();
+ try
+ {
+ byte[] issuerPrincipal = CertPathValidatorUtilities.getIssuerPrincipal(crl).getEncoded();
+ selector.setSubject(issuerPrincipal);
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException(
+ "Subject criteria for certificate selector to find issuer certificate for CRL could not be set.", e);
+ }
+
+ // get CRL signing certs
+ Collection coll;
+ try
+ {
+ coll = CertPathValidatorUtilities.findCertificates(selector, paramsPKIX.getStores());
+ coll.addAll(CertPathValidatorUtilities.findCertificates(selector, paramsPKIX.getAdditionalStores()));
+ coll.addAll(CertPathValidatorUtilities.findCertificates(selector, paramsPKIX.getCertStores()));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Issuer certificate for CRL cannot be searched.", e);
+ }
+
+ coll.add(defaultCRLSignCert);
+
+ Iterator cert_it = coll.iterator();
+
+ List validCerts = new ArrayList();
+ List validKeys = new ArrayList();
+
+ while (cert_it.hasNext())
+ {
+ X509Certificate signingCert = (X509Certificate)cert_it.next();
+
+ /*
+ * CA of the certificate, for which this CRL is checked, has also
+ * signed CRL, so skip the path validation, because is already done
+ */
+ if (signingCert.equals(defaultCRLSignCert))
+ {
+ validCerts.add(signingCert);
+ validKeys.add(defaultCRLSignKey);
+ continue;
+ }
+ try
+ {
+ CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME);
+ selector = new X509CertStoreSelector();
+ selector.setCertificate(signingCert);
+ ExtendedPKIXParameters temp = (ExtendedPKIXParameters)paramsPKIX.clone();
+ temp.setTargetCertConstraints(selector);
+ ExtendedPKIXBuilderParameters params = (ExtendedPKIXBuilderParameters)ExtendedPKIXBuilderParameters
+ .getInstance(temp);
+ /*
+ * if signingCert is placed not higher on the cert path a
+ * dependency loop results. CRL for cert is checked, but
+ * signingCert is needed for checking the CRL which is dependent
+ * on checking cert because it is higher in the cert path and so
+ * signing signingCert transitively. so, revocation is disabled,
+ * forgery attacks of the CRL are detected in this outer loop
+ * for all other it must be enabled to prevent forgery attacks
+ */
+ if (certPathCerts.contains(signingCert))
+ {
+ params.setRevocationEnabled(false);
+ }
+ else
+ {
+ params.setRevocationEnabled(true);
+ }
+ List certs = builder.build(params).getCertPath().getCertificates();
+ validCerts.add(signingCert);
+ validKeys.add(CertPathValidatorUtilities.getNextWorkingKey(certs, 0));
+ }
+ catch (CertPathBuilderException e)
+ {
+ throw new AnnotatedException("Internal error.", e);
+ }
+ catch (CertPathValidatorException e)
+ {
+ throw new AnnotatedException("Public key of issuer certificate of CRL could not be retrieved.", e);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ Set checkKeys = new HashSet();
+
+ AnnotatedException lastException = null;
+ for (int i = 0; i < validCerts.size(); i++)
+ {
+ X509Certificate signCert = (X509Certificate)validCerts.get(i);
+ boolean[] keyusage = signCert.getKeyUsage();
+
+ if (keyusage != null && (keyusage.length < 7 || !keyusage[CRL_SIGN]))
+ {
+ lastException = new AnnotatedException(
+ "Issuer certificate key usage extension does not permit CRL signing.");
+ }
+ else
+ {
+ checkKeys.add(validKeys.get(i));
+ }
+ }
+
+ if (checkKeys.isEmpty() && lastException == null)
+ {
+ throw new AnnotatedException("Cannot find a valid issuer certificate.");
+ }
+ if (checkKeys.isEmpty() && lastException != null)
+ {
+ throw lastException;
+ }
+
+ return checkKeys;
+ }
+
+ protected static PublicKey processCRLG(
+ X509CRL crl,
+ Set keys)
+ throws AnnotatedException
+ {
+ Exception lastException = null;
+ for (Iterator it = keys.iterator(); it.hasNext();)
+ {
+ PublicKey key = (PublicKey)it.next();
+ try
+ {
+ crl.verify(key);
+ return key;
+ }
+ catch (Exception e)
+ {
+ lastException = e;
+ }
+ }
+ throw new AnnotatedException("Cannot verify CRL.", lastException);
+ }
+
+ protected static X509CRL processCRLH(
+ Set deltacrls,
+ PublicKey key)
+ throws AnnotatedException
+ {
+ Exception lastException = null;
+
+ for (Iterator it = deltacrls.iterator(); it.hasNext();)
+ {
+ X509CRL crl = (X509CRL)it.next();
+ try
+ {
+ crl.verify(key);
+ return crl;
+ }
+ catch (Exception e)
+ {
+ lastException = e;
+ }
+ }
+
+ if (lastException != null)
+ {
+ throw new AnnotatedException("Cannot verify delta CRL.", lastException);
+ }
+ return null;
+ }
+
+ protected static Set processCRLA1i(
+ Date currentDate,
+ ExtendedPKIXParameters paramsPKIX,
+ X509Certificate cert,
+ X509CRL crl)
+ throws AnnotatedException
+ {
+ Set set = new HashSet();
+ if (paramsPKIX.isUseDeltasEnabled())
+ {
+ CRLDistPoint freshestCRL = null;
+ try
+ {
+ freshestCRL = CRLDistPoint
+ .getInstance(CertPathValidatorUtilities.getExtensionValue(cert, FRESHEST_CRL));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Freshest CRL extension could not be decoded from certificate.", e);
+ }
+ if (freshestCRL == null)
+ {
+ try
+ {
+ freshestCRL = CRLDistPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(crl,
+ FRESHEST_CRL));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Freshest CRL extension could not be decoded from CRL.", e);
+ }
+ }
+ if (freshestCRL != null)
+ {
+ try
+ {
+ CertPathValidatorUtilities.addAdditionalStoresFromCRLDistributionPoint(freshestCRL, paramsPKIX);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException(
+ "No new delta CRL locations could be added from Freshest CRL extension.", e);
+ }
+ // get delta CRL(s)
+ try
+ {
+ set.addAll(CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Exception obtaining delta CRLs.", e);
+ }
+ }
+ }
+ return set;
+ }
+
+ protected static Set[] processCRLA1ii(
+ Date currentDate,
+ ExtendedPKIXParameters paramsPKIX,
+ X509Certificate cert,
+ X509CRL crl)
+ throws AnnotatedException
+ {
+ Set deltaSet = new HashSet();
+ X509CRLStoreSelector crlselect = new X509CRLStoreSelector();
+ crlselect.setCertificateChecking(cert);
+
+ try
+ {
+ crlselect.addIssuerName(crl.getIssuerX500Principal().getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new AnnotatedException("Cannot extract issuer from CRL." + e, e);
+ }
+
+ crlselect.setCompleteCRLEnabled(true);
+ Set completeSet = CRL_UTIL.findCRLs(crlselect, paramsPKIX, currentDate);
+
+ if (paramsPKIX.isUseDeltasEnabled())
+ {
+ // get delta CRL(s)
+ try
+ {
+ deltaSet.addAll(CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException("Exception obtaining delta CRLs.", e);
+ }
+ }
+ return new Set[]
+ {
+ completeSet,
+ deltaSet};
+ }
+
+
+
+ /**
+ * If use-deltas is set, verify the issuer and scope of the delta CRL.
+ *
+ * @param deltaCRL The delta CRL.
+ * @param completeCRL The complete CRL.
+ * @param pkixParams The PKIX paramaters.
+ * @throws AnnotatedException if an exception occurs.
+ */
+ protected static void processCRLC(
+ X509CRL deltaCRL,
+ X509CRL completeCRL,
+ ExtendedPKIXParameters pkixParams)
+ throws AnnotatedException
+ {
+ if (deltaCRL == null)
+ {
+ return;
+ }
+ IssuingDistributionPoint completeidp = null;
+ try
+ {
+ completeidp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(
+ completeCRL, RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Issuing distribution point extension could not be decoded.", e);
+ }
+
+ if (pkixParams.isUseDeltasEnabled())
+ {
+ // (c) (1)
+ if (!deltaCRL.getIssuerX500Principal().equals(completeCRL.getIssuerX500Principal()))
+ {
+ throw new AnnotatedException("Complete CRL issuer does not match delta CRL issuer.");
+ }
+
+ // (c) (2)
+ IssuingDistributionPoint deltaidp = null;
+ try
+ {
+ deltaidp = IssuingDistributionPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(
+ deltaCRL, ISSUING_DISTRIBUTION_POINT));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException(
+ "Issuing distribution point extension from delta CRL could not be decoded.", e);
+ }
+
+ boolean match = false;
+ if (completeidp == null)
+ {
+ if (deltaidp == null)
+ {
+ match = true;
+ }
+ }
+ else
+ {
+ if (completeidp.equals(deltaidp))
+ {
+ match = true;
+ }
+ }
+ if (!match)
+ {
+ throw new AnnotatedException(
+ "Issuing distribution point extension from delta CRL and complete CRL does not match.");
+ }
+
+ // (c) (3)
+ ASN1Primitive completeKeyIdentifier = null;
+ try
+ {
+ completeKeyIdentifier = CertPathValidatorUtilities.getExtensionValue(
+ completeCRL, AUTHORITY_KEY_IDENTIFIER);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException(
+ "Authority key identifier extension could not be extracted from complete CRL.", e);
+ }
+
+ ASN1Primitive deltaKeyIdentifier = null;
+ try
+ {
+ deltaKeyIdentifier = CertPathValidatorUtilities.getExtensionValue(
+ deltaCRL, AUTHORITY_KEY_IDENTIFIER);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException(
+ "Authority key identifier extension could not be extracted from delta CRL.", e);
+ }
+
+ if (completeKeyIdentifier == null)
+ {
+ throw new AnnotatedException("CRL authority key identifier is null.");
+ }
+
+ if (deltaKeyIdentifier == null)
+ {
+ throw new AnnotatedException("Delta CRL authority key identifier is null.");
+ }
+
+ if (!completeKeyIdentifier.equals(deltaKeyIdentifier))
+ {
+ throw new AnnotatedException(
+ "Delta CRL authority key identifier does not match complete CRL authority key identifier.");
+ }
+ }
+ }
+
+ protected static void processCRLI(
+ Date validDate,
+ X509CRL deltacrl,
+ Object cert,
+ CertStatus certStatus,
+ ExtendedPKIXParameters pkixParams)
+ throws AnnotatedException
+ {
+ if (pkixParams.isUseDeltasEnabled() && deltacrl != null)
+ {
+ CertPathValidatorUtilities.getCertStatus(validDate, deltacrl, cert, certStatus);
+ }
+ }
+
+ protected static void processCRLJ(
+ Date validDate,
+ X509CRL completecrl,
+ Object cert,
+ CertStatus certStatus)
+ throws AnnotatedException
+ {
+ if (certStatus.getCertStatus() == CertStatus.UNREVOKED)
+ {
+ CertPathValidatorUtilities.getCertStatus(validDate, completecrl, cert, certStatus);
+ }
+ }
+
+ protected static PKIXPolicyNode prepareCertB(
+ CertPath certPath,
+ int index,
+ List[] policyNodes,
+ PKIXPolicyNode validPolicyTree,
+ int policyMapping)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ int n = certs.size();
+ // i as defined in the algorithm description
+ int i = n - index;
+ // (b)
+ //
+ ASN1Sequence pm = null;
+ try
+ {
+ pm = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.POLICY_MAPPINGS));
+ }
+ catch (AnnotatedException ex)
+ {
+ throw new ExtCertPathValidatorException("Policy mappings extension could not be decoded.", ex, certPath,
+ index);
+ }
+ PKIXPolicyNode _validPolicyTree = validPolicyTree;
+ if (pm != null)
+ {
+ ASN1Sequence mappings = (ASN1Sequence)pm;
+ Map m_idp = new HashMap();
+ Set s_idp = new HashSet();
+
+ for (int j = 0; j < mappings.size(); j++)
+ {
+ ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j);
+ String id_p = ((DERObjectIdentifier)mapping.getObjectAt(0)).getId();
+ String sd_p = ((DERObjectIdentifier)mapping.getObjectAt(1)).getId();
+ Set tmp;
+
+ if (!m_idp.containsKey(id_p))
+ {
+ tmp = new HashSet();
+ tmp.add(sd_p);
+ m_idp.put(id_p, tmp);
+ s_idp.add(id_p);
+ }
+ else
+ {
+ tmp = (Set)m_idp.get(id_p);
+ tmp.add(sd_p);
+ }
+ }
+
+ Iterator it_idp = s_idp.iterator();
+ while (it_idp.hasNext())
+ {
+ String id_p = (String)it_idp.next();
+
+ //
+ // (1)
+ //
+ if (policyMapping > 0)
+ {
+ boolean idp_found = false;
+ Iterator nodes_i = policyNodes[i].iterator();
+ while (nodes_i.hasNext())
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+ if (node.getValidPolicy().equals(id_p))
+ {
+ idp_found = true;
+ node.expectedPolicies = (Set)m_idp.get(id_p);
+ break;
+ }
+ }
+
+ if (!idp_found)
+ {
+ nodes_i = policyNodes[i].iterator();
+ while (nodes_i.hasNext())
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(node.getValidPolicy()))
+ {
+ Set pq = null;
+ ASN1Sequence policies = null;
+ try
+ {
+ policies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new ExtCertPathValidatorException(
+ "Certificate policies extension could not be decoded.", e, certPath, index);
+ }
+ Enumeration e = policies.getObjects();
+ while (e.hasMoreElements())
+ {
+ PolicyInformation pinfo = null;
+ try
+ {
+ pinfo = PolicyInformation.getInstance(e.nextElement());
+ }
+ catch (Exception ex)
+ {
+ throw new CertPathValidatorException(
+ "Policy information could not be decoded.", ex, certPath, index);
+ }
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId()))
+ {
+ try
+ {
+ pq = CertPathValidatorUtilities
+ .getQualifierSet(pinfo.getPolicyQualifiers());
+ }
+ catch (CertPathValidatorException ex)
+ {
+
+ throw new ExtCertPathValidatorException(
+ "Policy qualifier info set could not be decoded.", ex, certPath,
+ index);
+ }
+ break;
+ }
+ }
+ boolean ci = false;
+ if (cert.getCriticalExtensionOIDs() != null)
+ {
+ ci = cert.getCriticalExtensionOIDs().contains(
+ RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+ }
+
+ PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(p_node.getValidPolicy()))
+ {
+ PKIXPolicyNode c_node = new PKIXPolicyNode(new ArrayList(), i, (Set)m_idp
+ .get(id_p), p_node, pq, id_p, ci);
+ p_node.addChild(c_node);
+ policyNodes[i].add(c_node);
+ }
+ break;
+ }
+ }
+ }
+
+ //
+ // (2)
+ //
+ }
+ else if (policyMapping <= 0)
+ {
+ Iterator nodes_i = policyNodes[i].iterator();
+ while (nodes_i.hasNext())
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next();
+ if (node.getValidPolicy().equals(id_p))
+ {
+ PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent();
+ p_node.removeChild(node);
+ nodes_i.remove();
+ for (int k = (i - 1); k >= 0; k--)
+ {
+ List nodes = policyNodes[k];
+ for (int l = 0; l < nodes.size(); l++)
+ {
+ PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l);
+ if (!node2.hasChildren())
+ {
+ _validPolicyTree = CertPathValidatorUtilities.removePolicyNode(
+ _validPolicyTree, policyNodes, node2);
+ if (_validPolicyTree == null)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return _validPolicyTree;
+ }
+
+ protected static void prepareNextCertA(
+ CertPath certPath,
+ int index)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ //
+ // (a) check the policy mappings
+ //
+ ASN1Sequence pm = null;
+ try
+ {
+ pm = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.POLICY_MAPPINGS));
+ }
+ catch (AnnotatedException ex)
+ {
+ throw new ExtCertPathValidatorException("Policy mappings extension could not be decoded.", ex, certPath,
+ index);
+ }
+ if (pm != null)
+ {
+ ASN1Sequence mappings = pm;
+
+ for (int j = 0; j < mappings.size(); j++)
+ {
+ DERObjectIdentifier issuerDomainPolicy = null;
+ DERObjectIdentifier subjectDomainPolicy = null;
+ try
+ {
+ ASN1Sequence mapping = DERSequence.getInstance(mappings.getObjectAt(j));
+
+ issuerDomainPolicy = DERObjectIdentifier.getInstance(mapping.getObjectAt(0));
+ subjectDomainPolicy = DERObjectIdentifier.getInstance(mapping.getObjectAt(1));
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Policy mappings extension contents could not be decoded.",
+ e, certPath, index);
+ }
+
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(issuerDomainPolicy.getId()))
+ {
+
+ throw new CertPathValidatorException("IssuerDomainPolicy is anyPolicy", null, certPath, index);
+ }
+
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(subjectDomainPolicy.getId()))
+ {
+
+ throw new CertPathValidatorException("SubjectDomainPolicy is anyPolicy,", null, certPath, index);
+ }
+ }
+ }
+ }
+
+ protected static void processCertF(
+ CertPath certPath,
+ int index,
+ PKIXPolicyNode validPolicyTree,
+ int explicitPolicy)
+ throws CertPathValidatorException
+ {
+ //
+ // (f)
+ //
+ if (explicitPolicy <= 0 && validPolicyTree == null)
+ {
+ throw new ExtCertPathValidatorException("No valid policy tree found when one expected.", null, certPath,
+ index);
+ }
+ }
+
+ protected static PKIXPolicyNode processCertE(
+ CertPath certPath,
+ int index,
+ PKIXPolicyNode validPolicyTree)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (e)
+ //
+ ASN1Sequence certPolicies = null;
+ try
+ {
+ certPolicies = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.CERTIFICATE_POLICIES));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new ExtCertPathValidatorException("Could not read certificate policies extension from certificate.",
+ e, certPath, index);
+ }
+ if (certPolicies == null)
+ {
+ validPolicyTree = null;
+ }
+ return validPolicyTree;
+ }
+
+ protected static void processCertBC(
+ CertPath certPath,
+ int index,
+ PKIXNameConstraintValidator nameConstraintValidator)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ int n = certs.size();
+ // i as defined in the algorithm description
+ int i = n - index;
+ //
+ // (b), (c) permitted and excluded subtree checking.
+ //
+ if (!(CertPathValidatorUtilities.isSelfIssued(cert) && (i < n)))
+ {
+ X500Principal principal = CertPathValidatorUtilities.getSubjectPrincipal(cert);
+ ASN1InputStream aIn = new ASN1InputStream(principal.getEncoded());
+ ASN1Sequence dns;
+
+ try
+ {
+ dns = DERSequence.getInstance(aIn.readObject());
+ }
+ catch (Exception e)
+ {
+ throw new CertPathValidatorException("Exception extracting subject name when checking subtrees.", e,
+ certPath, index);
+ }
+
+ try
+ {
+ nameConstraintValidator.checkPermittedDN(dns);
+ nameConstraintValidator.checkExcludedDN(dns);
+ }
+ catch (PKIXNameConstraintValidatorException e)
+ {
+ throw new CertPathValidatorException("Subtree check for certificate subject failed.", e, certPath,
+ index);
+ }
+
+ GeneralNames altName = null;
+ try
+ {
+ altName = GeneralNames.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME));
+ }
+ catch (Exception e)
+ {
+ throw new CertPathValidatorException("Subject alternative name extension could not be decoded.", e,
+ certPath, index);
+ }
+ Vector emails = new X509Name(dns).getValues(X509Name.EmailAddress);
+ for (Enumeration e = emails.elements(); e.hasMoreElements();)
+ {
+ String email = (String)e.nextElement();
+ GeneralName emailAsGeneralName = new GeneralName(GeneralName.rfc822Name, email);
+ try
+ {
+ nameConstraintValidator.checkPermitted(emailAsGeneralName);
+ nameConstraintValidator.checkExcluded(emailAsGeneralName);
+ }
+ catch (PKIXNameConstraintValidatorException ex)
+ {
+ throw new CertPathValidatorException(
+ "Subtree check for certificate subject alternative email failed.", ex, certPath, index);
+ }
+ }
+ if (altName != null)
+ {
+ GeneralName[] genNames = null;
+ try
+ {
+ genNames = altName.getNames();
+ }
+ catch (Exception e)
+ {
+ throw new CertPathValidatorException("Subject alternative name contents could not be decoded.", e,
+ certPath, index);
+ }
+ for (int j = 0; j < genNames.length; j++)
+ {
+
+ try
+ {
+ nameConstraintValidator.checkPermitted(genNames[j]);
+ nameConstraintValidator.checkExcluded(genNames[j]);
+ }
+ catch (PKIXNameConstraintValidatorException e)
+ {
+ throw new CertPathValidatorException(
+ "Subtree check for certificate subject alternative name failed.", e, certPath, index);
+ }
+ }
+ }
+ }
+ }
+
+ protected static PKIXPolicyNode processCertD(
+ CertPath certPath,
+ int index,
+ Set acceptablePolicies,
+ PKIXPolicyNode validPolicyTree,
+ List[] policyNodes,
+ int inhibitAnyPolicy)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ int n = certs.size();
+ // i as defined in the algorithm description
+ int i = n - index;
+ //
+ // (d) policy Information checking against initial policy and
+ // policy mapping
+ //
+ ASN1Sequence certPolicies = null;
+ try
+ {
+ certPolicies = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.CERTIFICATE_POLICIES));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new ExtCertPathValidatorException("Could not read certificate policies extension from certificate.",
+ e, certPath, index);
+ }
+ if (certPolicies != null && validPolicyTree != null)
+ {
+ //
+ // (d) (1)
+ //
+ Enumeration e = certPolicies.getObjects();
+ Set pols = new HashSet();
+
+ while (e.hasMoreElements())
+ {
+ PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement());
+ DERObjectIdentifier pOid = pInfo.getPolicyIdentifier();
+
+ pols.add(pOid.getId());
+
+ if (!RFC3280CertPathUtilities.ANY_POLICY.equals(pOid.getId()))
+ {
+ Set pq = null;
+ try
+ {
+ pq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers());
+ }
+ catch (CertPathValidatorException ex)
+ {
+ throw new ExtCertPathValidatorException("Policy qualifier info set could not be build.", ex,
+ certPath, index);
+ }
+
+ boolean match = CertPathValidatorUtilities.processCertD1i(i, policyNodes, pOid, pq);
+
+ if (!match)
+ {
+ CertPathValidatorUtilities.processCertD1ii(i, policyNodes, pOid, pq);
+ }
+ }
+ }
+
+ if (acceptablePolicies.isEmpty() || acceptablePolicies.contains(RFC3280CertPathUtilities.ANY_POLICY))
+ {
+ acceptablePolicies.clear();
+ acceptablePolicies.addAll(pols);
+ }
+ else
+ {
+ Iterator it = acceptablePolicies.iterator();
+ Set t1 = new HashSet();
+
+ while (it.hasNext())
+ {
+ Object o = it.next();
+
+ if (pols.contains(o))
+ {
+ t1.add(o);
+ }
+ }
+ acceptablePolicies.clear();
+ acceptablePolicies.addAll(t1);
+ }
+
+ //
+ // (d) (2)
+ //
+ if ((inhibitAnyPolicy > 0) || ((i < n) && CertPathValidatorUtilities.isSelfIssued(cert)))
+ {
+ e = certPolicies.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement());
+
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(pInfo.getPolicyIdentifier().getId()))
+ {
+ Set _apq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers());
+ List _nodes = policyNodes[i - 1];
+
+ for (int k = 0; k < _nodes.size(); k++)
+ {
+ PKIXPolicyNode _node = (PKIXPolicyNode)_nodes.get(k);
+
+ Iterator _policySetIter = _node.getExpectedPolicies().iterator();
+ while (_policySetIter.hasNext())
+ {
+ Object _tmp = _policySetIter.next();
+
+ String _policy;
+ if (_tmp instanceof String)
+ {
+ _policy = (String)_tmp;
+ }
+ else if (_tmp instanceof DERObjectIdentifier)
+ {
+ _policy = ((DERObjectIdentifier)_tmp).getId();
+ }
+ else
+ {
+ continue;
+ }
+
+ boolean _found = false;
+ Iterator _childrenIter = _node.getChildren();
+
+ while (_childrenIter.hasNext())
+ {
+ PKIXPolicyNode _child = (PKIXPolicyNode)_childrenIter.next();
+
+ if (_policy.equals(_child.getValidPolicy()))
+ {
+ _found = true;
+ }
+ }
+
+ if (!_found)
+ {
+ Set _newChildExpectedPolicies = new HashSet();
+ _newChildExpectedPolicies.add(_policy);
+
+ PKIXPolicyNode _newChild = new PKIXPolicyNode(new ArrayList(), i,
+ _newChildExpectedPolicies, _node, _apq, _policy, false);
+ _node.addChild(_newChild);
+ policyNodes[i].add(_newChild);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ PKIXPolicyNode _validPolicyTree = validPolicyTree;
+ //
+ // (d) (3)
+ //
+ for (int j = (i - 1); j >= 0; j--)
+ {
+ List nodes = policyNodes[j];
+
+ for (int k = 0; k < nodes.size(); k++)
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
+ if (!node.hasChildren())
+ {
+ _validPolicyTree = CertPathValidatorUtilities.removePolicyNode(_validPolicyTree, policyNodes,
+ node);
+ if (_validPolicyTree == null)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // d (4)
+ //
+ Set criticalExtensionOids = cert.getCriticalExtensionOIDs();
+
+ if (criticalExtensionOids != null)
+ {
+ boolean critical = criticalExtensionOids.contains(RFC3280CertPathUtilities.CERTIFICATE_POLICIES);
+
+ List nodes = policyNodes[i];
+ for (int j = 0; j < nodes.size(); j++)
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(j);
+ node.setCritical(critical);
+ }
+ }
+ return _validPolicyTree;
+ }
+ return null;
+ }
+
+ protected static void processCertA(
+ CertPath certPath,
+ ExtendedPKIXParameters paramsPKIX,
+ int index,
+ PublicKey workingPublicKey,
+ boolean verificationAlreadyPerformed,
+ X500Principal workingIssuerName,
+ X509Certificate sign)
+ throws ExtCertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (a) verify
+ //
+ if (!verificationAlreadyPerformed)
+ {
+ try
+ {
+ // (a) (1)
+ //
+ CertPathValidatorUtilities.verifyX509Certificate(cert, workingPublicKey,
+ paramsPKIX.getSigProvider());
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new ExtCertPathValidatorException("Could not validate certificate signature.", e, certPath, index);
+ }
+ }
+
+ try
+ {
+ // (a) (2)
+ //
+ cert.checkValidity(CertPathValidatorUtilities
+ .getValidCertDateFromValidityModel(paramsPKIX, certPath, index));
+ }
+ catch (CertificateExpiredException e)
+ {
+ throw new ExtCertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index);
+ }
+ catch (CertificateNotYetValidException e)
+ {
+ throw new ExtCertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new ExtCertPathValidatorException("Could not validate time of certificate.", e, certPath, index);
+ }
+
+ //
+ // (a) (3)
+ //
+ if (paramsPKIX.isRevocationEnabled())
+ {
+ try
+ {
+ checkCRLs(paramsPKIX, cert, CertPathValidatorUtilities.getValidCertDateFromValidityModel(paramsPKIX,
+ certPath, index), sign, workingPublicKey, certs);
+ }
+ catch (AnnotatedException e)
+ {
+ Throwable cause = e;
+ if (null != e.getCause())
+ {
+ cause = e.getCause();
+ }
+ throw new ExtCertPathValidatorException(e.getMessage(), cause, certPath, index);
+ }
+ }
+
+ //
+ // (a) (4) name chaining
+ //
+ if (!CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).equals(workingIssuerName))
+ {
+ throw new ExtCertPathValidatorException("IssuerName(" + CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert)
+ + ") does not match SubjectName(" + workingIssuerName + ") of signing certificate.", null,
+ certPath, index);
+ }
+ }
+
+ protected static int prepareNextCertI1(
+ CertPath certPath,
+ int index,
+ int explicitPolicy)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (i)
+ //
+ ASN1Sequence pc = null;
+ try
+ {
+ pc = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.POLICY_CONSTRAINTS));
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Policy constraints extension cannot be decoded.", e, certPath,
+ index);
+ }
+
+ int tmpInt;
+
+ if (pc != null)
+ {
+ Enumeration policyConstraints = pc.getObjects();
+
+ while (policyConstraints.hasMoreElements())
+ {
+ try
+ {
+
+ ASN1TaggedObject constraint = ASN1TaggedObject.getInstance(policyConstraints.nextElement());
+ if (constraint.getTagNo() == 0)
+ {
+ tmpInt = DERInteger.getInstance(constraint, false).getValue().intValue();
+ if (tmpInt < explicitPolicy)
+ {
+ return tmpInt;
+ }
+ break;
+ }
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ExtCertPathValidatorException("Policy constraints extension contents cannot be decoded.",
+ e, certPath, index);
+ }
+ }
+ }
+ return explicitPolicy;
+ }
+
+ protected static int prepareNextCertI2(
+ CertPath certPath,
+ int index,
+ int policyMapping)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (i)
+ //
+ ASN1Sequence pc = null;
+ try
+ {
+ pc = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.POLICY_CONSTRAINTS));
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Policy constraints extension cannot be decoded.", e, certPath,
+ index);
+ }
+
+ int tmpInt;
+
+ if (pc != null)
+ {
+ Enumeration policyConstraints = pc.getObjects();
+
+ while (policyConstraints.hasMoreElements())
+ {
+ try
+ {
+ ASN1TaggedObject constraint = ASN1TaggedObject.getInstance(policyConstraints.nextElement());
+ if (constraint.getTagNo() == 1)
+ {
+ tmpInt = DERInteger.getInstance(constraint, false).getValue().intValue();
+ if (tmpInt < policyMapping)
+ {
+ return tmpInt;
+ }
+ break;
+ }
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ExtCertPathValidatorException("Policy constraints extension contents cannot be decoded.",
+ e, certPath, index);
+ }
+ }
+ }
+ return policyMapping;
+ }
+
+ protected static void prepareNextCertG(
+ CertPath certPath,
+ int index,
+ PKIXNameConstraintValidator nameConstraintValidator)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (g) handle the name constraints extension
+ //
+ NameConstraints nc = null;
+ try
+ {
+ ASN1Sequence ncSeq = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.NAME_CONSTRAINTS));
+ if (ncSeq != null)
+ {
+ nc = NameConstraints.getInstance(ncSeq);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Name constraints extension could not be decoded.", e, certPath,
+ index);
+ }
+ if (nc != null)
+ {
+
+ //
+ // (g) (1) permitted subtrees
+ //
+ ASN1Sequence permitted = nc.getPermittedSubtrees();
+ if (permitted != null)
+ {
+ try
+ {
+ nameConstraintValidator.intersectPermittedSubtree(permitted);
+ }
+ catch (Exception ex)
+ {
+ throw new ExtCertPathValidatorException(
+ "Permitted subtrees cannot be build from name constraints extension.", ex, certPath, index);
+ }
+ }
+
+ //
+ // (g) (2) excluded subtrees
+ //
+ ASN1Sequence excluded = nc.getExcludedSubtrees();
+ if (excluded != null)
+ {
+ Enumeration e = excluded.getObjects();
+ try
+ {
+ while (e.hasMoreElements())
+ {
+ GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement());
+ nameConstraintValidator.addExcludedSubtree(subtree);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new ExtCertPathValidatorException(
+ "Excluded subtrees cannot be build from name constraints extension.", ex, certPath, index);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks a distribution point for revocation information for the
+ * certificate <code>cert</code>.
+ *
+ * @param dp The distribution point to consider.
+ * @param paramsPKIX PKIX parameters.
+ * @param cert Certificate to check if it is revoked.
+ * @param validDate The date when the certificate revocation status should be
+ * checked.
+ * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+ * @param defaultCRLSignKey The public key of the issuer certificate
+ * <code>defaultCRLSignCert</code>.
+ * @param certStatus The current certificate revocation status.
+ * @param reasonMask The reasons mask which is already checked.
+ * @param certPathCerts The certificates of the certification path.
+ * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+ * or some error occurs.
+ */
+ private static void checkCRL(
+ DistributionPoint dp,
+ ExtendedPKIXParameters paramsPKIX,
+ X509Certificate cert,
+ Date validDate,
+ X509Certificate defaultCRLSignCert,
+ PublicKey defaultCRLSignKey,
+ CertStatus certStatus,
+ ReasonsMask reasonMask,
+ List certPathCerts)
+ throws AnnotatedException
+ {
+ Date currentDate = new Date(System.currentTimeMillis());
+ if (validDate.getTime() > currentDate.getTime())
+ {
+ throw new AnnotatedException("Validation time is in future.");
+ }
+
+ // (a)
+ /*
+ * We always get timely valid CRLs, so there is no step (a) (1).
+ * "locally cached" CRLs are assumed to be in getStore(), additional
+ * CRLs must be enabled in the ExtendedPKIXParameters and are in
+ * getAdditionalStore()
+ */
+
+ Set crls = CertPathValidatorUtilities.getCompleteCRLs(dp, cert, currentDate, paramsPKIX);
+ boolean validCrlFound = false;
+ AnnotatedException lastException = null;
+ Iterator crl_iter = crls.iterator();
+
+ while (crl_iter.hasNext() && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonMask.isAllReasons())
+ {
+ try
+ {
+ X509CRL crl = (X509CRL)crl_iter.next();
+
+ // (d)
+ ReasonsMask interimReasonsMask = RFC3280CertPathUtilities.processCRLD(crl, dp);
+
+ // (e)
+ /*
+ * The reasons mask is updated at the end, so only valid CRLs
+ * can update it. If this CRL does not contain new reasons it
+ * must be ignored.
+ */
+ if (!interimReasonsMask.hasNewReasons(reasonMask))
+ {
+ continue;
+ }
+
+ // (f)
+ Set keys = RFC3280CertPathUtilities.processCRLF(crl, cert, defaultCRLSignCert, defaultCRLSignKey,
+ paramsPKIX, certPathCerts);
+ // (g)
+ PublicKey key = RFC3280CertPathUtilities.processCRLG(crl, keys);
+
+ X509CRL deltaCRL = null;
+
+ if (paramsPKIX.isUseDeltasEnabled())
+ {
+ // get delta CRLs
+ Set deltaCRLs = CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl);
+ // we only want one valid delta CRL
+ // (h)
+ deltaCRL = RFC3280CertPathUtilities.processCRLH(deltaCRLs, key);
+ }
+
+ /*
+ * CRL must be be valid at the current time, not the validation
+ * time. If a certificate is revoked with reason keyCompromise,
+ * cACompromise, it can be used for forgery, also for the past.
+ * This reason may not be contained in older CRLs.
+ */
+
+ /*
+ * in the chain model signatures stay valid also after the
+ * certificate has been expired, so they do not have to be in
+ * the CRL validity time
+ */
+
+ if (paramsPKIX.getValidityModel() != ExtendedPKIXParameters.CHAIN_VALIDITY_MODEL)
+ {
+ /*
+ * if a certificate has expired, but was revoked, it is not
+ * more in the CRL, so it would be regarded as valid if the
+ * first check is not done
+ */
+ if (cert.getNotAfter().getTime() < crl.getThisUpdate().getTime())
+ {
+ throw new AnnotatedException("No valid CRL for current time found.");
+ }
+ }
+
+ RFC3280CertPathUtilities.processCRLB1(dp, cert, crl);
+
+ // (b) (2)
+ RFC3280CertPathUtilities.processCRLB2(dp, cert, crl);
+
+ // (c)
+ RFC3280CertPathUtilities.processCRLC(deltaCRL, crl, paramsPKIX);
+
+ // (i)
+ RFC3280CertPathUtilities.processCRLI(validDate, deltaCRL, cert, certStatus, paramsPKIX);
+
+ // (j)
+ RFC3280CertPathUtilities.processCRLJ(validDate, crl, cert, certStatus);
+
+ // (k)
+ if (certStatus.getCertStatus() == CRLReason.removeFromCRL)
+ {
+ certStatus.setCertStatus(CertStatus.UNREVOKED);
+ }
+
+ // update reasons mask
+ reasonMask.addReasons(interimReasonsMask);
+
+ Set criticalExtensions = crl.getCriticalExtensionOIDs();
+ if (criticalExtensions != null)
+ {
+ criticalExtensions = new HashSet(criticalExtensions);
+ criticalExtensions.remove(X509Extensions.IssuingDistributionPoint.getId());
+ criticalExtensions.remove(X509Extensions.DeltaCRLIndicator.getId());
+
+ if (!criticalExtensions.isEmpty())
+ {
+ throw new AnnotatedException("CRL contains unsupported critical extensions.");
+ }
+ }
+
+ if (deltaCRL != null)
+ {
+ criticalExtensions = deltaCRL.getCriticalExtensionOIDs();
+ if (criticalExtensions != null)
+ {
+ criticalExtensions = new HashSet(criticalExtensions);
+ criticalExtensions.remove(X509Extensions.IssuingDistributionPoint.getId());
+ criticalExtensions.remove(X509Extensions.DeltaCRLIndicator.getId());
+ if (!criticalExtensions.isEmpty())
+ {
+ throw new AnnotatedException("Delta CRL contains unsupported critical extension.");
+ }
+ }
+ }
+
+ validCrlFound = true;
+ }
+ catch (AnnotatedException e)
+ {
+ lastException = e;
+ }
+ }
+ if (!validCrlFound)
+ {
+ throw lastException;
+ }
+ }
+
+ /**
+ * Checks a certificate if it is revoked.
+ *
+ * @param paramsPKIX PKIX parameters.
+ * @param cert Certificate to check if it is revoked.
+ * @param validDate The date when the certificate revocation status should be
+ * checked.
+ * @param sign The issuer certificate of the certificate <code>cert</code>.
+ * @param workingPublicKey The public key of the issuer certificate <code>sign</code>.
+ * @param certPathCerts The certificates of the certification path.
+ * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+ * or some error occurs.
+ */
+ protected static void checkCRLs(
+ ExtendedPKIXParameters paramsPKIX,
+ X509Certificate cert,
+ Date validDate,
+ X509Certificate sign,
+ PublicKey workingPublicKey,
+ List certPathCerts)
+ throws AnnotatedException
+ {
+ AnnotatedException lastException = null;
+ CRLDistPoint crldp = null;
+ try
+ {
+ crldp = CRLDistPoint.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS));
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("CRL distribution point extension could not be read.", e);
+ }
+ try
+ {
+ CertPathValidatorUtilities.addAdditionalStoresFromCRLDistributionPoint(crldp, paramsPKIX);
+ }
+ catch (AnnotatedException e)
+ {
+ throw new AnnotatedException(
+ "No additional CRL locations could be decoded from CRL distribution point extension.", e);
+ }
+ CertStatus certStatus = new CertStatus();
+ ReasonsMask reasonsMask = new ReasonsMask();
+
+ boolean validCrlFound = false;
+ // for each distribution point
+ if (crldp != null)
+ {
+ DistributionPoint dps[] = null;
+ try
+ {
+ dps = crldp.getDistributionPoints();
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Distribution points could not be read.", e);
+ }
+ if (dps != null)
+ {
+ for (int i = 0; i < dps.length && certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons(); i++)
+ {
+ ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters)paramsPKIX.clone();
+ try
+ {
+ checkCRL(dps[i], paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask, certPathCerts);
+ validCrlFound = true;
+ }
+ catch (AnnotatedException e)
+ {
+ lastException = e;
+ }
+ }
+ }
+ }
+
+ /*
+ * If the revocation status has not been determined, repeat the process
+ * above with any available CRLs not specified in a distribution point
+ * but issued by the certificate issuer.
+ */
+
+ if (certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons())
+ {
+ try
+ {
+ /*
+ * assume a DP with both the reasons and the cRLIssuer fields
+ * omitted and a distribution point name of the certificate
+ * issuer.
+ */
+ ASN1Primitive issuer = null;
+ try
+ {
+ issuer = new ASN1InputStream(CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).getEncoded())
+ .readObject();
+ }
+ catch (Exception e)
+ {
+ throw new AnnotatedException("Issuer from certificate for CRL could not be reencoded.", e);
+ }
+ DistributionPoint dp = new DistributionPoint(new DistributionPointName(0, new GeneralNames(
+ new GeneralName(GeneralName.directoryName, issuer))), null, null);
+ ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters)paramsPKIX.clone();
+ checkCRL(dp, paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask,
+ certPathCerts);
+ validCrlFound = true;
+ }
+ catch (AnnotatedException e)
+ {
+ lastException = e;
+ }
+ }
+
+ if (!validCrlFound)
+ {
+ if (lastException instanceof AnnotatedException)
+ {
+ throw lastException;
+ }
+
+ throw new AnnotatedException("No valid CRL found.", lastException);
+ }
+ if (certStatus.getCertStatus() != CertStatus.UNREVOKED)
+ {
+ String message = "Certificate revocation after " + certStatus.getRevocationDate();
+ message += ", reason: " + crlReasons[certStatus.getCertStatus()];
+ throw new AnnotatedException(message);
+ }
+ if (!reasonsMask.isAllReasons() && certStatus.getCertStatus() == CertStatus.UNREVOKED)
+ {
+ certStatus.setCertStatus(CertStatus.UNDETERMINED);
+ }
+ if (certStatus.getCertStatus() == CertStatus.UNDETERMINED)
+ {
+ throw new AnnotatedException("Certificate status could not be determined.");
+ }
+ }
+
+ protected static int prepareNextCertJ(
+ CertPath certPath,
+ int index,
+ int inhibitAnyPolicy)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (j)
+ //
+ DERInteger iap = null;
+ try
+ {
+ iap = DERInteger.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.INHIBIT_ANY_POLICY));
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Inhibit any-policy extension cannot be decoded.", e, certPath,
+ index);
+ }
+
+ if (iap != null)
+ {
+ int _inhibitAnyPolicy = iap.getValue().intValue();
+
+ if (_inhibitAnyPolicy < inhibitAnyPolicy)
+ {
+ return _inhibitAnyPolicy;
+ }
+ }
+ return inhibitAnyPolicy;
+ }
+
+ protected static void prepareNextCertK(
+ CertPath certPath,
+ int index)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (k)
+ //
+ BasicConstraints bc = null;
+ try
+ {
+ bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.BASIC_CONSTRAINTS));
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
+ index);
+ }
+ if (bc != null)
+ {
+ if (!(bc.isCA()))
+ {
+ throw new CertPathValidatorException("Not a CA certificate");
+ }
+ }
+ else
+ {
+ throw new CertPathValidatorException("Intermediate certificate lacks BasicConstraints");
+ }
+ }
+
+ protected static int prepareNextCertL(
+ CertPath certPath,
+ int index,
+ int maxPathLength)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (l)
+ //
+ if (!CertPathValidatorUtilities.isSelfIssued(cert))
+ {
+ if (maxPathLength <= 0)
+ {
+ throw new ExtCertPathValidatorException("Max path length not greater than zero", null, certPath, index);
+ }
+
+ return maxPathLength - 1;
+ }
+ return maxPathLength;
+ }
+
+ protected static int prepareNextCertM(
+ CertPath certPath,
+ int index,
+ int maxPathLength)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+
+ //
+ // (m)
+ //
+ BasicConstraints bc = null;
+ try
+ {
+ bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.BASIC_CONSTRAINTS));
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
+ index);
+ }
+ if (bc != null)
+ {
+ BigInteger _pathLengthConstraint = bc.getPathLenConstraint();
+
+ if (_pathLengthConstraint != null)
+ {
+ int _plc = _pathLengthConstraint.intValue();
+
+ if (_plc < maxPathLength)
+ {
+ return _plc;
+ }
+ }
+ }
+ return maxPathLength;
+ }
+
+ protected static void prepareNextCertN(
+ CertPath certPath,
+ int index)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+
+ //
+ // (n)
+ //
+ boolean[] _usage = cert.getKeyUsage();
+
+ if ((_usage != null) && !_usage[RFC3280CertPathUtilities.KEY_CERT_SIGN])
+ {
+ throw new ExtCertPathValidatorException(
+ "Issuer certificate keyusage extension is critical and does not permit key signing.", null,
+ certPath, index);
+ }
+ }
+
+ protected static void prepareNextCertO(
+ CertPath certPath,
+ int index,
+ Set criticalExtensions,
+ List pathCheckers)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (o)
+ //
+
+ Iterator tmpIter;
+ tmpIter = pathCheckers.iterator();
+ while (tmpIter.hasNext())
+ {
+ try
+ {
+ ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
+ }
+ catch (CertPathValidatorException e)
+ {
+ throw new CertPathValidatorException(e.getMessage(), e.getCause(), certPath, index);
+ }
+ }
+ if (!criticalExtensions.isEmpty())
+ {
+ throw new ExtCertPathValidatorException("Certificate has unsupported critical extension.", null, certPath,
+ index);
+ }
+ }
+
+ protected static int prepareNextCertH1(
+ CertPath certPath,
+ int index,
+ int explicitPolicy)
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (h)
+ //
+ if (!CertPathValidatorUtilities.isSelfIssued(cert))
+ {
+ //
+ // (1)
+ //
+ if (explicitPolicy != 0)
+ {
+ return explicitPolicy - 1;
+ }
+ }
+ return explicitPolicy;
+ }
+
+ protected static int prepareNextCertH2(
+ CertPath certPath,
+ int index,
+ int policyMapping)
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (h)
+ //
+ if (!CertPathValidatorUtilities.isSelfIssued(cert))
+ {
+ //
+ // (2)
+ //
+ if (policyMapping != 0)
+ {
+ return policyMapping - 1;
+ }
+ }
+ return policyMapping;
+ }
+
+ protected static int prepareNextCertH3(
+ CertPath certPath,
+ int index,
+ int inhibitAnyPolicy)
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (h)
+ //
+ if (!CertPathValidatorUtilities.isSelfIssued(cert))
+ {
+ //
+ // (3)
+ //
+ if (inhibitAnyPolicy != 0)
+ {
+ return inhibitAnyPolicy - 1;
+ }
+ }
+ return inhibitAnyPolicy;
+ }
+
+ protected static final String[] crlReasons = new String[]
+ {
+ "unspecified",
+ "keyCompromise",
+ "cACompromise",
+ "affiliationChanged",
+ "superseded",
+ "cessationOfOperation",
+ "certificateHold",
+ "unknown",
+ "removeFromCRL",
+ "privilegeWithdrawn",
+ "aACompromise"};
+
+ protected static int wrapupCertA(
+ int explicitPolicy,
+ X509Certificate cert)
+ {
+ //
+ // (a)
+ //
+ if (!CertPathValidatorUtilities.isSelfIssued(cert) && (explicitPolicy != 0))
+ {
+ explicitPolicy--;
+ }
+ return explicitPolicy;
+ }
+
+ protected static int wrapupCertB(
+ CertPath certPath,
+ int index,
+ int explicitPolicy)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ //
+ // (b)
+ //
+ int tmpInt;
+ ASN1Sequence pc = null;
+ try
+ {
+ pc = DERSequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert,
+ RFC3280CertPathUtilities.POLICY_CONSTRAINTS));
+ }
+ catch (AnnotatedException e)
+ {
+ throw new ExtCertPathValidatorException("Policy constraints could not be decoded.", e, certPath, index);
+ }
+ if (pc != null)
+ {
+ Enumeration policyConstraints = pc.getObjects();
+
+ while (policyConstraints.hasMoreElements())
+ {
+ ASN1TaggedObject constraint = (ASN1TaggedObject)policyConstraints.nextElement();
+ switch (constraint.getTagNo())
+ {
+ case 0:
+ try
+ {
+ tmpInt = DERInteger.getInstance(constraint, false).getValue().intValue();
+ }
+ catch (Exception e)
+ {
+ throw new ExtCertPathValidatorException(
+ "Policy constraints requireExplicitPolicy field could not be decoded.", e, certPath,
+ index);
+ }
+ if (tmpInt == 0)
+ {
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ return explicitPolicy;
+ }
+
+ protected static void wrapupCertF(
+ CertPath certPath,
+ int index,
+ List pathCheckers,
+ Set criticalExtensions)
+ throws CertPathValidatorException
+ {
+ List certs = certPath.getCertificates();
+ X509Certificate cert = (X509Certificate)certs.get(index);
+ Iterator tmpIter;
+ tmpIter = pathCheckers.iterator();
+ while (tmpIter.hasNext())
+ {
+ try
+ {
+ ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions);
+ }
+ catch (CertPathValidatorException e)
+ {
+ throw new ExtCertPathValidatorException("Additional certificate path checker failed.", e, certPath,
+ index);
+ }
+ }
+
+ if (!criticalExtensions.isEmpty())
+ {
+ throw new ExtCertPathValidatorException("Certificate has unsupported critical extension", null, certPath,
+ index);
+ }
+ }
+
+ protected static PKIXPolicyNode wrapupCertG(
+ CertPath certPath,
+ ExtendedPKIXParameters paramsPKIX,
+ Set userInitialPolicySet,
+ int index,
+ List[] policyNodes,
+ PKIXPolicyNode validPolicyTree,
+ Set acceptablePolicies)
+ throws CertPathValidatorException
+ {
+ int n = certPath.getCertificates().size();
+ //
+ // (g)
+ //
+ PKIXPolicyNode intersection;
+
+ //
+ // (g) (i)
+ //
+ if (validPolicyTree == null)
+ {
+ if (paramsPKIX.isExplicitPolicyRequired())
+ {
+ throw new ExtCertPathValidatorException("Explicit policy requested but none available.", null,
+ certPath, index);
+ }
+ intersection = null;
+ }
+ else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g)
+ // (ii)
+ {
+ if (paramsPKIX.isExplicitPolicyRequired())
+ {
+ if (acceptablePolicies.isEmpty())
+ {
+ throw new ExtCertPathValidatorException("Explicit policy requested but none available.", null,
+ certPath, index);
+ }
+ else
+ {
+ Set _validPolicyNodeSet = new HashSet();
+
+ for (int j = 0; j < policyNodes.length; j++)
+ {
+ List _nodeDepth = policyNodes[j];
+
+ for (int k = 0; k < _nodeDepth.size(); k++)
+ {
+ PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
+
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(_node.getValidPolicy()))
+ {
+ Iterator _iter = _node.getChildren();
+ while (_iter.hasNext())
+ {
+ _validPolicyNodeSet.add(_iter.next());
+ }
+ }
+ }
+ }
+
+ Iterator _vpnsIter = _validPolicyNodeSet.iterator();
+ while (_vpnsIter.hasNext())
+ {
+ PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
+ String _validPolicy = _node.getValidPolicy();
+
+ if (!acceptablePolicies.contains(_validPolicy))
+ {
+ // validPolicyTree =
+ // removePolicyNode(validPolicyTree, policyNodes,
+ // _node);
+ }
+ }
+ if (validPolicyTree != null)
+ {
+ for (int j = (n - 1); j >= 0; j--)
+ {
+ List nodes = policyNodes[j];
+
+ for (int k = 0; k < nodes.size(); k++)
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
+ if (!node.hasChildren())
+ {
+ validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree,
+ policyNodes, node);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ intersection = validPolicyTree;
+ }
+ else
+ {
+ //
+ // (g) (iii)
+ //
+ // This implementation is not exactly same as the one described in
+ // RFC3280.
+ // However, as far as the validation result is concerned, both
+ // produce
+ // adequate result. The only difference is whether AnyPolicy is
+ // remain
+ // in the policy tree or not.
+ //
+ // (g) (iii) 1
+ //
+ Set _validPolicyNodeSet = new HashSet();
+
+ for (int j = 0; j < policyNodes.length; j++)
+ {
+ List _nodeDepth = policyNodes[j];
+
+ for (int k = 0; k < _nodeDepth.size(); k++)
+ {
+ PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k);
+
+ if (RFC3280CertPathUtilities.ANY_POLICY.equals(_node.getValidPolicy()))
+ {
+ Iterator _iter = _node.getChildren();
+ while (_iter.hasNext())
+ {
+ PKIXPolicyNode _c_node = (PKIXPolicyNode)_iter.next();
+ if (!RFC3280CertPathUtilities.ANY_POLICY.equals(_c_node.getValidPolicy()))
+ {
+ _validPolicyNodeSet.add(_c_node);
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // (g) (iii) 2
+ //
+ Iterator _vpnsIter = _validPolicyNodeSet.iterator();
+ while (_vpnsIter.hasNext())
+ {
+ PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next();
+ String _validPolicy = _node.getValidPolicy();
+
+ if (!userInitialPolicySet.contains(_validPolicy))
+ {
+ validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, _node);
+ }
+ }
+
+ //
+ // (g) (iii) 4
+ //
+ if (validPolicyTree != null)
+ {
+ for (int j = (n - 1); j >= 0; j--)
+ {
+ List nodes = policyNodes[j];
+
+ for (int k = 0; k < nodes.size(); k++)
+ {
+ PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k);
+ if (!node.hasChildren())
+ {
+ validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes,
+ node);
+ }
+ }
+ }
+ }
+
+ intersection = validPolicyTree;
+ }
+ return intersection;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
new file mode 100644
index 0000000..04f5a06
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.jce.provider;
+
+import org.bouncycastle.asn1.x509.ReasonFlags;
+
+/**
+ * This class helps to handle CRL revocation reasons mask. Each CRL handles a
+ * certain set of revocation reasons.
+ */
+class ReasonsMask
+{
+ private int _reasons;
+
+ /**
+ * Constructs are reason mask with the reasons.
+ *
+ * @param reasons The reasons.
+ */
+ ReasonsMask(ReasonFlags reasons)
+ {
+ _reasons = reasons.intValue();
+ }
+
+ private ReasonsMask(int reasons)
+ {
+ _reasons = reasons;
+ }
+
+ /**
+ * A reason mask with no reason.
+ *
+ */
+ ReasonsMask()
+ {
+ this(0);
+ }
+
+ /**
+ * A mask with all revocation reasons.
+ */
+ static final ReasonsMask allReasons = new ReasonsMask(ReasonFlags.aACompromise
+ | ReasonFlags.affiliationChanged | ReasonFlags.cACompromise
+ | ReasonFlags.certificateHold | ReasonFlags.cessationOfOperation
+ | ReasonFlags.keyCompromise | ReasonFlags.privilegeWithdrawn
+ | ReasonFlags.unused | ReasonFlags.superseded);
+
+ /**
+ * Adds all reasons from the reasons mask to this mask.
+ *
+ * @param mask The reasons mask to add.
+ */
+ void addReasons(ReasonsMask mask)
+ {
+ _reasons = _reasons | mask.getReasons();
+ }
+
+ /**
+ * Returns <code>true</code> if this reasons mask contains all possible
+ * reasons.
+ *
+ * @return <code>true</code> if this reasons mask contains all possible
+ * reasons.
+ */
+ boolean isAllReasons()
+ {
+ return _reasons == allReasons._reasons ? true : false;
+ }
+
+ /**
+ * Intersects this mask with the given reasons mask.
+ *
+ * @param mask The mask to intersect with.
+ * @return The intersection of this and teh given mask.
+ */
+ ReasonsMask intersect(ReasonsMask mask)
+ {
+ ReasonsMask _mask = new ReasonsMask();
+ _mask.addReasons(new ReasonsMask(_reasons & mask.getReasons()));
+ return _mask;
+ }
+
+ /**
+ * Returns <code>true</code> if the passed reasons mask has new reasons.
+ *
+ * @param mask The reasons mask which should be tested for new reasons.
+ * @return <code>true</code> if the passed reasons mask has new reasons.
+ */
+ boolean hasNewReasons(ReasonsMask mask)
+ {
+ return ((_reasons | mask.getReasons() ^ _reasons) != 0);
+ }
+
+ /**
+ * Returns the reasons in this mask.
+ *
+ * @return Returns the reasons.
+ */
+ int getReasons()
+ {
+ return _reasons;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
new file mode 100644
index 0000000..da7ee11
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
@@ -0,0 +1,296 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.CRLException;
+import java.security.cert.X509CRLEntry;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DEREnumerated;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.CRLReason;
+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.TBSCertList;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.x509.extension.X509ExtensionUtil;
+
+/**
+ * The following extensions are listed in RFC 2459 as relevant to CRL Entries
+ *
+ * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer
+ * (critical)
+ */
+public class X509CRLEntryObject extends X509CRLEntry
+{
+ private TBSCertList.CRLEntry c;
+
+ private X500Name certificateIssuer;
+ private int hashValue;
+ private boolean isHashValueSet;
+
+ public X509CRLEntryObject(TBSCertList.CRLEntry c)
+ {
+ this.c = c;
+ this.certificateIssuer = null;
+ }
+
+ /**
+ * Constructor for CRLEntries of indirect CRLs. If <code>isIndirect</code>
+ * is <code>false</code> {@link #getCertificateIssuer()} will always
+ * return <code>null</code>, <code>previousCertificateIssuer</code> is
+ * ignored. If this <code>isIndirect</code> is specified and this CRLEntry
+ * has no certificate issuer CRL entry extension
+ * <code>previousCertificateIssuer</code> is returned by
+ * {@link #getCertificateIssuer()}.
+ *
+ * @param c
+ * TBSCertList.CRLEntry object.
+ * @param isIndirect
+ * <code>true</code> if the corresponding CRL is a indirect
+ * CRL.
+ * @param previousCertificateIssuer
+ * Certificate issuer of the previous CRLEntry.
+ */
+ public X509CRLEntryObject(
+ TBSCertList.CRLEntry c,
+ boolean isIndirect,
+ X500Name previousCertificateIssuer)
+ {
+ this.c = c;
+ this.certificateIssuer = loadCertificateIssuer(isIndirect, previousCertificateIssuer);
+ }
+
+ /**
+ * Will return true if any extensions are present and marked as critical as
+ * we currently don't handle any extensions!
+ */
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ Set extns = getCriticalExtensionOIDs();
+
+ return extns != null && !extns.isEmpty();
+ }
+
+ private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer)
+ {
+ if (!isIndirect)
+ {
+ return null;
+ }
+
+ byte[] ext = getExtensionValue(X509Extension.certificateIssuer.getId());
+ if (ext == null)
+ {
+ return previousCertificateIssuer;
+ }
+
+ try
+ {
+ GeneralName[] names = GeneralNames.getInstance(
+ X509ExtensionUtil.fromExtensionValue(ext)).getNames();
+ for (int i = 0; i < names.length; i++)
+ {
+ if (names[i].getTagNo() == GeneralName.directoryName)
+ {
+ return X500Name.getInstance(names[i].getName());
+ }
+ }
+ return null;
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public X500Principal getCertificateIssuer()
+ {
+ if (certificateIssuer == null)
+ {
+ return null;
+ }
+ try
+ {
+ return new X500Principal(certificateIssuer.getEncoded());
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ private Set getExtensionOIDs(boolean critical)
+ {
+ Extensions extensions = c.getExtensions();
+
+ if (extensions != null)
+ {
+ Set set = new HashSet();
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (critical == ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+
+ return null;
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public byte[] getExtensionValue(String oid)
+ {
+ Extensions exts = c.getExtensions();
+
+ if (exts != null)
+ {
+ Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+
+ if (ext != null)
+ {
+ try
+ {
+ return ext.getExtnValue().getEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("error encoding " + e.toString());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Cache the hashCode value - calculating it with the standard method.
+ * @return calculated hashCode.
+ */
+ public int hashCode()
+ {
+ if (!isHashValueSet)
+ {
+ hashValue = super.hashCode();
+ isHashValueSet = true;
+ }
+
+ return hashValue;
+ }
+
+ public byte[] getEncoded()
+ throws CRLException
+ {
+ try
+ {
+ return c.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return c.getUserCertificate().getValue();
+ }
+
+ public Date getRevocationDate()
+ {
+ return c.getRevocationDate().getDate();
+ }
+
+ public boolean hasExtensions()
+ {
+ return c.getExtensions() != null;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append(" userCertificate: ").append(this.getSerialNumber()).append(nl);
+ buf.append(" revocationDate: ").append(this.getRevocationDate()).append(nl);
+ buf.append(" certificateIssuer: ").append(this.getCertificateIssuer()).append(nl);
+
+ Extensions extensions = c.getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+ if (e.hasMoreElements())
+ {
+ buf.append(" crlEntryExtensions:").append(nl);
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+ if (ext.getExtnValue() != null)
+ {
+ byte[] octs = ext.getExtnValue().getOctets();
+ ASN1InputStream dIn = new ASN1InputStream(octs);
+ buf.append(" critical(").append(ext.isCritical()).append(") ");
+ try
+ {
+ if (oid.equals(X509Extension.reasonCode))
+ {
+ buf.append(CRLReason.getInstance(DEREnumerated.getInstance(dIn.readObject()))).append(nl);
+ }
+ else if (oid.equals(X509Extension.certificateIssuer))
+ {
+ buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
+ }
+ }
+ catch (Exception ex)
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ }
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
new file mode 100644
index 0000000..4c87114
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
@@ -0,0 +1,576 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.CRLNumber;
+import org.bouncycastle.asn1.x509.CertificateList;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
+import org.bouncycastle.asn1.x509.TBSCertList;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.x509.extension.X509ExtensionUtil;
+
+/**
+ * The following extensions are listed in RFC 2459 as relevant to CRLs
+ *
+ * Authority Key Identifier
+ * Issuer Alternative Name
+ * CRL Number
+ * Delta CRL Indicator (critical)
+ * Issuing Distribution Point (critical)
+ */
+public class X509CRLObject
+ extends X509CRL
+{
+ private CertificateList c;
+ private String sigAlgName;
+ private byte[] sigAlgParams;
+ private boolean isIndirect;
+
+ static boolean isIndirectCRL(X509CRL crl)
+ throws CRLException
+ {
+ try
+ {
+ byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId());
+ return idp != null
+ && IssuingDistributionPoint.getInstance(X509ExtensionUtil.fromExtensionValue(idp)).isIndirectCRL();
+ }
+ catch (Exception e)
+ {
+ throw new ExtCRLException(
+ "Exception reading IssuingDistributionPoint", e);
+ }
+ }
+
+ public X509CRLObject(
+ CertificateList c)
+ throws CRLException
+ {
+ this.c = c;
+
+ try
+ {
+ this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+
+ if (c.getSignatureAlgorithm().getParameters() != null)
+ {
+ this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ }
+ else
+ {
+ this.sigAlgParams = null;
+ }
+
+ this.isIndirect = isIndirectCRL(this);
+ }
+ catch (Exception e)
+ {
+ throw new CRLException("CRL contents invalid: " + e);
+ }
+ }
+
+ /**
+ * Will return true if any extensions are present and marked
+ * as critical as we currently dont handle any extensions!
+ */
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ Set extns = getCriticalExtensionOIDs();
+
+ if (extns == null)
+ {
+ return false;
+ }
+
+ extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+ extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+
+ return !extns.isEmpty();
+ }
+
+ private Set getExtensionOIDs(boolean critical)
+ {
+ if (this.getVersion() == 2)
+ {
+ Extensions extensions = c.getTBSCertList().getExtensions();
+
+ if (extensions != null)
+ {
+ Set set = new HashSet();
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (critical == ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+ }
+
+ return null;
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public byte[] getExtensionValue(String oid)
+ {
+ Extensions exts = c.getTBSCertList().getExtensions();
+
+ if (exts != null)
+ {
+ Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+
+ if (ext != null)
+ {
+ try
+ {
+ return ext.getExtnValue().getEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException("error parsing " + e.toString());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public byte[] getEncoded()
+ throws CRLException
+ {
+ try
+ {
+ return c.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ public void verify(PublicKey key)
+ throws CRLException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ verify(key, BouncyCastleProvider.PROVIDER_NAME);
+ }
+
+ public void verify(PublicKey key, String sigProvider)
+ throws CRLException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature()))
+ {
+ throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
+ }
+
+ Signature sig;
+
+ if (sigProvider != null)
+ {
+ sig = Signature.getInstance(getSigAlgName(), sigProvider);
+ }
+ else
+ {
+ sig = Signature.getInstance(getSigAlgName());
+ }
+
+ sig.initVerify(key);
+ sig.update(this.getTBSCertList());
+
+ if (!sig.verify(this.getSignature()))
+ {
+ throw new SignatureException("CRL does not verify with supplied public key.");
+ }
+ }
+
+ public int getVersion()
+ {
+ return c.getVersionNumber();
+ }
+
+ public Principal getIssuerDN()
+ {
+ return new X509Principal(X500Name.getInstance(c.getIssuer().toASN1Primitive()));
+ }
+
+ public X500Principal getIssuerX500Principal()
+ {
+ try
+ {
+ return new X500Principal(c.getIssuer().getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("can't encode issuer DN");
+ }
+ }
+
+ public Date getThisUpdate()
+ {
+ return c.getThisUpdate().getDate();
+ }
+
+ public Date getNextUpdate()
+ {
+ if (c.getNextUpdate() != null)
+ {
+ return c.getNextUpdate().getDate();
+ }
+
+ return null;
+ }
+
+ private Set loadCRLEntries()
+ {
+ Set entrySet = new HashSet();
+ Enumeration certs = c.getRevokedCertificateEnumeration();
+
+ X500Name previousCertificateIssuer = null; // the issuer
+ while (certs.hasMoreElements())
+ {
+ TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
+ X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
+ entrySet.add(crlEntry);
+ if (isIndirect && entry.hasExtensions())
+ {
+ Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
+ }
+ }
+ }
+
+ return entrySet;
+ }
+
+ public X509CRLEntry getRevokedCertificate(BigInteger serialNumber)
+ {
+ Enumeration certs = c.getRevokedCertificateEnumeration();
+
+ X500Name previousCertificateIssuer = null; // the issuer
+ while (certs.hasMoreElements())
+ {
+ TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
+
+ if (serialNumber.equals(entry.getUserCertificate().getValue()))
+ {
+ return new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
+ }
+
+ if (isIndirect && entry.hasExtensions())
+ {
+ Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Set getRevokedCertificates()
+ {
+ Set entrySet = loadCRLEntries();
+
+ if (!entrySet.isEmpty())
+ {
+ return Collections.unmodifiableSet(entrySet);
+ }
+
+ return null;
+ }
+
+ public byte[] getTBSCertList()
+ throws CRLException
+ {
+ try
+ {
+ return c.getTBSCertList().getEncoded("DER");
+ }
+ catch (IOException e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ public byte[] getSignature()
+ {
+ return c.getSignature().getBytes();
+ }
+
+ public String getSigAlgName()
+ {
+ return sigAlgName;
+ }
+
+ public String getSigAlgOID()
+ {
+ return c.getSignatureAlgorithm().getAlgorithm().getId();
+ }
+
+ public byte[] getSigAlgParams()
+ {
+ if (sigAlgParams != null)
+ {
+ byte[] tmp = new byte[sigAlgParams.length];
+
+ System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a string representation of this CRL.
+ *
+ * @return a string representation of this CRL.
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append(" Version: ").append(this.getVersion()).append(
+ nl);
+ buf.append(" IssuerDN: ").append(this.getIssuerDN())
+ .append(nl);
+ buf.append(" This update: ").append(this.getThisUpdate())
+ .append(nl);
+ buf.append(" Next update: ").append(this.getNextUpdate())
+ .append(nl);
+ buf.append(" Signature Algorithm: ").append(this.getSigAlgName())
+ .append(nl);
+
+ byte[] sig = this.getSignature();
+
+ buf.append(" Signature: ").append(
+ new String(Hex.encode(sig, 0, 20))).append(nl);
+ for (int i = 20; i < sig.length; i += 20)
+ {
+ if (i < sig.length - 20)
+ {
+ buf.append(" ").append(
+ new String(Hex.encode(sig, i, 20))).append(nl);
+ }
+ else
+ {
+ buf.append(" ").append(
+ new String(Hex.encode(sig, i, sig.length - i))).append(nl);
+ }
+ }
+
+ Extensions extensions = c.getTBSCertList().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ if (e.hasMoreElements())
+ {
+ buf.append(" Extensions: ").append(nl);
+ }
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (ext.getExtnValue() != null)
+ {
+ byte[] octs = ext.getExtnValue().getOctets();
+ ASN1InputStream dIn = new ASN1InputStream(octs);
+ buf.append(" critical(").append(
+ ext.isCritical()).append(") ");
+ try
+ {
+ if (oid.equals(Extension.cRLNumber))
+ {
+ buf.append(
+ new CRLNumber(DERInteger.getInstance(
+ dIn.readObject()).getPositiveValue()))
+ .append(nl);
+ }
+ else if (oid.equals(Extension.deltaCRLIndicator))
+ {
+ buf.append(
+ "Base CRL: "
+ + new CRLNumber(DERInteger.getInstance(
+ dIn.readObject()).getPositiveValue()))
+ .append(nl);
+ }
+ else if (oid
+ .equals(Extension.issuingDistributionPoint))
+ {
+ buf.append(
+ IssuingDistributionPoint.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid
+ .equals(Extension.cRLDistributionPoints))
+ {
+ buf.append(
+ CRLDistPoint.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(Extension.freshestCRL))
+ {
+ buf.append(
+ CRLDistPoint.getInstance(dIn.readObject())).append(nl);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append(
+ ASN1Dump.dumpAsString(dIn.readObject()))
+ .append(nl);
+ }
+ }
+ catch (Exception ex)
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ }
+ Set set = getRevokedCertificates();
+ if (set != null)
+ {
+ Iterator it = set.iterator();
+ while (it.hasNext())
+ {
+ buf.append(it.next());
+ buf.append(nl);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Checks whether the given certificate is on this CRL.
+ *
+ * @param cert the certificate to check for.
+ * @return true if the given certificate is on this CRL,
+ * false otherwise.
+ */
+ public boolean isRevoked(Certificate cert)
+ {
+ if (!cert.getType().equals("X.509"))
+ {
+ throw new RuntimeException("X.509 CRL used with non X.509 Cert");
+ }
+
+ TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
+
+ X500Name caName = c.getIssuer();
+
+ if (certs != null)
+ {
+ BigInteger serial = ((X509Certificate)cert).getSerialNumber();
+
+ for (int i = 0; i < certs.length; i++)
+ {
+ if (isIndirect && certs[i].hasExtensions())
+ {
+ Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ caName = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
+ }
+ }
+
+ if (certs[i].getUserCertificate().getValue().equals(serial))
+ {
+ X500Name issuer;
+
+ if (cert instanceof X509Certificate)
+ {
+ issuer = X500Name.getInstance(((X509Certificate)cert).getIssuerX500Principal().getEncoded());
+ }
+ else
+ {
+ try
+ {
+ issuer = org.bouncycastle.asn1.x509.Certificate.getInstance(cert.getEncoded()).getIssuer();
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new RuntimeException("Cannot process certificate");
+ }
+ }
+
+ if (!caName.equals(issuer))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
new file mode 100644
index 0000000..e529836
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java
@@ -0,0 +1,826 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.NetscapeCertType;
+import org.bouncycastle.asn1.misc.NetscapeRevocationURL;
+import org.bouncycastle.asn1.misc.VerisignCzagExtension;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.X509CertificateStructure;
+import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public class X509CertificateObject
+ extends X509Certificate
+ implements PKCS12BagAttributeCarrier
+{
+ private X509CertificateStructure c;
+ private BasicConstraints basicConstraints;
+ private boolean[] keyUsage;
+ private boolean hashValueSet;
+ private int hashValue;
+
+ private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ public X509CertificateObject(
+ X509CertificateStructure c)
+ throws CertificateParsingException
+ {
+ this.c = c;
+
+ try
+ {
+ byte[] bytes = this.getExtensionBytes("2.5.29.19");
+
+ if (bytes != null)
+ {
+ basicConstraints = BasicConstraints.getInstance(ASN1Primitive.fromByteArray(bytes));
+ }
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
+ }
+
+ try
+ {
+ byte[] bytes = this.getExtensionBytes("2.5.29.15");
+ if (bytes != null)
+ {
+ DERBitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes));
+
+ bytes = bits.getBytes();
+ int length = (bytes.length * 8) - bits.getPadBits();
+
+ keyUsage = new boolean[(length < 9) ? 9 : length];
+
+ for (int i = 0; i != length; i++)
+ {
+ keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+ }
+ else
+ {
+ keyUsage = null;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException("cannot construct KeyUsage: " + e);
+ }
+ }
+
+ public void checkValidity()
+ throws CertificateExpiredException, CertificateNotYetValidException
+ {
+ this.checkValidity(new Date());
+ }
+
+ public void checkValidity(
+ Date date)
+ throws CertificateExpiredException, CertificateNotYetValidException
+ {
+ if (date.getTime() > this.getNotAfter().getTime()) // for other VM compatibility
+ {
+ throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
+ }
+
+ if (date.getTime() < this.getNotBefore().getTime())
+ {
+ throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
+ }
+ }
+
+ public int getVersion()
+ {
+ return c.getVersion();
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return c.getSerialNumber().getValue();
+ }
+
+ public Principal getIssuerDN()
+ {
+ try
+ {
+ return new X509Principal(X500Name.getInstance(c.getIssuer().getEncoded()));
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public X500Principal getIssuerX500Principal()
+ {
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(c.getIssuer());
+
+ return new X500Principal(bOut.toByteArray());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("can't encode issuer DN");
+ }
+ }
+
+ public Principal getSubjectDN()
+ {
+ return new X509Principal(X500Name.getInstance(c.getSubject().toASN1Primitive()));
+ }
+
+ public X500Principal getSubjectX500Principal()
+ {
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(c.getSubject());
+
+ return new X500Principal(bOut.toByteArray());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("can't encode issuer DN");
+ }
+ }
+
+ public Date getNotBefore()
+ {
+ return c.getStartDate().getDate();
+ }
+
+ public Date getNotAfter()
+ {
+ return c.getEndDate().getDate();
+ }
+
+ public byte[] getTBSCertificate()
+ throws CertificateEncodingException
+ {
+ try
+ {
+ return c.getTBSCertificate().getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CertificateEncodingException(e.toString());
+ }
+ }
+
+ public byte[] getSignature()
+ {
+ return c.getSignature().getBytes();
+ }
+
+ /**
+ * return a more "meaningful" representation for the signature algorithm used in
+ * the certficate.
+ */
+ public String getSigAlgName()
+ {
+ Provider prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
+
+ if (prov != null)
+ {
+ String algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+
+ if (algName != null)
+ {
+ return algName;
+ }
+ }
+
+ Provider[] provs = Security.getProviders();
+
+ //
+ // search every provider looking for a real algorithm
+ //
+ for (int i = 0; i != provs.length; i++)
+ {
+ String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+ if (algName != null)
+ {
+ return algName;
+ }
+ }
+
+ return this.getSigAlgOID();
+ }
+
+ /**
+ * return the object identifier for the signature.
+ */
+ public String getSigAlgOID()
+ {
+ return c.getSignatureAlgorithm().getObjectId().getId();
+ }
+
+ /**
+ * return the signature parameters, or null if there aren't any.
+ */
+ public byte[] getSigAlgParams()
+ {
+ if (c.getSignatureAlgorithm().getParameters() != null)
+ {
+ try
+ {
+ return c.getSignatureAlgorithm().getParameters().toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public boolean[] getIssuerUniqueID()
+ {
+ DERBitString id = c.getTBSCertificate().getIssuerUniqueId();
+
+ if (id != null)
+ {
+ byte[] bytes = id.getBytes();
+ boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];
+
+ for (int i = 0; i != boolId.length; i++)
+ {
+ boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+
+ return boolId;
+ }
+
+ return null;
+ }
+
+ public boolean[] getSubjectUniqueID()
+ {
+ DERBitString id = c.getTBSCertificate().getSubjectUniqueId();
+
+ if (id != null)
+ {
+ byte[] bytes = id.getBytes();
+ boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];
+
+ for (int i = 0; i != boolId.length; i++)
+ {
+ boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+
+ return boolId;
+ }
+
+ return null;
+ }
+
+ public boolean[] getKeyUsage()
+ {
+ return keyUsage;
+ }
+
+ public List getExtendedKeyUsage()
+ throws CertificateParsingException
+ {
+ byte[] bytes = this.getExtensionBytes("2.5.29.37");
+
+ if (bytes != null)
+ {
+ try
+ {
+ ASN1InputStream dIn = new ASN1InputStream(bytes);
+ ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
+ List list = new ArrayList();
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ list.add(((DERObjectIdentifier)seq.getObjectAt(i)).getId());
+ }
+
+ return Collections.unmodifiableList(list);
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException("error processing extended key usage extension");
+ }
+ }
+
+ return null;
+ }
+
+ public int getBasicConstraints()
+ {
+ if (basicConstraints != null)
+ {
+ if (basicConstraints.isCA())
+ {
+ if (basicConstraints.getPathLenConstraint() == null)
+ {
+ return Integer.MAX_VALUE;
+ }
+ else
+ {
+ return basicConstraints.getPathLenConstraint().intValue();
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ return -1;
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ if (this.getVersion() == 3)
+ {
+ Set set = new HashSet();
+ X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+ X509Extension ext = extensions.getExtension(oid);
+
+ if (ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+ }
+
+ return null;
+ }
+
+ private byte[] getExtensionBytes(String oid)
+ {
+ X509Extensions exts = c.getTBSCertificate().getExtensions();
+
+ if (exts != null)
+ {
+ X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid));
+ if (ext != null)
+ {
+ return ext.getValue().getOctets();
+ }
+ }
+
+ return null;
+ }
+
+ public byte[] getExtensionValue(String oid)
+ {
+ X509Extensions exts = c.getTBSCertificate().getExtensions();
+
+ if (exts != null)
+ {
+ X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid));
+
+ if (ext != null)
+ {
+ try
+ {
+ return ext.getValue().getEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException("error parsing " + e.toString());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ if (this.getVersion() == 3)
+ {
+ Set set = new HashSet();
+ X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+ X509Extension ext = extensions.getExtension(oid);
+
+ if (!ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+ }
+
+ return null;
+ }
+
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ if (this.getVersion() == 3)
+ {
+ X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+ String oidId = oid.getId();
+
+ if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE)
+ || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES)
+ || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS)
+ || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY)
+ || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS)
+ || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT)
+ || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR)
+ || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS)
+ || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS)
+ || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME)
+ || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS))
+ {
+ continue;
+ }
+
+ X509Extension ext = extensions.getExtension(oid);
+
+ if (ext.isCritical())
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public PublicKey getPublicKey()
+ {
+ try
+ {
+ return BouncyCastleProvider.getPublicKey(c.getSubjectPublicKeyInfo());
+ }
+ catch (IOException e)
+ {
+ return null; // should never happen...
+ }
+ }
+
+ // BEGIN android-changed
+ private byte[] encoded;
+ // END android-changed
+ public byte[] getEncoded()
+ throws CertificateEncodingException
+ {
+ try
+ {
+ // BEGIN android-changed
+ if (encoded == null) {
+ encoded = c.getEncoded(ASN1Encoding.DER);
+ }
+ return encoded;
+ // END android-changed
+ }
+ catch (IOException e)
+ {
+ throw new CertificateEncodingException(e.toString());
+ }
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof Certificate))
+ {
+ return false;
+ }
+
+ Certificate other = (Certificate)o;
+
+ try
+ {
+ byte[] b1 = this.getEncoded();
+ byte[] b2 = other.getEncoded();
+
+ return Arrays.areEqual(b1, b2);
+ }
+ catch (CertificateEncodingException e)
+ {
+ return false;
+ }
+ }
+
+ public synchronized int hashCode()
+ {
+ if (!hashValueSet)
+ {
+ hashValue = calculateHashCode();
+ hashValueSet = true;
+ }
+
+ return hashValue;
+ }
+
+ private int calculateHashCode()
+ {
+ try
+ {
+ int hashCode = 0;
+ byte[] certData = this.getEncoded();
+ for (int i = 1; i < certData.length; i++)
+ {
+ hashCode += certData[i] * i;
+ }
+ return hashCode;
+ }
+ catch (CertificateEncodingException e)
+ {
+ return 0;
+ }
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ DERObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append(" [0] Version: ").append(this.getVersion()).append(nl);
+ buf.append(" SerialNumber: ").append(this.getSerialNumber()).append(nl);
+ buf.append(" IssuerDN: ").append(this.getIssuerDN()).append(nl);
+ buf.append(" Start Date: ").append(this.getNotBefore()).append(nl);
+ buf.append(" Final Date: ").append(this.getNotAfter()).append(nl);
+ buf.append(" SubjectDN: ").append(this.getSubjectDN()).append(nl);
+ buf.append(" Public Key: ").append(this.getPublicKey()).append(nl);
+ buf.append(" Signature Algorithm: ").append(this.getSigAlgName()).append(nl);
+
+ byte[] sig = this.getSignature();
+
+ buf.append(" Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
+ for (int i = 20; i < sig.length; i += 20)
+ {
+ if (i < sig.length - 20)
+ {
+ buf.append(" ").append(new String(Hex.encode(sig, i, 20))).append(nl);
+ }
+ else
+ {
+ buf.append(" ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
+ }
+ }
+
+ X509Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ if (e.hasMoreElements())
+ {
+ buf.append(" Extensions: \n");
+ }
+
+ while (e.hasMoreElements())
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+ X509Extension ext = extensions.getExtension(oid);
+
+ if (ext.getValue() != null)
+ {
+ byte[] octs = ext.getValue().getOctets();
+ ASN1InputStream dIn = new ASN1InputStream(octs);
+ buf.append(" critical(").append(ext.isCritical()).append(") ");
+ try
+ {
+ if (oid.equals(X509Extension.basicConstraints))
+ {
+ buf.append(BasicConstraints.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(X509Extension.keyUsage))
+ {
+ buf.append(new KeyUsage((DERBitString)dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
+ {
+ buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
+ {
+ buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
+ {
+ buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
+ //buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ catch (Exception ex)
+ {
+ buf.append(oid.getId());
+ // buf.append(" value = ").append(new String(Hex.encode(ext.getValue().getOctets()))).append(nl);
+ buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ }
+
+ return buf.toString();
+ }
+
+ public final void verify(
+ PublicKey key)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ Signature signature;
+ String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+
+ try
+ {
+ signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME);
+ }
+ catch (Exception e)
+ {
+ signature = Signature.getInstance(sigName);
+ }
+
+ checkSignature(key, signature);
+ }
+
+ public final void verify(
+ PublicKey key,
+ String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+ Signature signature = Signature.getInstance(sigName, sigProvider);
+
+ checkSignature(key, signature);
+ }
+
+ private void checkSignature(
+ PublicKey key,
+ Signature signature)
+ throws CertificateException, NoSuchAlgorithmException,
+ SignatureException, InvalidKeyException
+ {
+ if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature()))
+ {
+ throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+ }
+
+ ASN1Encodable params = c.getSignatureAlgorithm().getParameters();
+
+ // TODO This should go after the initVerify?
+ X509SignatureUtil.setSignatureParameters(signature, params);
+
+ signature.initVerify(key);
+
+ signature.update(this.getTBSCertificate());
+
+ if (!signature.verify(this.getSignature()))
+ {
+ throw new SignatureException("certificate does not verify with supplied key");
+ }
+ }
+
+ private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
+ {
+ if (!id1.getObjectId().equals(id2.getObjectId()))
+ {
+ 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());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java
new file mode 100644
index 0000000..8e492dc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java
@@ -0,0 +1,148 @@
+package org.bouncycastle.jce.provider;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.PSSParameterSpec;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+// 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;
+
+class X509SignatureUtil
+{
+ // BEGIN android-changed
+ private static final ASN1Null derNull = DERNull.INSTANCE;
+ // END android-changed
+
+ static void setSignatureParameters(
+ Signature signature,
+ ASN1Encodable params)
+ throws NoSuchAlgorithmException, SignatureException, InvalidKeyException
+ {
+ if (params != null && !derNull.equals(params))
+ {
+ AlgorithmParameters sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider());
+
+ try
+ {
+ sigParams.init(params.toASN1Primitive().getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new SignatureException("IOException decoding parameters: " + e.getMessage());
+ }
+
+ if (signature.getAlgorithm().endsWith("MGF1"))
+ {
+ try
+ {
+ signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class));
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new SignatureException("Exception extracting parameters: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ static String getSignatureName(
+ AlgorithmIdentifier sigAlgId)
+ {
+ ASN1Encodable params = sigAlgId.getParameters();
+
+ if (params != null && !derNull.equals(params))
+ {
+ // BEGIN android-removed
+ // if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+ // {
+ // RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+ //
+ // return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1";
+ // }
+ // END android-removed
+ if (sigAlgId.getObjectId().equals(X9ObjectIdentifiers.ecdsa_with_SHA2))
+ {
+ ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params);
+
+ return getDigestAlgName((DERObjectIdentifier)ecDsaParams.getObjectAt(0)) + "withECDSA";
+ }
+ }
+
+ return sigAlgId.getObjectId().getId();
+ }
+
+ /**
+ * Return the digest algorithm using one of the standard JCA string
+ * representations rather the the algorithm identifier (if possible).
+ */
+ private static String getDigestAlgName(
+ DERObjectIdentifier digestAlgOID)
+ {
+ if (PKCSObjectIdentifiers.md5.equals(digestAlgOID))
+ {
+ return "MD5";
+ }
+ else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID))
+ {
+ return "SHA1";
+ }
+ // BEGIN android-removed
+ // else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
+ // {
+ // return "SHA224";
+ // }
+ // END android-removed
+ 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();
+ }
+ }
+}