diff options
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/jce/provider')
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.<n>=org.bouncycastle.jce.provider.BouncyCastleProvider + * </code> + * </pre> + * Where <n> 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(); + } + } +} |