diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-05-31 11:07:45 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-05-31 11:07:45 +0400 |
commit | 2b976f5364cfdbc37d3086019d93483c983eb80b (patch) | |
tree | cb846af3fd1d43f9c2562a1fb2d06b997ad8f229 /prov/src/main/java/org/bouncycastle/jce/provider | |
parent | 5f714bd92fbd780d22406f4bc3681be005f6f04a (diff) |
initial reshuffle
Diffstat (limited to 'prov/src/main/java/org/bouncycastle/jce/provider')
54 files changed, 16901 insertions, 0 deletions
diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java b/prov/src/main/java/org/bouncycastle/jce/provider/AnnotatedException.java new file mode 100644 index 00000000..c9ac46ef --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java new file mode 100644 index 00000000..0433965c --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -0,0 +1,281 @@ +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.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.49"; + + public static final String PROVIDER_NAME = "BC"; + + public static final ProviderConfiguration CONFIGURATION = new BouncyCastleProviderConfiguration(); + + private static final Map keyInfoConverters = new HashMap(); + + /* + * Configurable symmetric ciphers + */ + private static final String SYMMETRIC_PACKAGE = "org.bouncycastle.jcajce.provider.symmetric."; + + private static final String[] SYMMETRIC_GENERIC = + { + "PBEPBKDF2", "PBEPKCS12" + }; + + private static final String[] SYMMETRIC_MACS = + { + "SipHash" + }; + + private static final String[] SYMMETRIC_CIPHERS = + { + "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" + }; + + /* + * Configurable asymmetric ciphers + */ + private static final String ASYMMETRIC_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", "IES" + }; + + private static final String[] ASYMMETRIC_CIPHERS = + { + "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145" + }; + + /* + * Configurable digests + */ + private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest."; + private static final String[] DIGESTS = + { + "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Tiger", "Whirlpool" + }; + + /* + * Configurable digests + */ + private static final String KEYSTORE_PACKAGE = "org.bouncycastle.jcajce.provider.keystore."; + private static final String[] KEYSTORES = + { + "BC", "PKCS12" + }; + + /** + * 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.49, info); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + setup(); + return null; + } + }); + } + + private void setup() + { + loadAlgorithms(DIGEST_PACKAGE, DIGESTS); + + loadAlgorithms(SYMMETRIC_PACKAGE, SYMMETRIC_GENERIC); + + loadAlgorithms(SYMMETRIC_PACKAGE, SYMMETRIC_MACS); + + loadAlgorithms(SYMMETRIC_PACKAGE, SYMMETRIC_CIPHERS); + + loadAlgorithms(ASYMMETRIC_PACKAGE, ASYMMETRIC_GENERIC); + + loadAlgorithms(ASYMMETRIC_PACKAGE, ASYMMETRIC_CIPHERS); + + loadAlgorithms(KEYSTORE_PACKAGE, KEYSTORES); + + // + // 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"); + + // + // cipher engines + // + put("Cipher.BROKENPBEWITHMD5ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithMD5AndDES"); + + put("Cipher.BROKENPBEWITHSHA1ANDDES", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$BrokePBEWithSHA1AndDES"); + + + put("Cipher.OLDPBEWITHSHAANDTWOFISH-CBC", "org.bouncycastle.jce.provider.BrokenJCEBlockCipher$OldPBEWithSHAAndTwofish"); + + // Certification Path API + put("CertPathValidator.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathValidatorSpi"); + put("CertPathBuilder.RFC3281", "org.bouncycastle.jce.provider.PKIXAttrCertPathBuilderSpi"); + put("CertPathValidator.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi"); + put("CertPathBuilder.RFC3280", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi"); + put("CertPathValidator.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi"); + put("CertPathBuilder.PKIX", "org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi"); + put("CertStore.Collection", "org.bouncycastle.jce.provider.CertStoreCollectionSpi"); + put("CertStore.LDAP", "org.bouncycastle.jce.provider.X509LDAPCertStoreSpi"); + put("CertStore.Multi", "org.bouncycastle.jce.provider.MultiCertStoreSpi"); + put("Alg.Alias.CertStore.X509LDAP", "LDAP"); + } + + 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!! + throw new InternalError("cannot create instance of " + + packageName + names[i] + "$Mappings : " + e); + } + } + } + } + + 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 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/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java new file mode 100644 index 00000000..cda05e83 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProviderConfiguration.java @@ -0,0 +1,167 @@ +package org.bouncycastle.jce.provider; + +import java.security.Permission; + +import javax.crypto.spec.DHParameterSpec; + +import org.bouncycastle.jcajce.provider.asymmetric.util.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 Object 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)) + { + Object dhSpec; + + if (securityManager != null) + { + securityManager.checkPermission(BC_DH_LOCAL_PERMISSION); + } + + if (parameter instanceof DHParameterSpec || parameter instanceof DHParameterSpec[] || parameter == null) + { + dhSpec = 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 instanceof DHParameterSpec[] || parameter == null) + { + dhDefaultParams = parameter; + } + else + { + throw new IllegalArgumentException("not a valid DHParameterSpec or DHParameterSpec[]"); + } + } + } + + public ECParameterSpec getEcImplicitlyCa() + { + ECParameterSpec spec = (ECParameterSpec)ecThreadSpec.get(); + + if (spec != null) + { + return spec; + } + + return ecImplicitCaParams; + } + + public DHParameterSpec getDHDefaultParameters(int keySize) + { + Object params = dhThreadSpec.get(); + if (params == null) + { + params = dhDefaultParams; + } + + if (params instanceof DHParameterSpec) + { + DHParameterSpec spec = (DHParameterSpec)params; + + if (spec.getP().bitLength() == keySize) + { + return spec; + } + } + else if (params instanceof DHParameterSpec[]) + { + DHParameterSpec[] specs = (DHParameterSpec[])params; + + for (int i = 0; i != specs.length; i++) + { + if (specs[i].getP().bitLength() == keySize) + { + return specs[i]; + } + } + } + + return null; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java b/prov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java new file mode 100644 index 00000000..cb88e208 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BrokenJCEBlockCipher.java @@ -0,0 +1,621 @@ +package org.bouncycastle.jce.provider; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +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.DESEngine; +import org.bouncycastle.crypto.engines.DESedeEngine; +import org.bouncycastle.crypto.engines.TwofishEngine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.CTSBlockCipher; +import org.bouncycastle.crypto.modes.OFBBlockCipher; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.params.RC2Parameters; +import org.bouncycastle.crypto.params.RC5Parameters; +import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; +import org.bouncycastle.util.Strings; + +public class BrokenJCEBlockCipher + implements BrokenPBE +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + IvParameterSpec.class, + PBEParameterSpec.class, + RC2ParameterSpec.class, + RC5ParameterSpec.class + }; + + private BufferedBlockCipher cipher; + private ParametersWithIV ivParam; + + private int pbeType = PKCS12; + private int pbeHash = SHA1; + private int pbeKeySize; + private int pbeIvSize; + + private int ivLength = 0; + + private AlgorithmParameters engineParams = null; + + protected BrokenJCEBlockCipher( + BlockCipher engine) + { + cipher = new PaddedBufferedBlockCipher(engine); + } + + protected BrokenJCEBlockCipher( + BlockCipher engine, + int pbeType, + int pbeHash, + int pbeKeySize, + int pbeIvSize) + { + cipher = new PaddedBufferedBlockCipher(engine); + + this.pbeType = pbeType; + this.pbeHash = pbeHash; + this.pbeKeySize = pbeKeySize; + this.pbeIvSize = pbeIvSize; + } + + protected int engineGetBlockSize() + { + return cipher.getBlockSize(); + } + + protected byte[] engineGetIV() + { + return (ivParam != null) ? ivParam.getIV() : null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length; + } + + protected int engineGetOutputSize( + int inputLen) + { + return cipher.getOutputSize(inputLen); + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + 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) + { + String modeName = Strings.toUpperCase(mode); + + if (modeName.equals("ECB")) + { + ivLength = 0; + cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher()); + } + else if (modeName.equals("CBC")) + { + ivLength = cipher.getUnderlyingCipher().getBlockSize(); + cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(cipher.getUnderlyingCipher())); + } + else if (modeName.startsWith("OFB")) + { + ivLength = cipher.getUnderlyingCipher().getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new PaddedBufferedBlockCipher( + new OFBBlockCipher(cipher.getUnderlyingCipher(), wordSize)); + } + else + { + cipher = new PaddedBufferedBlockCipher( + new OFBBlockCipher(cipher.getUnderlyingCipher(), 8 * cipher.getBlockSize())); + } + } + else if (modeName.startsWith("CFB")) + { + ivLength = cipher.getUnderlyingCipher().getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new PaddedBufferedBlockCipher( + new CFBBlockCipher(cipher.getUnderlyingCipher(), wordSize)); + } + else + { + cipher = new PaddedBufferedBlockCipher( + new CFBBlockCipher(cipher.getUnderlyingCipher(), 8 * cipher.getBlockSize())); + } + } + else + { + throw new IllegalArgumentException("can't support mode " + mode); + } + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String paddingName = Strings.toUpperCase(padding); + + if (paddingName.equals("NOPADDING")) + { + cipher = new BufferedBlockCipher(cipher.getUnderlyingCipher()); + } + else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING") || paddingName.equals("ISO10126PADDING")) + { + cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher()); + } + else if (paddingName.equals("WITHCTS")) + { + cipher = new CTSBlockCipher(cipher.getUnderlyingCipher()); + } + else + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + // + // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it). + // + if (key instanceof BCPBEKey) + { + param = BrokenPBE.Util.makePBEParameters((BCPBEKey)key, params, pbeType, pbeHash, + cipher.getUnderlyingCipher().getAlgorithmName(), pbeKeySize, pbeIvSize); + + if (pbeIvSize != 0) + { + ivParam = (ParametersWithIV)param; + } + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else if (params instanceof IvParameterSpec) + { + if (ivLength != 0) + { + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV()); + ivParam = (ParametersWithIV)param; + } + else + { + param = new KeyParameter(key.getEncoded()); + } + } + 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 (rc5Param.getWordSize() != 32) + { + throw new IllegalArgumentException("can only accept RC5 word size 32 (at the moment...)"); + } + if ((rc5Param.getIV() != null) && (ivLength != 0)) + { + param = new ParametersWithIV(param, rc5Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type."); + } + + if ((ivLength != 0) && !(param instanceof ParametersWithIV)) + { + if (random == null) + { + random = new SecureRandom(); + } + + if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) + { + byte[] iv = new byte[ivLength]; + + random.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()); + } + } + + engineParams = params; + engineInit(opmode, key, paramSpec, random); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + int length = cipher.getUpdateOutputSize(inputLen); + + if (length > 0) + { + byte[] out = new byte[length]; + + cipher.processBytes(input, inputOffset, inputLen, out, 0); + return out; + } + + cipher.processBytes(input, inputOffset, inputLen, null, 0); + + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + 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()); + } + + 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 + { + int len = 0; + + 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()); + } + } + + protected byte[] engineWrap( + Key key) + throws IllegalBlockSizeException, java.security.InvalidKeyException + { + byte[] encoded = key.getEncoded(); + if (encoded == null) + { + throw new InvalidKeyException("Cannot wrap key, null encoding."); + } + + try + { + return engineDoFinal(encoded, 0, encoded.length); + } + catch (BadPaddingException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + protected Key engineUnwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException + { + byte[] encoded = null; + try + { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } + catch (BadPaddingException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (IllegalBlockSizeException e2) + { + throw new InvalidKeyException(e2.getMessage()); + } + + if (wrappedKeyType == Cipher.SECRET_KEY) + { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } + else + { + try + { + KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + + if (wrappedKeyType == Cipher.PUBLIC_KEY) + { + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } + else if (wrappedKeyType == Cipher.PRIVATE_KEY) + { + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + } + catch (NoSuchProviderException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (InvalidKeySpecException e2) + { + throw new InvalidKeyException("Unknown key type " + e2.getMessage()); + } + + throw new InvalidKeyException("Unknown key type " + wrappedKeyType); + } + } + + /* + * The ciphers that inherit from us. + */ + + /** + * PBEWithMD5AndDES + */ + static public class BrokePBEWithMD5AndDES + extends BrokenJCEBlockCipher + { + public BrokePBEWithMD5AndDES() + { + super(new CBCBlockCipher(new DESEngine()), PKCS5S1, MD5, 64, 64); + } + } + + /** + * PBEWithSHA1AndDES + */ + static public class BrokePBEWithSHA1AndDES + extends BrokenJCEBlockCipher + { + public BrokePBEWithSHA1AndDES() + { + super(new CBCBlockCipher(new DESEngine()), PKCS5S1, SHA1, 64, 64); + } + } + + /** + * PBEWithSHAAnd3-KeyTripleDES-CBC + */ + static public class BrokePBEWithSHAAndDES3Key + extends BrokenJCEBlockCipher + { + public BrokePBEWithSHAAndDES3Key() + { + super(new CBCBlockCipher(new DESedeEngine()), PKCS12, SHA1, 192, 64); + } + } + + /** + * OldPBEWithSHAAnd3-KeyTripleDES-CBC + */ + static public class OldPBEWithSHAAndDES3Key + extends BrokenJCEBlockCipher + { + public OldPBEWithSHAAndDES3Key() + { + super(new CBCBlockCipher(new DESedeEngine()), OLD_PKCS12, SHA1, 192, 64); + } + } + + /** + * PBEWithSHAAnd2-KeyTripleDES-CBC + */ + static public class BrokePBEWithSHAAndDES2Key + extends BrokenJCEBlockCipher + { + public BrokePBEWithSHAAndDES2Key() + { + super(new CBCBlockCipher(new DESedeEngine()), PKCS12, SHA1, 128, 64); + } + } + + /** + * OldPBEWithSHAAndTwofish-CBC + */ + static public class OldPBEWithSHAAndTwofish + extends BrokenJCEBlockCipher + { + public OldPBEWithSHAAndTwofish() + { + super(new CBCBlockCipher(new TwofishEngine()), OLD_PKCS12, SHA1, 256, 128); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java b/prov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java new file mode 100644 index 00000000..e6186f67 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BrokenKDF2BytesGenerator.java @@ -0,0 +1,127 @@ +package org.bouncycastle.jce.provider; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.params.KDFParameters; + +/** + * Generator for PBE derived keys and ivs as defined by IEEE P1363a + * <br> + * This implementation is based on draft 9 of IEEE P1363a. <b>Note:</b> + * as this is still a draft the output of this generator may change, don't + * use it for anything that might be subject to long term storage. + */ +public class BrokenKDF2BytesGenerator + implements DerivationFunction +{ + private Digest digest; + private byte[] shared; + private byte[] iv; + + /** + * Construct a KDF2 Parameters generator. Generates key material + * according to IEEE P1363a - if you want orthodox results you should + * use a digest specified in the standard. + * <p> + * <b>Note:</b> IEEE P1363a standard is still a draft standard, if the standard + * changes this function, the output of this function will change as well. + * Don't use this routine for anything subject to long term storage. + * + * @param digest the digest to be used as the source of derived keys. + */ + public BrokenKDF2BytesGenerator( + Digest digest) + { + this.digest = digest; + } + + public void init( + DerivationParameters param) + { + if (!(param instanceof KDFParameters)) + { + throw new IllegalArgumentException("KDF parameters required for KDF2Generator"); + } + + KDFParameters p = (KDFParameters)param; + + shared = p.getSharedSecret(); + iv = p.getIV(); + } + + /** + * return the underlying digest. + */ + public Digest getDigest() + { + return digest; + } + + /** + * fill len bytes of the output buffer with bytes generated from + * the derivation function. + * + * @throws IllegalArgumentException if the size of the request will cause an overflow. + * @throws DataLengthException if the out buffer is too small. + */ + public int generateBytes( + byte[] out, + int outOff, + int len) + throws DataLengthException, IllegalArgumentException + { + if ((out.length - len) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBits = len * 8; + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^23 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBits > (digest.getDigestSize() * 8 * (2L^32 - 1))) + { + new IllegalArgumentException("Output length to large"); + } + + int cThreshold = (int)(oBits / digest.getDigestSize()); + + byte[] dig = null; + + dig = new byte[digest.getDigestSize()]; + + for (int counter = 1; counter <= cThreshold; counter++) + { + digest.update(shared, 0, shared.length); + + digest.update((byte)(counter & 0xff)); + digest.update((byte)((counter >> 8) & 0xff)); + digest.update((byte)((counter >> 16) & 0xff)); + digest.update((byte)((counter >> 24) & 0xff)); + + digest.update(iv, 0, iv.length); + + digest.doFinal(dig, 0); + + if ((len - outOff) > dig.length) + { + System.arraycopy(dig, 0, out, outOff, dig.length); + outOff += dig.length; + } + else + { + System.arraycopy(dig, 0, out, outOff, len - outOff); + } + } + + digest.reset(); + + return len; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BrokenPBE.java b/prov/src/main/java/org/bouncycastle/jce/provider/BrokenPBE.java new file mode 100644 index 00000000..a1736253 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BrokenPBE.java @@ -0,0 +1,441 @@ +package org.bouncycastle.jce.provider; + +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.PBEParameterSpec; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.MD5Digest; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; + +/** + * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0, + * with a bug affecting 180 bit plus keys - this class is only here to + * allow smooth migration of the version 0 keystore to version 1. Don't + * use it (it won't be staying around). + * <p> + * The document this implementation is based on can be found at + * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html> + * RSA's PKCS12 Page</a> + */ +class OldPKCS12ParametersGenerator + extends PBEParametersGenerator +{ + public static final int KEY_MATERIAL = 1; + public static final int IV_MATERIAL = 2; + public static final int MAC_MATERIAL = 3; + + private Digest digest; + + private int u; + private int v; + + /** + * Construct a PKCS 12 Parameters generator. This constructor will + * accept MD5, SHA1, and RIPEMD160. + * + * @param digest the digest to be used as the source of derived keys. + * @exception IllegalArgumentException if an unknown digest is passed in. + */ + public OldPKCS12ParametersGenerator( + Digest digest) + { + this.digest = digest; + if (digest instanceof MD5Digest) + { + u = 128 / 8; + v = 512 / 8; + } + else if (digest instanceof SHA1Digest) + { + u = 160 / 8; + v = 512 / 8; + } + else if (digest instanceof RIPEMD160Digest) + { + u = 160 / 8; + v = 512 / 8; + } + else + { + throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported"); + } + } + + /** + * add a + b + 1, returning the result in a. The a value is treated + * as a BigInteger of length (b.length * 8) bits. The result is + * modulo 2^b.length in case of overflow. + */ + private void adjust( + byte[] a, + int aOff, + byte[] b) + { + int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1; + + a[aOff + b.length - 1] = (byte)x; + x >>>= 8; + + for (int i = b.length - 2; i >= 0; i--) + { + x += (b[i] & 0xff) + (a[aOff + i] & 0xff); + a[aOff + i] = (byte)x; + x >>>= 8; + } + } + + /** + * generation of a derived key ala PKCS12 V1.0. + */ + private byte[] generateDerivedKey( + int idByte, + int n) + { + byte[] D = new byte[v]; + byte[] dKey = new byte[n]; + + for (int i = 0; i != D.length; i++) + { + D[i] = (byte)idByte; + } + + byte[] S; + + if ((salt != null) && (salt.length != 0)) + { + S = new byte[v * ((salt.length + v - 1) / v)]; + + for (int i = 0; i != S.length; i++) + { + S[i] = salt[i % salt.length]; + } + } + else + { + S = new byte[0]; + } + + byte[] P; + + if ((password != null) && (password.length != 0)) + { + P = new byte[v * ((password.length + v - 1) / v)]; + + for (int i = 0; i != P.length; i++) + { + P[i] = password[i % password.length]; + } + } + else + { + P = new byte[0]; + } + + byte[] I = new byte[S.length + P.length]; + + System.arraycopy(S, 0, I, 0, S.length); + System.arraycopy(P, 0, I, S.length, P.length); + + byte[] B = new byte[v]; + int c = (n + u - 1) / u; + + for (int i = 1; i <= c; i++) + { + byte[] A = new byte[u]; + + digest.update(D, 0, D.length); + digest.update(I, 0, I.length); + digest.doFinal(A, 0); + for (int j = 1; j != iterationCount; j++) + { + digest.update(A, 0, A.length); + digest.doFinal(A, 0); + } + + for (int j = 0; j != B.length; j++) + { + B[i] = A[j % A.length]; + } + + for (int j = 0; j != I.length / v; j++) + { + adjust(I, j * v, B); + } + + if (i == c) + { + System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u)); + } + else + { + System.arraycopy(A, 0, dKey, (i - 1) * u, A.length); + } + } + + return dKey; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + */ + public CipherParameters generateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); + + byte[] iv = generateDerivedKey(IV_MATERIAL, ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedMacParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = generateDerivedKey(MAC_MATERIAL, keySize); + + return new KeyParameter(dKey, 0, keySize); + } +} + +public interface BrokenPBE +{ + // + // PBE Based encryption constants - by default we do PKCS12 with SHA-1 + // + static final int MD5 = 0; + static final int SHA1 = 1; + static final int RIPEMD160 = 2; + + static final int PKCS5S1 = 0; + static final int PKCS5S2 = 1; + static final int PKCS12 = 2; + static final int OLD_PKCS12 = 3; + + /** + * uses the appropriate mixer to generate the key and IV if neccessary. + */ + static class Util + { + /** + * a faulty parity routine... + * + * @param bytes the byte array to set the parity on. + */ + static private void setOddParity( + byte[] bytes) + { + for (int i = 0; i < bytes.length; i++) + { + int b = bytes[i]; + bytes[i] = (byte)((b & 0xfe) | + (((b >> 1) ^ + (b >> 2) ^ + (b >> 3) ^ + (b >> 4) ^ + (b >> 5) ^ + (b >> 6) ^ + (b >> 7)) ^ 0x01)); + } + } + + static private PBEParametersGenerator makePBEGenerator( + int type, + int hash) + { + PBEParametersGenerator generator; + + if (type == PKCS5S1) + { + switch (hash) + { + case MD5: + generator = new PKCS5S1ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS5S1ParametersGenerator(new SHA1Digest()); + break; + default: + throw new IllegalStateException("PKCS5 scheme 1 only supports only MD5 and SHA1."); + } + } + else if (type == PKCS5S2) + { + generator = new PKCS5S2ParametersGenerator(); + } + else if (type == OLD_PKCS12) + { + switch (hash) + { + case MD5: + generator = new OldPKCS12ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new OldPKCS12ParametersGenerator(new SHA1Digest()); + break; + case RIPEMD160: + generator = new OldPKCS12ParametersGenerator(new RIPEMD160Digest()); + break; + default: + throw new IllegalStateException("unknown digest scheme for PBE encryption."); + } + } + else + { + switch (hash) + { + case MD5: + generator = new PKCS12ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS12ParametersGenerator(new SHA1Digest()); + break; + case RIPEMD160: + generator = new PKCS12ParametersGenerator(new RIPEMD160Digest()); + break; + default: + throw new IllegalStateException("unknown digest scheme for PBE encryption."); + } + } + + return generator; + } + + /** + * construct a key and iv (if neccessary) suitable for use with a + * Cipher. + */ + static CipherParameters makePBEParameters( + BCPBEKey pbeKey, + AlgorithmParameterSpec spec, + int type, + int hash, + String targetAlgorithm, + int keySize, + int ivSize) + { + if ((spec == null) || !(spec instanceof PBEParameterSpec)) + { + throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); + } + + PBEParameterSpec pbeParam = (PBEParameterSpec)spec; + PBEParametersGenerator generator = makePBEGenerator(type, hash); + byte[] key = pbeKey.getEncoded(); + CipherParameters param; + + generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); + + if (ivSize != 0) + { + param = generator.generateDerivedParameters(keySize, ivSize); + } + else + { + param = generator.generateDerivedParameters(keySize); + } + + if (targetAlgorithm.startsWith("DES")) + { + if (param instanceof ParametersWithIV) + { + KeyParameter kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); + + setOddParity(kParam.getKey()); + } + else + { + KeyParameter kParam = (KeyParameter)param; + + setOddParity(kParam.getKey()); + } + } + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + /** + * generate a PBE based key suitable for a MAC algorithm, the + * key size is chosen according the MAC size, or the hashing algorithm, + * whichever is greater. + */ + static CipherParameters makePBEMacParameters( + BCPBEKey pbeKey, + AlgorithmParameterSpec spec, + int type, + int hash, + int keySize) + { + if ((spec == null) || !(spec instanceof PBEParameterSpec)) + { + throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); + } + + PBEParameterSpec pbeParam = (PBEParameterSpec)spec; + PBEParametersGenerator generator = makePBEGenerator(type, hash); + byte[] key = pbeKey.getEncoded(); + CipherParameters param; + + generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); + + param = generator.generateDerivedMacParameters(keySize); + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java new file mode 100644 index 00000000..9200fdae --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java @@ -0,0 +1,1426 @@ +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.Extension; +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.jce.X509LDAPCertStoreParameters; +import org.bouncycastle.jce.exception.ExtCertPathValidatorException; +import org.bouncycastle.util.Integers; +import org.bouncycastle.util.Selector; +import org.bouncycastle.util.StoreException; +import org.bouncycastle.x509.ExtendedPKIXBuilderParameters; +import org.bouncycastle.x509.ExtendedPKIXParameters; +import org.bouncycastle.x509.X509AttributeCertStoreSelector; +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 = Extension.certificatePolicies.getId(); + protected static final String BASIC_CONSTRAINTS = Extension.basicConstraints.getId(); + protected static final String POLICY_MAPPINGS = Extension.policyMappings.getId(); + protected static final String SUBJECT_ALTERNATIVE_NAME = Extension.subjectAlternativeName.getId(); + protected static final String NAME_CONSTRAINTS = Extension.nameConstraints.getId(); + protected static final String KEY_USAGE = Extension.keyUsage.getId(); + protected static final String INHIBIT_ANY_POLICY = Extension.inhibitAnyPolicy.getId(); + protected static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId(); + protected static final String DELTA_CRL_INDICATOR = Extension.deltaCRLIndicator.getId(); + protected static final String POLICY_CONSTRAINTS = Extension.policyConstraints.getId(); + protected static final String FRESHEST_CRL = Extension.freshestCRL.getId(); + protected static final String CRL_DISTRIBUTION_POINTS = Extension.cRLDistributionPoints.getId(); + protected static final String AUTHORITY_KEY_IDENTIFIER = Extension.authorityKeyIdentifier.getId(); + + protected static final String ANY_POLICY = "2.5.29.32.0"; + + protected static final String CRL_NUMBER = Extension.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(); + if (list.get(0).equals(Integers.valueOf(GeneralName.uniformResourceIdentifier))) + { + // 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 + { + 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)); + } + } + 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; + } + + 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; + } + + 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/prov/src/main/java/org/bouncycastle/jce/provider/CertStatus.java b/prov/src/main/java/org/bouncycastle/jce/provider/CertStatus.java new file mode 100644 index 00000000..ba3da165 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/CertStoreCollectionSpi.java new file mode 100644 index 00000000..210d986d --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/DHUtil.java b/prov/src/main/java/org/bouncycastle/jce/provider/DHUtil.java new file mode 100644 index 00000000..2470af99 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java b/prov/src/main/java/org/bouncycastle/jce/provider/ExtCRLException.java new file mode 100644 index 00000000..3bc820f3 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEDHPrivateKey.java new file mode 100644 index 00000000..b38f60bb --- /dev/null +++ b/prov/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( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEDHPublicKey.java new file mode 100644 index 00000000..6ff1e083 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java new file mode 100644 index 00000000..3175237d --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java @@ -0,0 +1,478 @@ +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; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; +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.util.EC5Util; +import org.bouncycastle.jcajce.provider.asymmetric.util.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); + + 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 + { + 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 + { + if (algorithm.equals("ECGOST3410")) + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + } + else + { + + 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( + ASN1ObjectIdentifier 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/prov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java new file mode 100644 index 00000000..00df81f9 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java @@ -0,0 +1,525 @@ +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; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +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.util.EC5Util; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.bouncycastle.jce.ECGOST3410NamedCurveTable; +import org.bouncycastle.jce.interfaces.ECPointEncoder; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +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; + private GOST3410PublicKeyAlgParameters gostParams; + + public JCEECPublicKey( + String algorithm, + JCEECPublicKey key) + { + this.algorithm = algorithm; + this.q = key.q; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.gostParams = key.gostParams; + } + + 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) + { + 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 + { + 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; + + 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); + + try + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; + } + } + else + { + 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/prov/src/main/java/org/bouncycastle/jce/provider/JCEElGamalPrivateKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEElGamalPrivateKey.java new file mode 100644 index 00000000..afaddfa0 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCEElGamalPrivateKey.java @@ -0,0 +1,167 @@ +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.ASN1Integer; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.oiw.ElGamalParameter; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.bouncycastle.jce.interfaces.ElGamalPrivateKey; +import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.bouncycastle.jce.spec.ElGamalParameterSpec; +import org.bouncycastle.jce.spec.ElGamalPrivateKeySpec; + +public class JCEElGamalPrivateKey + implements ElGamalPrivateKey, DHPrivateKey, PKCS12BagAttributeCarrier +{ + static final long serialVersionUID = 4819350091141529678L; + + BigInteger x; + + ElGamalParameterSpec elSpec; + + private PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected JCEElGamalPrivateKey() + { + } + + JCEElGamalPrivateKey( + ElGamalPrivateKey key) + { + this.x = key.getX(); + this.elSpec = key.getParameters(); + } + + JCEElGamalPrivateKey( + DHPrivateKey key) + { + this.x = key.getX(); + this.elSpec = new ElGamalParameterSpec(key.getParams().getP(), key.getParams().getG()); + } + + JCEElGamalPrivateKey( + ElGamalPrivateKeySpec spec) + { + this.x = spec.getX(); + this.elSpec = new ElGamalParameterSpec(spec.getParams().getP(), spec.getParams().getG()); + } + + JCEElGamalPrivateKey( + DHPrivateKeySpec spec) + { + this.x = spec.getX(); + this.elSpec = new ElGamalParameterSpec(spec.getP(), spec.getG()); + } + + JCEElGamalPrivateKey( + PrivateKeyInfo info) + throws IOException + { + ElGamalParameter params = new ElGamalParameter((ASN1Sequence)info.getAlgorithmId().getParameters()); + DERInteger derX = ASN1Integer.getInstance(info.parsePrivateKey()); + + this.x = derX.getValue(); + this.elSpec = new ElGamalParameterSpec(params.getP(), params.getG()); + } + + JCEElGamalPrivateKey( + ElGamalPrivateKeyParameters params) + { + this.x = params.getX(); + this.elSpec = new ElGamalParameterSpec(params.getParameters().getP(), params.getParameters().getG()); + } + + public String getAlgorithm() + { + return "ElGamal"; + } + + /** + * 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() + { + return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(elSpec.getP(), elSpec.getG())), new DERInteger(getX())); + } + + public ElGamalParameterSpec getParameters() + { + return elSpec; + } + + public DHParameterSpec getParams() + { + return new DHParameterSpec(elSpec.getP(), elSpec.getG()); + } + + public BigInteger getX() + { + return x; + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + x = (BigInteger)in.readObject(); + + this.elSpec = new ElGamalParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject()); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.writeObject(this.getX()); + out.writeObject(elSpec.getP()); + out.writeObject(elSpec.getG()); + } + + public void setBagAttribute( + ASN1ObjectIdentifier oid, + ASN1Encodable attribute) + { + attrCarrier.setBagAttribute(oid, attribute); + } + + public ASN1Encodable getBagAttribute( + ASN1ObjectIdentifier oid) + { + return attrCarrier.getBagAttribute(oid); + } + + public Enumeration getBagAttributeKeys() + { + return attrCarrier.getBagAttributeKeys(); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/JCEElGamalPublicKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEElGamalPublicKey.java new file mode 100644 index 00000000..cb7a0abf --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCEElGamalPublicKey.java @@ -0,0 +1,140 @@ +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.oiw.ElGamalParameter; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.bouncycastle.jce.interfaces.ElGamalPublicKey; +import org.bouncycastle.jce.spec.ElGamalParameterSpec; +import org.bouncycastle.jce.spec.ElGamalPublicKeySpec; + +public class JCEElGamalPublicKey + implements ElGamalPublicKey, DHPublicKey +{ + static final long serialVersionUID = 8712728417091216948L; + + private BigInteger y; + private ElGamalParameterSpec elSpec; + + JCEElGamalPublicKey( + ElGamalPublicKeySpec spec) + { + this.y = spec.getY(); + this.elSpec = new ElGamalParameterSpec(spec.getParams().getP(), spec.getParams().getG()); + } + + JCEElGamalPublicKey( + DHPublicKeySpec spec) + { + this.y = spec.getY(); + this.elSpec = new ElGamalParameterSpec(spec.getP(), spec.getG()); + } + + JCEElGamalPublicKey( + ElGamalPublicKey key) + { + this.y = key.getY(); + this.elSpec = key.getParameters(); + } + + JCEElGamalPublicKey( + DHPublicKey key) + { + this.y = key.getY(); + this.elSpec = new ElGamalParameterSpec(key.getParams().getP(), key.getParams().getG()); + } + + JCEElGamalPublicKey( + ElGamalPublicKeyParameters params) + { + this.y = params.getY(); + this.elSpec = new ElGamalParameterSpec(params.getParameters().getP(), params.getParameters().getG()); + } + + JCEElGamalPublicKey( + BigInteger y, + ElGamalParameterSpec elSpec) + { + this.y = y; + this.elSpec = elSpec; + } + + JCEElGamalPublicKey( + SubjectPublicKeyInfo info) + { + ElGamalParameter params = new ElGamalParameter((ASN1Sequence)info.getAlgorithmId().getParameters()); + DERInteger derY = null; + + try + { + derY = (DERInteger)info.parsePublicKey(); + } + catch (IOException e) + { + throw new IllegalArgumentException("invalid info structure in DSA public key"); + } + + this.y = derY.getValue(); + this.elSpec = new ElGamalParameterSpec(params.getP(), params.getG()); + } + + public String getAlgorithm() + { + return "ElGamal"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(OIWObjectIdentifiers.elGamalAlgorithm, new ElGamalParameter(elSpec.getP(), elSpec.getG())), new DERInteger(y)); + } + + public ElGamalParameterSpec getParameters() + { + return elSpec; + } + + public DHParameterSpec getParams() + { + return new DHParameterSpec(elSpec.getP(), elSpec.getG()); + } + + public BigInteger getY() + { + return y; + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + this.y = (BigInteger)in.readObject(); + this.elSpec = new ElGamalParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject()); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.writeObject(this.getY()); + out.writeObject(elSpec.getP()); + out.writeObject(elSpec.getG()); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java new file mode 100644 index 00000000..f9bb5dd3 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey.java @@ -0,0 +1,241 @@ +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() + { + return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPrivateKey(getModulus(), getPublicExponent(), getPrivateExponent(), getPrimeP(), getPrimeQ(), getPrimeExponentP(), getPrimeExponentQ(), getCrtCoefficient())); + } + + /** + * 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/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java new file mode 100644 index 00000000..cacedd4b --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPrivateKey.java @@ -0,0 +1,146 @@ +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.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() + { + return KeyUtil.getEncodedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new org.bouncycastle.asn1.pkcs.RSAPrivateKey(getModulus(), ZERO, getPrivateExponent(), ZERO, ZERO, ZERO, ZERO, ZERO)); + } + + 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( + ASN1ObjectIdentifier 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/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java new file mode 100644 index 00000000..a09295d5 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCERSAPublicKey.java @@ -0,0 +1,131 @@ +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() + { + return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKeyStructure(getModulus(), getPublicExponent())); + } + + 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/prov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java b/prov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java new file mode 100644 index 00000000..46104b27 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java @@ -0,0 +1,613 @@ +package org.bouncycastle.jce.provider; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +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; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +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; +import org.bouncycastle.crypto.engines.BlowfishEngine; +import org.bouncycastle.crypto.engines.DESEngine; +import org.bouncycastle.crypto.engines.DESedeEngine; +import org.bouncycastle.crypto.engines.RC4Engine; +import org.bouncycastle.crypto.engines.SkipjackEngine; +import org.bouncycastle.crypto.engines.TwofishEngine; +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 = + { + RC2ParameterSpec.class, + RC5ParameterSpec.class, + 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) + throws BadPaddingException, IllegalBlockSizeException + { + 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) + throws BadPaddingException + { + if (inputLen != 0) + { + cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + cipher.reset(); + + return inputLen; + } + + protected byte[] engineWrap( + Key key) + throws IllegalBlockSizeException, InvalidKeyException + { + byte[] encoded = key.getEncoded(); + if (encoded == null) + { + throw new InvalidKeyException("Cannot wrap key, null encoding."); + } + + try + { + return engineDoFinal(encoded, 0, encoded.length); + } + catch (BadPaddingException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + protected Key engineUnwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException + { + byte[] encoded; + try + { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } + catch (BadPaddingException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (IllegalBlockSizeException e2) + { + throw new InvalidKeyException(e2.getMessage()); + } + + if (wrappedKeyType == Cipher.SECRET_KEY) + { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } + else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY) + { + /* + * The caller doesn't know the algorithm as it is part of + * the encrypted data. + */ + try + { + PrivateKeyInfo in = PrivateKeyInfo.getInstance(encoded); + + PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in); + + if (privKey != null) + { + return privKey; + } + else + { + throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("Invalid key encoding."); + } + } + else + { + try + { + KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + + if (wrappedKeyType == Cipher.PUBLIC_KEY) + { + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } + else if (wrappedKeyType == Cipher.PRIVATE_KEY) + { + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + } + catch (NoSuchProviderException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (InvalidKeySpecException e2) + { + throw new InvalidKeyException("Unknown key type " + e2.getMessage()); + } + + throw new InvalidKeyException("Unknown key type " + wrappedKeyType); + } + } + + /* + * The ciphers that inherit from us. + */ + + /** + * 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); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java new file mode 100644 index 00000000..50a714c8 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPrivateKey.java @@ -0,0 +1,180 @@ +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.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 = DSAParameter.getInstance(info.getPrivateKeyAlgorithm().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( + ASN1ObjectIdentifier 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/prov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java b/prov/src/main/java/org/bouncycastle/jce/provider/JDKDSAPublicKey.java new file mode 100644 index 00000000..85a39a46 --- /dev/null +++ b/prov/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.getAlgorithm().getParameters())) + { + DSAParameter params = DSAParameter.getInstance(info.getAlgorithm().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/prov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java b/prov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java new file mode 100644 index 00000000..7e8340aa --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/JDKPKCS12StoreParameter.java @@ -0,0 +1,51 @@ +package org.bouncycastle.jce.provider; + +import java.io.OutputStream; +import java.security.KeyStore; +import java.security.KeyStore.LoadStoreParameter; +import java.security.KeyStore.ProtectionParameter; + +/** + * @deprecated use org.bouncycastle.jcajce.config.PKCS12StoreParameter + */ +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/prov/src/main/java/org/bouncycastle/jce/provider/MultiCertStoreSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/MultiCertStoreSpi.java new file mode 100644 index 00000000..cf3d15d7 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/MultiCertStoreSpi.java @@ -0,0 +1,85 @@ +package org.bouncycastle.jce.provider; + +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.CRLSelector; +import java.security.cert.CertSelector; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.CertStoreParameters; +import java.security.cert.CertStoreSpi; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.jce.MultiCertStoreParameters; + +public class MultiCertStoreSpi + extends CertStoreSpi +{ + private MultiCertStoreParameters params; + + public MultiCertStoreSpi(CertStoreParameters params) + throws InvalidAlgorithmParameterException + { + super(params); + + if (!(params instanceof MultiCertStoreParameters)) + { + throw new InvalidAlgorithmParameterException("org.bouncycastle.jce.provider.MultiCertStoreSpi: parameter must be a MultiCertStoreParameters object\n" + params.toString()); + } + + this.params = (MultiCertStoreParameters)params; + } + + public Collection engineGetCertificates(CertSelector certSelector) + throws CertStoreException + { + boolean searchAllStores = params.getSearchAllStores(); + Iterator iter = params.getCertStores().iterator(); + List allCerts = searchAllStores ? new ArrayList() : Collections.EMPTY_LIST; + + while (iter.hasNext()) + { + CertStore store = (CertStore)iter.next(); + Collection certs = store.getCertificates(certSelector); + + if (searchAllStores) + { + allCerts.addAll(certs); + } + else if (!certs.isEmpty()) + { + return certs; + } + } + + return allCerts; + } + + public Collection engineGetCRLs(CRLSelector crlSelector) + throws CertStoreException + { + boolean searchAllStores = params.getSearchAllStores(); + Iterator iter = params.getCertStores().iterator(); + List allCRLs = searchAllStores ? new ArrayList() : Collections.EMPTY_LIST; + + while (iter.hasNext()) + { + CertStore store = (CertStore)iter.next(); + Collection crls = store.getCRLs(crlSelector); + + if (searchAllStores) + { + allCRLs.addAll(crls); + } + else if (!crls.isEmpty()) + { + return crls; + } + } + + return allCRLs; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java b/prov/src/main/java/org/bouncycastle/jce/provider/PEMUtil.java new file mode 100644 index 00000000..04718efc --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java new file mode 100644 index 00000000..14aef43e --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathBuilderSpi.java @@ -0,0 +1,303 @@ +package org.bouncycastle.jce.provider; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.Principal; +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 java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import org.bouncycastle.jce.exception.ExtCertPathBuilderException; +import org.bouncycastle.util.Selector; +import org.bouncycastle.x509.ExtendedPKIXBuilderParameters; +import org.bouncycastle.x509.X509AttributeCertStoreSelector; +import org.bouncycastle.x509.X509AttributeCertificate; +import org.bouncycastle.x509.X509CertStoreSelector; + +public class PKIXAttrCertPathBuilderSpi + 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; + if (params instanceof ExtendedPKIXBuilderParameters) + { + pkixParams = (ExtendedPKIXBuilderParameters) params; + } + else + { + pkixParams = (ExtendedPKIXBuilderParameters) ExtendedPKIXBuilderParameters + .getInstance((PKIXBuilderParameters) params); + } + + Collection targets; + Iterator targetIter; + List certPathList = new ArrayList(); + X509AttributeCertificate cert; + + // search target certificates + + Selector certSelect = pkixParams.getTargetConstraints(); + if (!(certSelect instanceof X509AttributeCertStoreSelector)) + { + throw new CertPathBuilderException( + "TargetConstraints must be an instance of " + + X509AttributeCertStoreSelector.class.getName() + + " for "+this.getClass().getName()+" class."); + } + + try + { + targets = CertPathValidatorUtilities.findCertificates((X509AttributeCertStoreSelector)certSelect, pkixParams.getStores()); + } + catch (AnnotatedException e) + { + throw new ExtCertPathBuilderException("Error finding target attribute certificate.", e); + } + + if (targets.isEmpty()) + { + throw new CertPathBuilderException( + "No attribute certificate found matching targetContraints."); + } + + CertPathBuilderResult result = null; + + // check all potential target certificates + targetIter = targets.iterator(); + while (targetIter.hasNext() && result == null) + { + cert = (X509AttributeCertificate) targetIter.next(); + + X509CertStoreSelector selector = new X509CertStoreSelector(); + Principal[] principals = cert.getIssuer().getPrincipals(); + Set issuers = new HashSet(); + for (int i = 0; i < principals.length; i++) + { + try + { + if (principals[i] instanceof X500Principal) + { + selector.setSubject(((X500Principal)principals[i]).getEncoded()); + } + issuers.addAll(CertPathValidatorUtilities.findCertificates(selector, pkixParams.getStores())); + issuers.addAll(CertPathValidatorUtilities.findCertificates(selector, pkixParams.getCertStores())); + } + catch (AnnotatedException e) + { + throw new ExtCertPathBuilderException( + "Public key certificate for attribute certificate cannot be searched.", + e); + } + catch (IOException e) + { + throw new ExtCertPathBuilderException( + "cannot encode X500Principal.", + e); + } + } + if (issuers.isEmpty()) + { + throw new CertPathBuilderException( + "Public key certificate for attribute certificate cannot be found."); + } + Iterator it = issuers.iterator(); + while (it.hasNext() && result == null) + { + result = build(cert, (X509Certificate)it.next(), pkixParams, certPathList); + } + } + + if (result == null && certPathException != null) + { + throw new ExtCertPathBuilderException( + "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; + + private CertPathBuilderResult build(X509AttributeCertificate attrCert, 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("RFC3281", 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) + { + CertPath certPath; + PKIXCertPathValidatorResult result; + 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 additional 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(); + // TODO Use CertPathValidatorUtilities.isSelfIssued(issuer)? + // if untrusted self signed certificate continue + if (issuer.getIssuerX500Principal().equals( + issuer.getSubjectX500Principal())) + { + continue; + } + builderResult = build(attrCert, issuer, pkixParams, tbvPath); + } + } + } + catch (AnnotatedException e) + { + certPathException = new AnnotatedException( + "No valid certification path could be build.", e); + } + if (builderResult == null) + { + tbvPath.remove(tbvCert); + } + return builderResult; + } + +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathValidatorSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathValidatorSpi.java new file mode 100644 index 00000000..c1759bac --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXAttrCertPathValidatorSpi.java @@ -0,0 +1,99 @@ +package org.bouncycastle.jce.provider; + +import java.security.InvalidAlgorithmParameterException; +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.X509Certificate; +import java.util.Date; +import java.util.Set; + +import org.bouncycastle.jce.exception.ExtCertPathValidatorException; +import org.bouncycastle.util.Selector; +import org.bouncycastle.x509.ExtendedPKIXParameters; +import org.bouncycastle.x509.X509AttributeCertStoreSelector; +import org.bouncycastle.x509.X509AttributeCertificate; + +/** + * CertPathValidatorSpi implementation for X.509 Attribute Certificates la RFC 3281. + * + * @see org.bouncycastle.x509.ExtendedPKIXParameters + */ +public class PKIXAttrCertPathValidatorSpi + extends CertPathValidatorSpi +{ + + /** + * Validates an attribute certificate with the given certificate path. + * + * <p> + * <code>params</code> must be an instance of + * <code>ExtendedPKIXParameters</code>. + * <p> + * The target constraints in the <code>params</code> must be an + * <code>X509AttributeCertStoreSelector</code> with at least the attribute + * certificate criterion set. Obey that also target informations may be + * necessary to correctly validate this attribute certificate. + * <p> + * The attribute certificate issuer must be added to the trusted attribute + * issuers with {@link ExtendedPKIXParameters#setTrustedACIssuers(Set)}. + * + * @param certPath The certificate path which belongs to the attribute + * certificate issuer public key certificate. + * @param params The PKIX parameters. + * @return A <code>PKIXCertPathValidatorResult</code> of the result of + * validating the <code>certPath</code>. + * @throws InvalidAlgorithmParameterException if <code>params</code> is + * inappropriate for this validator. + * @throws CertPathValidatorException if the verification fails. + */ + public CertPathValidatorResult engineValidate(CertPath certPath, + CertPathParameters params) throws CertPathValidatorException, + InvalidAlgorithmParameterException + { + if (!(params instanceof ExtendedPKIXParameters)) + { + throw new InvalidAlgorithmParameterException( + "Parameters must be a " + + ExtendedPKIXParameters.class.getName() + " instance."); + } + ExtendedPKIXParameters pkixParams = (ExtendedPKIXParameters) params; + + Selector certSelect = pkixParams.getTargetConstraints(); + if (!(certSelect instanceof X509AttributeCertStoreSelector)) + { + throw new InvalidAlgorithmParameterException( + "TargetConstraints must be an instance of " + + X509AttributeCertStoreSelector.class.getName() + " for " + + this.getClass().getName() + " class."); + } + X509AttributeCertificate attrCert = ((X509AttributeCertStoreSelector) certSelect) + .getAttributeCert(); + + CertPath holderCertPath = RFC3281CertPathUtilities.processAttrCert1(attrCert, pkixParams); + CertPathValidatorResult result = RFC3281CertPathUtilities.processAttrCert2(certPath, pkixParams); + X509Certificate issuerCert = (X509Certificate) certPath + .getCertificates().get(0); + RFC3281CertPathUtilities.processAttrCert3(issuerCert, pkixParams); + RFC3281CertPathUtilities.processAttrCert4(issuerCert, pkixParams); + RFC3281CertPathUtilities.processAttrCert5(attrCert, pkixParams); + // 6 already done in X509AttributeCertStoreSelector + RFC3281CertPathUtilities.processAttrCert7(attrCert, certPath, holderCertPath, pkixParams); + RFC3281CertPathUtilities.additionalChecks(attrCert, pkixParams); + Date date = null; + try + { + date = CertPathValidatorUtilities + .getValidCertDateFromValidityModel(pkixParams, null, -1); + } + catch (AnnotatedException e) + { + throw new ExtCertPathValidatorException( + "Could not get validity date from attribute certificate.", e); + } + RFC3281CertPathUtilities.checkCRLs(attrCert, pkixParams, issuerCert, date, certPath.getCertificates()); + return result; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCRLUtil.java new file mode 100644 index 00000000..c94016d7 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathBuilderSpi.java new file mode 100644 index 00000000..384eb861 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java new file mode 100644 index 00000000..f28a02a7 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi.java @@ -0,0 +1,431 @@ +package org.bouncycastle.jce.provider; + +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 +{ + + 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); + } + + // + // (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--) + { + // 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/prov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java new file mode 100644 index 00000000..7ecc4860 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidator.java @@ -0,0 +1,1927 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; +import java.util.Collections; +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.Integers; +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); + } + } + + public void intersectPermittedSubtree(GeneralSubtree permitted) + { + intersectPermittedSubtree(new GeneralSubtree[] { permitted }); + } + + /** + * Updates the permitted set of these name constraints with the intersection + * with the given subtree. + * + * @param permitted The permitted subtrees + */ + + public void intersectPermittedSubtree(GeneralSubtree[] permitted) + { + Map subtreesMap = new HashMap(); + + // group in sets in a map ordered by tag no. + for (int i = 0; i != permitted.length; i++) + { + GeneralSubtree subtree = permitted[i]; + Integer tagNo = Integers.valueOf(subtree.getBase().getTagNo()); + 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/prov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXNameConstraintValidatorException.java new file mode 100644 index 00000000..b06d5e5b --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java new file mode 100644 index 00000000..34376055 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java new file mode 100644 index 00000000..769edb8a --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java @@ -0,0 +1,2565 @@ +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())); + + } + + public static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId(); + + public static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId(); + + public static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId(); + + public static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId(); + + public static final String FRESHEST_CRL = X509Extensions.FreshestCRL.getId(); + + public static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId(); + + public static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId(); + + public static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId(); + + public static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints.getId(); + + public static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId(); + + public static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId(); + + public static final String AUTHORITY_KEY_IDENTIFIER = X509Extensions.AuthorityKeyIdentifier.getId(); + + public static final String KEY_USAGE = X509Extensions.KeyUsage.getId(); + + public static final String CRL_NUMBER = X509Extensions.CRLNumber.getId(); + + public 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 + // + GeneralSubtree[] 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 + // + GeneralSubtree[] excluded = nc.getExcludedSubtrees(); + if (excluded != null) + { + for (int i = 0; i != excluded.length; i++) + try + { + nameConstraintValidator.addExcludedSubtree(excluded[i]); + } + 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: " + criticalExtensions, 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: " + criticalExtensions, 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/prov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java new file mode 100644 index 00000000..19dbae1d --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/RFC3281CertPathUtilities.java @@ -0,0 +1,703 @@ +package org.bouncycastle.jce.provider; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.cert.CertPath; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertPathBuilderResult; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorResult; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Date; +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.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Primitive; +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.TargetInformation; +import org.bouncycastle.asn1.x509.X509Extensions; +import org.bouncycastle.jce.exception.ExtCertPathValidatorException; +import org.bouncycastle.x509.ExtendedPKIXBuilderParameters; +import org.bouncycastle.x509.ExtendedPKIXParameters; +import org.bouncycastle.x509.PKIXAttrCertChecker; +import org.bouncycastle.x509.X509AttributeCertificate; +import org.bouncycastle.x509.X509CertStoreSelector; + +class RFC3281CertPathUtilities +{ + + private static final String TARGET_INFORMATION = X509Extensions.TargetInformation + .getId(); + + private static final String NO_REV_AVAIL = X509Extensions.NoRevAvail + .getId(); + + private static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints + .getId(); + + private static final String AUTHORITY_INFO_ACCESS = X509Extensions.AuthorityInfoAccess + .getId(); + + protected static void processAttrCert7(X509AttributeCertificate attrCert, + CertPath certPath, CertPath holderCertPath, + ExtendedPKIXParameters pkixParams) throws CertPathValidatorException + { + // TODO: + // AA Controls + // Attribute encryption + // Proxy + Set set = attrCert.getCriticalExtensionOIDs(); + // 7.1 + // process extensions + + // target information checked in step 6 / X509AttributeCertStoreSelector + if (set.contains(TARGET_INFORMATION)) + { + try + { + TargetInformation.getInstance(CertPathValidatorUtilities + .getExtensionValue(attrCert, TARGET_INFORMATION)); + } + catch (AnnotatedException e) + { + throw new ExtCertPathValidatorException( + "Target information extension could not be read.", e); + } + catch (IllegalArgumentException e) + { + throw new ExtCertPathValidatorException( + "Target information extension could not be read.", e); + } + } + set.remove(TARGET_INFORMATION); + for (Iterator it = pkixParams.getAttrCertCheckers().iterator(); it + .hasNext();) + { + ((PKIXAttrCertChecker) it.next()).check(attrCert, certPath, + holderCertPath, set); + } + if (!set.isEmpty()) + { + throw new CertPathValidatorException( + "Attribute certificate contains unsupported critical extensions: " + + set); + } + } + + /** + * Checks if an attribute certificate is revoked. + * + * @param attrCert Attribute certificate to check if it is revoked. + * @param paramsPKIX PKIX parameters. + * @param issuerCert The issuer certificate of the attribute certificate + * <code>attrCert</code>. + * @param validDate The date when the certificate revocation status should + * be checked. + * @param certPathCerts The certificates of the certification path to be + * checked. + * + * @throws CertPathValidatorException if the certificate is revoked or the + * status cannot be checked or some error occurs. + */ + protected static void checkCRLs(X509AttributeCertificate attrCert, + ExtendedPKIXParameters paramsPKIX, X509Certificate issuerCert, + Date validDate, List certPathCerts) throws CertPathValidatorException + { + if (paramsPKIX.isRevocationEnabled()) + { + // check if revocation is available + if (attrCert.getExtensionValue(NO_REV_AVAIL) == null) + { + CRLDistPoint crldp = null; + try + { + crldp = CRLDistPoint.getInstance(CertPathValidatorUtilities + .getExtensionValue(attrCert, CRL_DISTRIBUTION_POINTS)); + } + catch (AnnotatedException e) + { + throw new CertPathValidatorException( + "CRL distribution point extension could not be read.", + e); + } + try + { + CertPathValidatorUtilities + .addAdditionalStoresFromCRLDistributionPoint(crldp, + paramsPKIX); + } + catch (AnnotatedException e) + { + throw new CertPathValidatorException( + "No additional CRL locations could be decoded from CRL distribution point extension.", + e); + } + CertStatus certStatus = new CertStatus(); + ReasonsMask reasonsMask = new ReasonsMask(); + + AnnotatedException lastException = null; + boolean validCrlFound = false; + // for each distribution point + if (crldp != null) + { + DistributionPoint dps[] = null; + try + { + dps = crldp.getDistributionPoints(); + } + catch (Exception e) + { + throw new ExtCertPathValidatorException( + "Distribution points could not be read.", e); + } + try + { + for (int i = 0; i < dps.length + && certStatus.getCertStatus() == CertStatus.UNREVOKED + && !reasonsMask.isAllReasons(); i++) + { + ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters) paramsPKIX + .clone(); + checkCRL(dps[i], attrCert, paramsPKIXClone, + validDate, issuerCert, certStatus, reasonsMask, + certPathCerts); + validCrlFound = true; + } + } + catch (AnnotatedException e) + { + lastException = new AnnotatedException( + "No valid CRL for distribution point found.", 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( + ((X500Principal) attrCert.getIssuer() + .getPrincipals()[0]).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, attrCert, paramsPKIXClone, validDate, + issuerCert, certStatus, reasonsMask, certPathCerts); + validCrlFound = true; + } + catch (AnnotatedException e) + { + lastException = new AnnotatedException( + "No valid CRL for distribution point found.", e); + } + } + + if (!validCrlFound) + { + throw new ExtCertPathValidatorException( + "No valid CRL found.", lastException); + } + if (certStatus.getCertStatus() != CertStatus.UNREVOKED) + { + String message = "Attribute certificate revocation after " + + certStatus.getRevocationDate(); + message += ", reason: " + + RFC3280CertPathUtilities.crlReasons[certStatus + .getCertStatus()]; + throw new CertPathValidatorException(message); + } + if (!reasonsMask.isAllReasons() + && certStatus.getCertStatus() == CertStatus.UNREVOKED) + { + certStatus.setCertStatus(CertStatus.UNDETERMINED); + } + if (certStatus.getCertStatus() == CertStatus.UNDETERMINED) + { + throw new CertPathValidatorException( + "Attribute certificate status could not be determined."); + } + + } + else + { + if (attrCert.getExtensionValue(CRL_DISTRIBUTION_POINTS) != null + || attrCert.getExtensionValue(AUTHORITY_INFO_ACCESS) != null) + { + throw new CertPathValidatorException( + "No rev avail extension is set, but also an AC revocation pointer."); + } + } + } + } + + protected static void additionalChecks(X509AttributeCertificate attrCert, + ExtendedPKIXParameters pkixParams) throws CertPathValidatorException + { + // 1 + for (Iterator it = pkixParams.getProhibitedACAttributes().iterator(); it + .hasNext();) + { + String oid = (String) it.next(); + if (attrCert.getAttributes(oid) != null) + { + throw new CertPathValidatorException( + "Attribute certificate contains prohibited attribute: " + + oid + "."); + } + } + for (Iterator it = pkixParams.getNecessaryACAttributes().iterator(); it + .hasNext();) + { + String oid = (String) it.next(); + if (attrCert.getAttributes(oid) == null) + { + throw new CertPathValidatorException( + "Attribute certificate does not contain necessary attribute: " + + oid + "."); + } + } + } + + protected static void processAttrCert5(X509AttributeCertificate attrCert, + ExtendedPKIXParameters pkixParams) throws CertPathValidatorException + { + try + { + attrCert.checkValidity(CertPathValidatorUtilities + .getValidDate(pkixParams)); + } + catch (CertificateExpiredException e) + { + throw new ExtCertPathValidatorException( + "Attribute certificate is not valid.", e); + } + catch (CertificateNotYetValidException e) + { + throw new ExtCertPathValidatorException( + "Attribute certificate is not valid.", e); + } + } + + protected static void processAttrCert4(X509Certificate acIssuerCert, + ExtendedPKIXParameters pkixParams) throws CertPathValidatorException + { + Set set = pkixParams.getTrustedACIssuers(); + boolean trusted = false; + for (Iterator it = set.iterator(); it.hasNext();) + { + TrustAnchor anchor = (TrustAnchor) it.next(); + if (acIssuerCert.getSubjectX500Principal().getName("RFC2253") + .equals(anchor.getCAName()) + || acIssuerCert.equals(anchor.getTrustedCert())) + { + trusted = true; + } + } + if (!trusted) + { + throw new CertPathValidatorException( + "Attribute certificate issuer is not directly trusted."); + } + } + + protected static void processAttrCert3(X509Certificate acIssuerCert, + ExtendedPKIXParameters pkixParams) throws CertPathValidatorException + { + if (acIssuerCert.getKeyUsage() != null + && (!acIssuerCert.getKeyUsage()[0] && !acIssuerCert.getKeyUsage()[1])) + { + throw new CertPathValidatorException( + "Attribute certificate issuer public key cannot be used to validate digital signatures."); + } + if (acIssuerCert.getBasicConstraints() != -1) + { + throw new CertPathValidatorException( + "Attribute certificate issuer is also a public key certificate issuer."); + } + } + + protected static CertPathValidatorResult processAttrCert2( + CertPath certPath, ExtendedPKIXParameters pkixParams) + throws CertPathValidatorException + { + CertPathValidator validator = null; + try + { + validator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME); + } + catch (NoSuchProviderException e) + { + throw new ExtCertPathValidatorException( + "Support class could not be created.", e); + } + catch (NoSuchAlgorithmException e) + { + throw new ExtCertPathValidatorException( + "Support class could not be created.", e); + } + try + { + return validator.validate(certPath, pkixParams); + } + catch (CertPathValidatorException e) + { + throw new ExtCertPathValidatorException( + "Certification path for issuer certificate of attribute certificate could not be validated.", + e); + } + catch (InvalidAlgorithmParameterException e) + { + // must be a programming error + throw new RuntimeException(e.getMessage()); + } + } + + /** + * Searches for a holder public key certificate and verifies its + * certification path. + * + * @param attrCert the attribute certificate. + * @param pkixParams The PKIX parameters. + * @return The certificate path of the holder certificate. + * @throws AnnotatedException if + * <ul> + * <li>no public key certificate can be found although holder + * information is given by an entity name or a base certificate + * ID + * <li>support classes cannot be created + * <li>no certification path for the public key certificate can + * be built + * </ul> + */ + protected static CertPath processAttrCert1( + X509AttributeCertificate attrCert, ExtendedPKIXParameters pkixParams) + throws CertPathValidatorException + { + CertPathBuilderResult result = null; + // find holder PKCs + Set holderPKCs = new HashSet(); + if (attrCert.getHolder().getIssuer() != null) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + selector.setSerialNumber(attrCert.getHolder().getSerialNumber()); + Principal[] principals = attrCert.getHolder().getIssuer(); + for (int i = 0; i < principals.length; i++) + { + try + { + if (principals[i] instanceof X500Principal) + { + selector.setIssuer(((X500Principal)principals[i]) + .getEncoded()); + } + holderPKCs.addAll(CertPathValidatorUtilities + .findCertificates(selector, pkixParams.getStores())); + } + catch (AnnotatedException e) + { + throw new ExtCertPathValidatorException( + "Public key certificate for attribute certificate cannot be searched.", + e); + } + catch (IOException e) + { + throw new ExtCertPathValidatorException( + "Unable to encode X500 principal.", e); + } + } + if (holderPKCs.isEmpty()) + { + throw new CertPathValidatorException( + "Public key certificate specified in base certificate ID for attribute certificate cannot be found."); + } + } + if (attrCert.getHolder().getEntityNames() != null) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + Principal[] principals = attrCert.getHolder().getEntityNames(); + for (int i = 0; i < principals.length; i++) + { + try + { + if (principals[i] instanceof X500Principal) + { + selector.setIssuer(((X500Principal) principals[i]) + .getEncoded()); + } + holderPKCs.addAll(CertPathValidatorUtilities + .findCertificates(selector, pkixParams.getStores())); + } + catch (AnnotatedException e) + { + throw new ExtCertPathValidatorException( + "Public key certificate for attribute certificate cannot be searched.", + e); + } + catch (IOException e) + { + throw new ExtCertPathValidatorException( + "Unable to encode X500 principal.", e); + } + } + if (holderPKCs.isEmpty()) + { + throw new CertPathValidatorException( + "Public key certificate specified in entity name for attribute certificate cannot be found."); + } + } + // verify cert paths for PKCs + ExtendedPKIXBuilderParameters params = (ExtendedPKIXBuilderParameters) ExtendedPKIXBuilderParameters + .getInstance(pkixParams); + CertPathValidatorException lastException = null; + for (Iterator it = holderPKCs.iterator(); it.hasNext();) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + selector.setCertificate((X509Certificate) it.next()); + params.setTargetConstraints(selector); + CertPathBuilder builder = null; + try + { + builder = CertPathBuilder.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME); + } + catch (NoSuchProviderException e) + { + throw new ExtCertPathValidatorException( + "Support class could not be created.", e); + } + catch (NoSuchAlgorithmException e) + { + throw new ExtCertPathValidatorException( + "Support class could not be created.", e); + } + try + { + result = builder.build(ExtendedPKIXBuilderParameters + .getInstance(params)); + } + catch (CertPathBuilderException e) + { + lastException = new ExtCertPathValidatorException( + "Certification path for public key certificate of attribute certificate could not be build.", + e); + } + catch (InvalidAlgorithmParameterException e) + { + // must be a programming error + throw new RuntimeException(e.getMessage()); + } + } + if (lastException != null) + { + throw lastException; + } + return result.getCertPath(); + } + + /** + * + * Checks a distribution point for revocation information for the + * certificate <code>attrCert</code>. + * + * @param dp The distribution point to consider. + * @param attrCert The attribute certificate which should be checked. + * @param paramsPKIX PKIX parameters. + * @param validDate The date when the certificate revocation status should + * be checked. + * @param issuerCert Certificate to check if it is revoked. + * @param reasonMask The reasons mask which is already checked. + * @param certPathCerts The certificates of the certification path to be + * checked. + * @throws AnnotatedException if the certificate is revoked or the status + * cannot be checked or some error occurs. + */ + private static void checkCRL(DistributionPoint dp, + X509AttributeCertificate attrCert, ExtendedPKIXParameters paramsPKIX, + Date validDate, X509Certificate issuerCert, CertStatus certStatus, + ReasonsMask reasonMask, List certPathCerts) throws AnnotatedException + { + + /* + * 4.3.6 No Revocation Available + * + * The noRevAvail extension, defined in [X.509-2000], allows an AC + * issuer to indicate that no revocation information will be made + * available for this AC. + */ + if (attrCert.getExtensionValue(X509Extensions.NoRevAvail.getId()) != null) + { + return; + } + 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, attrCert, + 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, attrCert, + null, null, 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 vality 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 (attrCert.getNotAfter().getTime() < crl.getThisUpdate() + .getTime()) + { + throw new AnnotatedException( + "No valid CRL for current time found."); + } + } + + RFC3280CertPathUtilities.processCRLB1(dp, attrCert, crl); + + // (b) (2) + RFC3280CertPathUtilities.processCRLB2(dp, attrCert, crl); + + // (c) + RFC3280CertPathUtilities.processCRLC(deltaCRL, crl, paramsPKIX); + + // (i) + RFC3280CertPathUtilities.processCRLI(validDate, deltaCRL, + attrCert, certStatus, paramsPKIX); + + // (j) + RFC3280CertPathUtilities.processCRLJ(validDate, crl, attrCert, + certStatus); + + // (k) + if (certStatus.getCertStatus() == CRLReason.removeFromCRL) + { + certStatus.setCertStatus(CertStatus.UNREVOKED); + } + + // update reasons mask + reasonMask.addReasons(interimReasonsMask); + validCrlFound = true; + } + catch (AnnotatedException e) + { + lastException = e; + } + } + if (!validCrlFound) + { + throw lastException; + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java b/prov/src/main/java/org/bouncycastle/jce/provider/ReasonsMask.java new file mode 100644 index 00000000..04f5a063 --- /dev/null +++ b/prov/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/prov/src/main/java/org/bouncycastle/jce/provider/X509AttrCertParser.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509AttrCertParser.java new file mode 100644 index 00000000..847f32bd --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509AttrCertParser.java @@ -0,0 +1,156 @@ +package org.bouncycastle.jce.provider; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.SignedData; +import org.bouncycastle.x509.X509AttributeCertificate; +import org.bouncycastle.x509.X509StreamParserSpi; +import org.bouncycastle.x509.X509V2AttributeCertificate; +import org.bouncycastle.x509.util.StreamParsingException; + +public class X509AttrCertParser + extends X509StreamParserSpi +{ + private static final PEMUtil PEM_PARSER = new PEMUtil("ATTRIBUTE CERTIFICATE"); + + private ASN1Set sData = null; + private int sDataObjectCount = 0; + private InputStream currentStream = null; + + private X509AttributeCertificate readDERCertificate( + InputStream in) + throws IOException + { + ASN1InputStream dIn = new ASN1InputStream(in); + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + + if (seq.size() > 1 + && seq.getObjectAt(0) instanceof DERObjectIdentifier) + { + if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) + { + sData = new SignedData(ASN1Sequence.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates(); + + return getCertificate(); + } + } + + return new X509V2AttributeCertificate(seq.getEncoded()); + } + + private X509AttributeCertificate getCertificate() + throws IOException + { + if (sData != null) + { + while (sDataObjectCount < sData.size()) + { + Object obj = sData.getObjectAt(sDataObjectCount++); + + if (obj instanceof ASN1TaggedObject && ((ASN1TaggedObject)obj).getTagNo() == 2) + { + return new X509V2AttributeCertificate( + ASN1Sequence.getInstance((ASN1TaggedObject)obj, false).getEncoded()); + } + } + } + + return null; + } + + private X509AttributeCertificate readPEMCertificate( + InputStream in) + throws IOException + { + ASN1Sequence seq = PEM_PARSER.readPEMObject(in); + + if (seq != null) + { + return new X509V2AttributeCertificate(seq.getEncoded()); + } + + return null; + } + + public void engineInit(InputStream in) + { + currentStream = in; + sData = null; + sDataObjectCount = 0; + + if (!currentStream.markSupported()) + { + currentStream = new BufferedInputStream(currentStream); + } + } + + public Object engineRead() + throws StreamParsingException + { + try + { + if (sData != null) + { + if (sDataObjectCount != sData.size()) + { + return getCertificate(); + } + else + { + sData = null; + sDataObjectCount = 0; + return null; + } + } + + currentStream.mark(10); + int tag = currentStream.read(); + + if (tag == -1) + { + return null; + } + + if (tag != 0x30) // assume ascii PEM encoded. + { + currentStream.reset(); + return readPEMCertificate(currentStream); + } + else + { + currentStream.reset(); + return readDERCertificate(currentStream); + } + } + catch (Exception e) + { + throw new StreamParsingException(e.toString(), e); + } + } + + public Collection engineReadAll() + throws StreamParsingException + { + X509AttributeCertificate cert; + List certs = new ArrayList(); + + while ((cert = (X509AttributeCertificate)engineRead()) != null) + { + certs.add(cert); + } + + return certs; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java new file mode 100644 index 00000000..d5c3700a --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java @@ -0,0 +1,301 @@ +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.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +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; + +/** + * 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; + } + + Extension ext = getExtension(Extension.certificateIssuer); + if (ext == null) + { + return previousCertificateIssuer; + } + + try + { + GeneralName[] names = GeneralNames.getInstance(ext.getParsedValue()).getNames(); + for (int i = 0; i < names.length; i++) + { + if (names[i].getTagNo() == GeneralName.directoryName) + { + return X500Name.getInstance(names[i].getName()); + } + } + return null; + } + catch (Exception 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); + } + + private Extension getExtension(ASN1ObjectIdentifier oid) + { + Extensions exts = c.getExtensions(); + + if (exts != null) + { + return exts.getExtension(oid); + } + + return null; + } + + public byte[] getExtensionValue(String oid) + { + Extension ext = 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(ASN1Enumerated.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/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java new file mode 100644 index 00000000..cd832118 --- /dev/null +++ b/prov/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.ASN1Integer; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +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; + +/** + * 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(ASN1OctetString.getInstance(idp).getOctets()).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(ASN1Integer.getInstance( + dIn.readObject()).getPositiveValue())) + .append(nl); + } + else if (oid.equals(Extension.deltaCRLIndicator)) + { + buf.append( + "Base CRL: " + + new CRLNumber(ASN1Integer.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/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLParser.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLParser.java new file mode 100644 index 00000000..40f0a64f --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLParser.java @@ -0,0 +1,150 @@ +package org.bouncycastle.jce.provider; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.CRL; +import java.security.cert.CRLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.SignedData; +import org.bouncycastle.asn1.x509.CertificateList; +import org.bouncycastle.x509.X509StreamParserSpi; +import org.bouncycastle.x509.util.StreamParsingException; + +public class X509CRLParser + extends X509StreamParserSpi +{ + private static final PEMUtil PEM_PARSER = new PEMUtil("CRL"); + + private ASN1Set sData = null; + private int sDataObjectCount = 0; + private InputStream currentStream = null; + + private CRL readDERCRL( + InputStream in) + throws IOException, CRLException + { + ASN1InputStream dIn = new ASN1InputStream(in); + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + + if (seq.size() > 1 + && seq.getObjectAt(0) instanceof DERObjectIdentifier) + { + if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) + { + sData = new SignedData(ASN1Sequence.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs(); + + return getCRL(); + } + } + + return new X509CRLObject(CertificateList.getInstance(seq)); + } + + private CRL getCRL() + throws CRLException + { + if (sData == null || sDataObjectCount >= sData.size()) + { + return null; + } + + return new X509CRLObject( + CertificateList.getInstance( + sData.getObjectAt(sDataObjectCount++))); + } + + private CRL readPEMCRL( + InputStream in) + throws IOException, CRLException + { + ASN1Sequence seq = PEM_PARSER.readPEMObject(in); + + if (seq != null) + { + return new X509CRLObject(CertificateList.getInstance(seq)); + } + + return null; + } + + public void engineInit(InputStream in) + { + currentStream = in; + sData = null; + sDataObjectCount = 0; + + if (!currentStream.markSupported()) + { + currentStream = new BufferedInputStream(currentStream); + } + } + + public Object engineRead() + throws StreamParsingException + { + try + { + if (sData != null) + { + if (sDataObjectCount != sData.size()) + { + return getCRL(); + } + else + { + sData = null; + sDataObjectCount = 0; + return null; + } + } + + currentStream.mark(10); + int tag = currentStream.read(); + + if (tag == -1) + { + return null; + } + + if (tag != 0x30) // assume ascii PEM encoded. + { + currentStream.reset(); + return readPEMCRL(currentStream); + } + else + { + currentStream.reset(); + return readDERCRL(currentStream); + } + } + catch (Exception e) + { + throw new StreamParsingException(e.toString(), e); + } + } + + public Collection engineReadAll() + throws StreamParsingException + { + CRL crl; + List certs = new ArrayList(); + + while ((crl = (CRL)engineRead()) != null) + { + certs.add(crl); + } + + return certs; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CertPairParser.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertPairParser.java new file mode 100644 index 00000000..41d64480 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertPairParser.java @@ -0,0 +1,77 @@ +package org.bouncycastle.jce.provider; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.CertificateParsingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.x509.CertificatePair; +import org.bouncycastle.x509.X509CertificatePair; +import org.bouncycastle.x509.X509StreamParserSpi; +import org.bouncycastle.x509.util.StreamParsingException; + +public class X509CertPairParser + extends X509StreamParserSpi +{ + private InputStream currentStream = null; + + private X509CertificatePair readDERCrossCertificatePair( + InputStream in) + throws IOException, CertificateParsingException + { + ASN1InputStream dIn = new ASN1InputStream(in); + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + CertificatePair pair = CertificatePair.getInstance(seq); + return new X509CertificatePair(pair); + } + + public void engineInit(InputStream in) + { + currentStream = in; + + if (!currentStream.markSupported()) + { + currentStream = new BufferedInputStream(currentStream); + } + } + + public Object engineRead() throws StreamParsingException + { + try + { + + currentStream.mark(10); + int tag = currentStream.read(); + + if (tag == -1) + { + return null; + } + + currentStream.reset(); + return readDERCrossCertificatePair(currentStream); + } + catch (Exception e) + { + throw new StreamParsingException(e.toString(), e); + } + } + + public Collection engineReadAll() throws StreamParsingException + { + X509CertificatePair pair; + List certs = new ArrayList(); + + while ((pair = (X509CertificatePair)engineRead()) != null) + { + certs.add(pair); + } + + return certs; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CertParser.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertParser.java new file mode 100644 index 00000000..a407ba83 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertParser.java @@ -0,0 +1,158 @@ +package org.bouncycastle.jce.provider; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.SignedData; +import org.bouncycastle.x509.X509StreamParserSpi; +import org.bouncycastle.x509.util.StreamParsingException; + +public class X509CertParser + extends X509StreamParserSpi +{ + private static final PEMUtil PEM_PARSER = new PEMUtil("CERTIFICATE"); + + private ASN1Set sData = null; + private int sDataObjectCount = 0; + private InputStream currentStream = null; + + private Certificate readDERCertificate( + InputStream in) + throws IOException, CertificateParsingException + { + ASN1InputStream dIn = new ASN1InputStream(in); + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + + if (seq.size() > 1 + && seq.getObjectAt(0) instanceof DERObjectIdentifier) + { + if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) + { + sData = new SignedData(ASN1Sequence.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates(); + + return getCertificate(); + } + } + + return new X509CertificateObject( + org.bouncycastle.asn1.x509.Certificate.getInstance(seq)); + } + + private Certificate getCertificate() + throws CertificateParsingException + { + if (sData != null) + { + while (sDataObjectCount < sData.size()) + { + Object obj = sData.getObjectAt(sDataObjectCount++); + + if (obj instanceof ASN1Sequence) + { + return new X509CertificateObject( + org.bouncycastle.asn1.x509.Certificate.getInstance(obj)); + } + } + } + + return null; + } + + private Certificate readPEMCertificate( + InputStream in) + throws IOException, CertificateParsingException + { + ASN1Sequence seq = PEM_PARSER.readPEMObject(in); + + if (seq != null) + { + return new X509CertificateObject( + org.bouncycastle.asn1.x509.Certificate.getInstance(seq)); + } + + return null; + } + + public void engineInit(InputStream in) + { + currentStream = in; + sData = null; + sDataObjectCount = 0; + + if (!currentStream.markSupported()) + { + currentStream = new BufferedInputStream(currentStream); + } + } + + public Object engineRead() + throws StreamParsingException + { + try + { + if (sData != null) + { + if (sDataObjectCount != sData.size()) + { + return getCertificate(); + } + else + { + sData = null; + sDataObjectCount = 0; + return null; + } + } + + currentStream.mark(10); + int tag = currentStream.read(); + + if (tag == -1) + { + return null; + } + + if (tag != 0x30) // assume ascii PEM encoded. + { + currentStream.reset(); + return readPEMCertificate(currentStream); + } + else + { + currentStream.reset(); + return readDERCertificate(currentStream); + } + } + catch (Exception e) + { + throw new StreamParsingException(e.toString(), e); + } + } + + public Collection engineReadAll() + throws StreamParsingException + { + Certificate cert; + List certs = new ArrayList(); + + while ((cert = (Certificate)engineRead()) != null) + { + certs.add(cert); + } + + return certs; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java new file mode 100644 index 00000000..97ff6f98 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java @@ -0,0 +1,901 @@ +package org.bouncycastle.jce.provider; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +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.Collection; +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.ASN1String; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +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.x500.style.RFC4519Style; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.KeyUsage; +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.Integers; +import org.bouncycastle.util.encoders.Hex; + +public class X509CertificateObject + extends X509Certificate + implements PKCS12BagAttributeCarrier +{ + private org.bouncycastle.asn1.x509.Certificate c; + private BasicConstraints basicConstraints; + private boolean[] keyUsage; + private boolean hashValueSet; + private int hashValue; + + private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + public X509CertificateObject( + org.bouncycastle.asn1.x509.Certificate 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.getVersionNumber(); + } + + 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().getAlgorithm().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(((ASN1ObjectIdentifier)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 Collection getSubjectAlternativeNames() + throws CertificateParsingException + { + return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId())); + } + + public Collection getIssuerAlternativeNames() + throws CertificateParsingException + { + return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId())); + } + + public Set getCriticalExtensionOIDs() + { + if (this.getVersion() == 3) + { + Set set = new HashSet(); + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (ext.isCritical()) + { + set.add(oid.getId()); + } + } + + return set; + } + } + + return null; + } + + private byte[] getExtensionBytes(String oid) + { + Extensions exts = c.getTBSCertificate().getExtensions(); + + if (exts != null) + { + Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); + if (ext != null) + { + return ext.getExtnValue().getOctets(); + } + } + + return null; + } + + public byte[] getExtensionValue(String oid) + { + Extensions exts = c.getTBSCertificate().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 Set getNonCriticalExtensionOIDs() + { + if (this.getVersion() == 3) + { + Set set = new HashSet(); + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + Extension ext = extensions.getExtension(oid); + + if (!ext.isCritical()) + { + set.add(oid.getId()); + } + } + + return set; + } + } + + return null; + } + + public boolean hasUnsupportedCriticalExtension() + { + if (this.getVersion() == 3) + { + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)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; + } + + Extension 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... + } + } + + public byte[] getEncoded() + throws CertificateEncodingException + { + try + { + return c.getEncoded(ASN1Encoding.DER); + } + 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( + ASN1ObjectIdentifier 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); + } + } + + Extensions extensions = c.getTBSCertificate().getExtensions(); + + if (extensions != null) + { + Enumeration e = extensions.oids(); + + if (e.hasMoreElements()) + { + buf.append(" Extensions: \n"); + } + + 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.basicConstraints)) + { + buf.append(BasicConstraints.getInstance(dIn.readObject())).append(nl); + } + else if (oid.equals(Extension.keyUsage)) + { + buf.append(KeyUsage.getInstance(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.getExtnValue().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.getAlgorithm().equals(id2.getAlgorithm())) + { + return false; + } + + if (id1.getParameters() == null) + { + if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE)) + { + return false; + } + + return true; + } + + if (id2.getParameters() == null) + { + if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE)) + { + return false; + } + + return true; + } + + return id1.getParameters().equals(id2.getParameters()); + } + + private static Collection getAlternativeNames(byte[] extVal) + throws CertificateParsingException + { + if (extVal == null) + { + return null; + } + try + { + Collection temp = new ArrayList(); + Enumeration it = ASN1Sequence.getInstance(extVal).getObjects(); + while (it.hasMoreElements()) + { + GeneralName genName = GeneralName.getInstance(it.nextElement()); + List list = new ArrayList(); + list.add(Integers.valueOf(genName.getTagNo())); + switch (genName.getTagNo()) + { + case GeneralName.ediPartyName: + case GeneralName.x400Address: + case GeneralName.otherName: + list.add(genName.getEncoded()); + break; + case GeneralName.directoryName: + list.add(X500Name.getInstance(RFC4519Style.INSTANCE, genName.getName()).toString()); + break; + case GeneralName.dNSName: + case GeneralName.rfc822Name: + case GeneralName.uniformResourceIdentifier: + list.add(((ASN1String)genName.getName()).getString()); + break; + case GeneralName.registeredID: + list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId()); + break; + case GeneralName.iPAddress: + byte[] addrBytes = DEROctetString.getInstance(genName.getName()).getOctets(); + final String addr; + try + { + addr = InetAddress.getByAddress(addrBytes).getHostAddress(); + } + catch (UnknownHostException e) + { + continue; + } + list.add(addr); + break; + default: + throw new IOException("Bad tag number: " + genName.getTagNo()); + } + + temp.add(Collections.unmodifiableList(list)); + } + if (temp.size() == 0) + { + return null; + } + return Collections.unmodifiableCollection(temp); + } + catch (Exception e) + { + throw new CertificateParsingException(e.getMessage()); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java new file mode 100644 index 00000000..3797607c --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509LDAPCertStoreSpi.java @@ -0,0 +1,477 @@ +package org.bouncycastle.jce.provider; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +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.CertificateFactory; +import java.security.cert.X509CRLSelector; +import java.security.cert.X509CertSelector; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.security.auth.x500.X500Principal; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.x509.CertificatePair; +import org.bouncycastle.jce.X509LDAPCertStoreParameters; + +/** + * + * This is a general purpose implementation to get X.509 certificates and CRLs + * from a LDAP location. + * <p> + * At first a search is performed in the ldap*AttributeNames of the + * {@link org.bouncycastle.jce.X509LDAPCertStoreParameters} with the given + * information of the subject (for all kind of certificates) or issuer (for + * CRLs), respectively, if a X509CertSelector is given with that details. For + * CRLs, CA certificates and cross certificates a coarse search is made only for + * entries with that content to get more possibly matchign results. + */ +public class X509LDAPCertStoreSpi + extends CertStoreSpi +{ + private X509LDAPCertStoreParameters params; + + public X509LDAPCertStoreSpi(CertStoreParameters params) + throws InvalidAlgorithmParameterException + { + super(params); + + if (!(params instanceof X509LDAPCertStoreParameters)) + { + throw new InvalidAlgorithmParameterException( + X509LDAPCertStoreSpi.class.getName() + ": parameter must be a " + X509LDAPCertStoreParameters.class.getName() + " object\n" + + params.toString()); + } + + this.params = (X509LDAPCertStoreParameters)params; + } + + /** + * Initial Context Factory. + */ + private static String LDAP_PROVIDER = "com.sun.jndi.ldap.LdapCtxFactory"; + + /** + * Processing referrals.. + */ + private static String REFERRALS_IGNORE = "ignore"; + + /** + * Security level to be used for LDAP connections. + */ + private static final String SEARCH_SECURITY_LEVEL = "none"; + + /** + * Package Prefix for loading URL context factories. + */ + private static final String URL_CONTEXT_PREFIX = "com.sun.jndi.url"; + + private DirContext connectLDAP() throws NamingException + { + Properties props = new Properties(); + props.setProperty(Context.INITIAL_CONTEXT_FACTORY, LDAP_PROVIDER); + props.setProperty(Context.BATCHSIZE, "0"); + + props.setProperty(Context.PROVIDER_URL, params.getLdapURL()); + props.setProperty(Context.URL_PKG_PREFIXES, URL_CONTEXT_PREFIX); + props.setProperty(Context.REFERRAL, REFERRALS_IGNORE); + props.setProperty(Context.SECURITY_AUTHENTICATION, + SEARCH_SECURITY_LEVEL); + + DirContext ctx = new InitialDirContext(props); + return ctx; + } + + private String parseDN(String subject, String subjectAttributeName) + { + String temp = subject; + int begin = temp.toLowerCase().indexOf( + subjectAttributeName.toLowerCase()); + temp = temp.substring(begin + subjectAttributeName.length()); + int end = temp.indexOf(','); + if (end == -1) + { + end = temp.length(); + } + while (temp.charAt(end - 1) == '\\') + { + end = temp.indexOf(',', end + 1); + if (end == -1) + { + end = temp.length(); + } + } + temp = temp.substring(0, end); + begin = temp.indexOf('='); + temp = temp.substring(begin + 1); + if (temp.charAt(0) == ' ') + { + temp = temp.substring(1); + } + if (temp.startsWith("\"")) + { + temp = temp.substring(1); + } + if (temp.endsWith("\"")) + { + temp = temp.substring(0, temp.length() - 1); + } + return temp; + } + + public Collection engineGetCertificates(CertSelector selector) + throws CertStoreException + { + if (!(selector instanceof X509CertSelector)) + { + throw new CertStoreException("selector is not a X509CertSelector"); + } + X509CertSelector xselector = (X509CertSelector)selector; + + Set certSet = new HashSet(); + + Set set = getEndCertificates(xselector); + set.addAll(getCACertificates(xselector)); + set.addAll(getCrossCertificates(xselector)); + + Iterator it = set.iterator(); + + try + { + CertificateFactory cf = CertificateFactory.getInstance("X.509", + BouncyCastleProvider.PROVIDER_NAME); + while (it.hasNext()) + { + byte[] bytes = (byte[])it.next(); + if (bytes == null || bytes.length == 0) + { + continue; + } + + List bytesList = new ArrayList(); + bytesList.add(bytes); + + try + { + CertificatePair pair = CertificatePair + .getInstance(new ASN1InputStream(bytes) + .readObject()); + bytesList.clear(); + if (pair.getForward() != null) + { + bytesList.add(pair.getForward().getEncoded()); + } + if (pair.getReverse() != null) + { + bytesList.add(pair.getReverse().getEncoded()); + } + } + catch (IOException e) + { + + } + catch (IllegalArgumentException e) + { + + } + for (Iterator it2 = bytesList.iterator(); it2.hasNext();) + { + ByteArrayInputStream bIn = new ByteArrayInputStream( + (byte[])it2.next()); + try + { + Certificate cert = cf.generateCertificate(bIn); + // System.out.println(((X509Certificate) + // cert).getSubjectX500Principal()); + if (xselector.match(cert)) + { + certSet.add(cert); + } + } + catch (Exception e) + { + + } + } + } + } + catch (Exception e) + { + throw new CertStoreException( + "certificate cannot be constructed from LDAP result: " + e); + } + + return certSet; + } + + private Set certSubjectSerialSearch(X509CertSelector xselector, + String[] attrs, String attrName, String subjectAttributeName) + throws CertStoreException + { + Set set = new HashSet(); + try + { + if (xselector.getSubjectAsBytes() != null + || xselector.getSubjectAsString() != null + || xselector.getCertificate() != null) + { + String subject = null; + String serial = null; + if (xselector.getCertificate() != null) + { + subject = xselector.getCertificate() + .getSubjectX500Principal().getName("RFC1779"); + serial = xselector.getCertificate().getSerialNumber() + .toString(); + } + else + { + if (xselector.getSubjectAsBytes() != null) + { + subject = new X500Principal(xselector + .getSubjectAsBytes()).getName("RFC1779"); + } + else + { + subject = xselector.getSubjectAsString(); + } + } + String attrValue = parseDN(subject, subjectAttributeName); + set.addAll(search(attrName, "*" + attrValue + "*", attrs)); + if (serial != null + && params.getSearchForSerialNumberIn() != null) + { + attrValue = serial; + attrName = params.getSearchForSerialNumberIn(); + set.addAll(search(attrName, "*" + attrValue + "*", attrs)); + } + } + else + { + set.addAll(search(attrName, "*", attrs)); + } + } + catch (IOException e) + { + throw new CertStoreException("exception processing selector: " + e); + } + + return set; + } + + private Set getEndCertificates(X509CertSelector xselector) + throws CertStoreException + { + String[] attrs = {params.getUserCertificateAttribute()}; + String attrName = params.getLdapUserCertificateAttributeName(); + String subjectAttributeName = params.getUserCertificateSubjectAttributeName(); + + Set set = certSubjectSerialSearch(xselector, attrs, attrName, + subjectAttributeName); + return set; + } + + private Set getCACertificates(X509CertSelector xselector) + throws CertStoreException + { + String[] attrs = {params.getCACertificateAttribute()}; + String attrName = params.getLdapCACertificateAttributeName(); + String subjectAttributeName = params + .getCACertificateSubjectAttributeName(); + Set set = certSubjectSerialSearch(xselector, attrs, attrName, + subjectAttributeName); + + if (set.isEmpty()) + { + set.addAll(search(null, "*", attrs)); + } + + return set; + } + + private Set getCrossCertificates(X509CertSelector xselector) + throws CertStoreException + { + String[] attrs = {params.getCrossCertificateAttribute()}; + String attrName = params.getLdapCrossCertificateAttributeName(); + String subjectAttributeName = params + .getCrossCertificateSubjectAttributeName(); + Set set = certSubjectSerialSearch(xselector, attrs, attrName, + subjectAttributeName); + + if (set.isEmpty()) + { + set.addAll(search(null, "*", attrs)); + } + + return set; + } + + public Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException + { + String[] attrs = {params.getCertificateRevocationListAttribute()}; + if (!(selector instanceof X509CRLSelector)) + { + throw new CertStoreException("selector is not a X509CRLSelector"); + } + X509CRLSelector xselector = (X509CRLSelector)selector; + + Set crlSet = new HashSet(); + + String attrName = params.getLdapCertificateRevocationListAttributeName(); + Set set = new HashSet(); + + if (xselector.getIssuerNames() != null) + { + for (Iterator it = xselector.getIssuerNames().iterator(); it + .hasNext();) + { + Object o = it.next(); + String attrValue = null; + if (o instanceof String) + { + String issuerAttributeName = params + .getCertificateRevocationListIssuerAttributeName(); + attrValue = parseDN((String)o, issuerAttributeName); + } + else + { + String issuerAttributeName = params + .getCertificateRevocationListIssuerAttributeName(); + attrValue = parseDN(new X500Principal((byte[])o) + .getName("RFC1779"), issuerAttributeName); + } + set.addAll(search(attrName, "*" + attrValue + "*", attrs)); + } + } + else + { + set.addAll(search(attrName, "*", attrs)); + } + set.addAll(search(null, "*", attrs)); + Iterator it = set.iterator(); + + try + { + CertificateFactory cf = CertificateFactory.getInstance("X.509", + BouncyCastleProvider.PROVIDER_NAME); + while (it.hasNext()) + { + CRL crl = cf.generateCRL(new ByteArrayInputStream((byte[])it + .next())); + if (xselector.match(crl)) + { + crlSet.add(crl); + } + } + } + catch (Exception e) + { + throw new CertStoreException( + "CRL cannot be constructed from LDAP result " + e); + } + + return crlSet; + } + + /** + * Returns a Set of byte arrays with the certificate or CRL encodings. + * + * @param attributeName The attribute name to look for in the LDAP. + * @param attributeValue The value the attribute name must have. + * @param attrs The attributes in the LDAP which hold the certificate, + * certificate pair or CRL in a found entry. + * @return Set of byte arrays with the certificate encodings. + */ + private Set search(String attributeName, String attributeValue, + String[] attrs) throws CertStoreException + { + String filter = attributeName + "=" + attributeValue; + if (attributeName == null) + { + filter = null; + } + DirContext ctx = null; + Set set = new HashSet(); + try + { + + ctx = connectLDAP(); + + SearchControls constraints = new SearchControls(); + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + constraints.setCountLimit(0); + for (int i = 0; i < attrs.length; i++) + { + String temp[] = new String[1]; + temp[0] = attrs[i]; + constraints.setReturningAttributes(temp); + + String filter2 = "(&(" + filter + ")(" + temp[0] + "=*))"; + if (filter == null) + { + filter2 = "(" + temp[0] + "=*)"; + } + NamingEnumeration results = ctx.search(params.getBaseDN(), + filter2, constraints); + while (results.hasMoreElements()) + { + SearchResult sr = (SearchResult)results.next(); + // should only be one attribute in the attribute set with + // one + // attribute value as byte array + NamingEnumeration enumeration = ((Attribute)(sr + .getAttributes().getAll().next())).getAll(); + while (enumeration.hasMore()) + { + Object o = enumeration.next(); + set.add(o); + } + } + } + } + catch (Exception e) + { + throw new CertStoreException( + "Error getting results from LDAP directory " + e); + + } + finally + { + try + { + if (null != ctx) + { + ctx.close(); + } + } + catch (Exception e) + { + } + } + return set; + } + +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java new file mode 100644 index 00000000..c9a13885 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java @@ -0,0 +1,138 @@ +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; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +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 +{ + private static final ASN1Null derNull = DERNull.INSTANCE; + + 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)) + { + if (sigAlgId.getObjectId().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + { + RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); + + return getDigestAlgName(rsaParams.getHashAlgorithm().getObjectId()) + "withRSAandMGF1"; + } + 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"; + } + else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID)) + { + return "SHA512"; + } + 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"; + } + else + { + return digestAlgOID.getId(); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreAttrCertCollection.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreAttrCertCollection.java new file mode 100644 index 00000000..7e2dc6a3 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreAttrCertCollection.java @@ -0,0 +1,34 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; + +import org.bouncycastle.util.CollectionStore; +import org.bouncycastle.util.Selector; +import org.bouncycastle.x509.X509CollectionStoreParameters; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; + +public class X509StoreAttrCertCollection + extends X509StoreSpi +{ + private CollectionStore _store; + + public X509StoreAttrCertCollection() + { + } + + public void engineInit(X509StoreParameters params) + { + if (!(params instanceof X509CollectionStoreParameters)) + { + throw new IllegalArgumentException(params.toString()); + } + + _store = new CollectionStore(((X509CollectionStoreParameters)params).getCollection()); + } + + public Collection engineGetMatches(Selector selector) + { + return _store.getMatches(selector); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCRLCollection.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCRLCollection.java new file mode 100644 index 00000000..b914f171 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCRLCollection.java @@ -0,0 +1,34 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; + +import org.bouncycastle.util.CollectionStore; +import org.bouncycastle.util.Selector; +import org.bouncycastle.x509.X509CollectionStoreParameters; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; + +public class X509StoreCRLCollection + extends X509StoreSpi +{ + private CollectionStore _store; + + public X509StoreCRLCollection() + { + } + + public void engineInit(X509StoreParameters params) + { + if (!(params instanceof X509CollectionStoreParameters)) + { + throw new IllegalArgumentException(params.toString()); + } + + _store = new CollectionStore(((X509CollectionStoreParameters)params).getCollection()); + } + + public Collection engineGetMatches(Selector selector) + { + return _store.getMatches(selector); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCertCollection.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCertCollection.java new file mode 100644 index 00000000..db88f316 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCertCollection.java @@ -0,0 +1,34 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; + +import org.bouncycastle.util.CollectionStore; +import org.bouncycastle.util.Selector; +import org.bouncycastle.x509.X509CollectionStoreParameters; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; + +public class X509StoreCertCollection + extends X509StoreSpi +{ + private CollectionStore _store; + + public X509StoreCertCollection() + { + } + + public void engineInit(X509StoreParameters params) + { + if (!(params instanceof X509CollectionStoreParameters)) + { + throw new IllegalArgumentException(params.toString()); + } + + _store = new CollectionStore(((X509CollectionStoreParameters)params).getCollection()); + } + + public Collection engineGetMatches(Selector selector) + { + return _store.getMatches(selector); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCertPairCollection.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCertPairCollection.java new file mode 100644 index 00000000..e67c25ba --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreCertPairCollection.java @@ -0,0 +1,64 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; + +import org.bouncycastle.util.CollectionStore; +import org.bouncycastle.util.Selector; +import org.bouncycastle.x509.X509CollectionStoreParameters; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; + +/** + * This class is a collection based Bouncy Castle + * {@link org.bouncycastle.x509.X509Store} SPI implementation for certificate + * pairs. + * + * @see org.bouncycastle.x509.X509Store + * @see org.bouncycastle.x509.X509CertificatePair + */ +public class X509StoreCertPairCollection extends X509StoreSpi +{ + + private CollectionStore _store; + + public X509StoreCertPairCollection() + { + } + + /** + * Initializes this store. + * + * @param params The {@link X509CollectionStoreParameters}s for this store. + * @throws IllegalArgumentException if <code>params</code> is no instance of + * <code>X509CollectionStoreParameters</code>. + */ + public void engineInit(X509StoreParameters params) + { + if (!(params instanceof X509CollectionStoreParameters)) + { + throw new IllegalArgumentException( + "Initialization parameters must be an instance of " + + X509CollectionStoreParameters.class.getName() + + "."); + } + + _store = new CollectionStore(((X509CollectionStoreParameters)params) + .getCollection()); + } + + /** + * Returns a colelction of certificate pairs which match the given + * <code>selector</code>. + * <p/> + * The returned collection contains + * {@link org.bouncycastle.x509.X509CertificatePair}s. The selector must be + * a {@link org.bouncycastle.x509.X509CertPairStoreSelector} to select + * certificate pairs. + * + * @return A collection with matching certificate pairs. + */ + public Collection engineGetMatches(Selector selector) + { + return _store.getMatches(selector); + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPAttrCerts.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPAttrCerts.java new file mode 100644 index 00000000..96baa129 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPAttrCerts.java @@ -0,0 +1,79 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.jce.X509LDAPCertStoreParameters; +import org.bouncycastle.util.Selector; +import org.bouncycastle.util.StoreException; +import org.bouncycastle.x509.X509AttributeCertStoreSelector; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; +import org.bouncycastle.x509.util.LDAPStoreHelper; + +/** + * A SPI implementation of Bouncy Castle <code>X509Store</code> for getting + * attribute certificates from an LDAP directory. + * + * @see org.bouncycastle.x509.X509Store + */ +public class X509StoreLDAPAttrCerts extends X509StoreSpi +{ + + private LDAPStoreHelper helper; + + public X509StoreLDAPAttrCerts() + { + } + + /** + * Initializes this LDAP attribute cert store implementation. + * + * @param parameters <code>X509LDAPCertStoreParameters</code>. + * @throws IllegalArgumentException if <code>params</code> is not an instance of + * <code>X509LDAPCertStoreParameters</code>. + */ + public void engineInit(X509StoreParameters parameters) + { + if (!(parameters instanceof X509LDAPCertStoreParameters)) + { + throw new IllegalArgumentException( + "Initialization parameters must be an instance of " + + X509LDAPCertStoreParameters.class.getName() + "."); + } + helper = new LDAPStoreHelper((X509LDAPCertStoreParameters)parameters); + } + + /** + * Returns a collection of matching attribute certificates from the LDAP + * location. + * <p/> + * The selector must be a of type + * <code>X509AttributeCertStoreSelector</code>. If it is not an empty + * collection is returned. + * <p/> + * <p/> + * The subject and the serial number should be reasonable criterias for a + * selector. + * + * @param selector The selector to use for finding. + * @return A collection with the matches. + * @throws StoreException if an exception occurs while searching. + */ + public Collection engineGetMatches(Selector selector) throws StoreException + { + if (!(selector instanceof X509AttributeCertStoreSelector)) + { + return Collections.EMPTY_SET; + } + X509AttributeCertStoreSelector xselector = (X509AttributeCertStoreSelector)selector; + Set set = new HashSet(); + set.addAll(helper.getAACertificates(xselector)); + set.addAll(helper.getAttributeCertificateAttributes(xselector)); + set.addAll(helper.getAttributeDescriptorCertificates(xselector)); + return set; + } + +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCRLs.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCRLs.java new file mode 100644 index 00000000..5f4dfb48 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCRLs.java @@ -0,0 +1,87 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.jce.X509LDAPCertStoreParameters; +import org.bouncycastle.util.Selector; +import org.bouncycastle.util.StoreException; +import org.bouncycastle.x509.X509CRLStoreSelector; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; +import org.bouncycastle.x509.util.LDAPStoreHelper; + +/** + * A SPI implementation of Bouncy Castle <code>X509Store</code> for getting + * certificate revocation lists from an LDAP directory. + * + * @see org.bouncycastle.x509.X509Store + */ +public class X509StoreLDAPCRLs extends X509StoreSpi +{ + + private LDAPStoreHelper helper; + + public X509StoreLDAPCRLs() + { + } + + /** + * Initializes this LDAP CRL store implementation. + * + * @param params <code>X509LDAPCertStoreParameters</code>. + * @throws IllegalArgumentException if <code>params</code> is not an instance of + * <code>X509LDAPCertStoreParameters</code>. + */ + public void engineInit(X509StoreParameters params) + { + if (!(params instanceof X509LDAPCertStoreParameters)) + { + throw new IllegalArgumentException( + "Initialization parameters must be an instance of " + + X509LDAPCertStoreParameters.class.getName() + "."); + } + helper = new LDAPStoreHelper((X509LDAPCertStoreParameters)params); + } + + /** + * Returns a collection of matching CRLs from the LDAP location. + * <p/> + * The selector must be a of type <code>X509CRLStoreSelector</code>. If + * it is not an empty collection is returned. + * <p/> + * The issuer should be a reasonable criteria for a selector. + * + * @param selector The selector to use for finding. + * @return A collection with the matches. + * @throws StoreException if an exception occurs while searching. + */ + public Collection engineGetMatches(Selector selector) throws StoreException + { + if (!(selector instanceof X509CRLStoreSelector)) + { + return Collections.EMPTY_SET; + } + X509CRLStoreSelector xselector = (X509CRLStoreSelector)selector; + Set set = new HashSet(); + // test only delta CRLs should be selected + if (xselector.isDeltaCRLIndicatorEnabled()) + { + set.addAll(helper.getDeltaCertificateRevocationLists(xselector)); + } + // nothing specified + else + { + set.addAll(helper.getDeltaCertificateRevocationLists(xselector)); + set.addAll(helper.getAttributeAuthorityRevocationLists(xselector)); + set + .addAll(helper + .getAttributeCertificateRevocationLists(xselector)); + set.addAll(helper.getAuthorityRevocationLists(xselector)); + set.addAll(helper.getCertificateRevocationLists(xselector)); + } + return set; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCertPairs.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCertPairs.java new file mode 100644 index 00000000..f5687d8c --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCertPairs.java @@ -0,0 +1,75 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.jce.X509LDAPCertStoreParameters; +import org.bouncycastle.util.Selector; +import org.bouncycastle.util.StoreException; +import org.bouncycastle.x509.X509CertPairStoreSelector; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; +import org.bouncycastle.x509.util.LDAPStoreHelper; + +/** + * A SPI implementation of Bouncy Castle <code>X509Store</code> for getting + * cross certificates pairs from an LDAP directory. + * + * @see org.bouncycastle.x509.X509Store + */ +public class X509StoreLDAPCertPairs extends X509StoreSpi +{ + + private LDAPStoreHelper helper; + + public X509StoreLDAPCertPairs() + { + } + + /** + * Initializes this LDAP cross certificate pair store implementation. + * + * @param parameters <code>X509LDAPCertStoreParameters</code>. + * @throws IllegalArgumentException if <code>params</code> is not an instance of + * <code>X509LDAPCertStoreParameters</code>. + */ + public void engineInit(X509StoreParameters parameters) + { + if (!(parameters instanceof X509LDAPCertStoreParameters)) + { + throw new IllegalArgumentException( + "Initialization parameters must be an instance of " + + X509LDAPCertStoreParameters.class.getName() + "."); + } + helper = new LDAPStoreHelper((X509LDAPCertStoreParameters)parameters); + } + + /** + * Returns a collection of matching cross certificate pairs from the LDAP + * location. + * <p/> + * The selector must be a of type <code>X509CertPairStoreSelector</code>. + * If it is not an empty collection is returned. + * <p/> + * <p/> + * The subject should be a reasonable criteria for a selector. + * + * @param selector The selector to use for finding. + * @return A collection with the matches. + * @throws StoreException if an exception occurs while searching. + */ + public Collection engineGetMatches(Selector selector) throws StoreException + { + if (!(selector instanceof X509CertPairStoreSelector)) + { + return Collections.EMPTY_SET; + } + X509CertPairStoreSelector xselector = (X509CertPairStoreSelector)selector; + Set set = new HashSet(); + set.addAll(helper.getCrossCertificatePairs(xselector)); + return set; + } + +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCerts.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCerts.java new file mode 100644 index 00000000..dd811a17 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509StoreLDAPCerts.java @@ -0,0 +1,128 @@ +package org.bouncycastle.jce.provider; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.bouncycastle.jce.X509LDAPCertStoreParameters; +import org.bouncycastle.util.Selector; +import org.bouncycastle.util.StoreException; +import org.bouncycastle.x509.X509CertPairStoreSelector; +import org.bouncycastle.x509.X509CertStoreSelector; +import org.bouncycastle.x509.X509CertificatePair; +import org.bouncycastle.x509.X509StoreParameters; +import org.bouncycastle.x509.X509StoreSpi; +import org.bouncycastle.x509.util.LDAPStoreHelper; + +/** + * A SPI implementation of Bouncy Castle <code>X509Store</code> for getting + * certificates form a LDAP directory. + * + * @see org.bouncycastle.x509.X509Store + */ +public class X509StoreLDAPCerts + extends X509StoreSpi +{ + + private LDAPStoreHelper helper; + + public X509StoreLDAPCerts() + { + } + + /** + * Initializes this LDAP cert store implementation. + * + * @param params <code>X509LDAPCertStoreParameters</code>. + * @throws IllegalArgumentException if <code>params</code> is not an instance of + * <code>X509LDAPCertStoreParameters</code>. + */ + public void engineInit(X509StoreParameters params) + { + if (!(params instanceof X509LDAPCertStoreParameters)) + { + throw new IllegalArgumentException( + "Initialization parameters must be an instance of " + + X509LDAPCertStoreParameters.class.getName() + "."); + } + helper = new LDAPStoreHelper((X509LDAPCertStoreParameters)params); + } + + /** + * Returns a collection of matching certificates from the LDAP location. + * <p/> + * The selector must be a of type <code>X509CertStoreSelector</code>. If + * it is not an empty collection is returned. + * <p/> + * The implementation searches only for CA certificates, if the method + * {@link java.security.cert.X509CertSelector#getBasicConstraints()} is + * greater or equal to 0. If it is -2 only end certificates are searched. + * <p/> + * The subject and the serial number for end certificates should be + * reasonable criterias for a selector. + * + * @param selector The selector to use for finding. + * @return A collection with the matches. + * @throws StoreException if an exception occurs while searching. + */ + public Collection engineGetMatches(Selector selector) throws StoreException + { + if (!(selector instanceof X509CertStoreSelector)) + { + return Collections.EMPTY_SET; + } + X509CertStoreSelector xselector = (X509CertStoreSelector)selector; + Set set = new HashSet(); + // test if only CA certificates should be selected + if (xselector.getBasicConstraints() > 0) + { + set.addAll(helper.getCACertificates(xselector)); + set.addAll(getCertificatesFromCrossCertificatePairs(xselector)); + } + // only end certificates should be selected + else if (xselector.getBasicConstraints() == -2) + { + set.addAll(helper.getUserCertificates(xselector)); + } + // nothing specified + else + { + set.addAll(helper.getUserCertificates(xselector)); + set.addAll(helper.getCACertificates(xselector)); + set.addAll(getCertificatesFromCrossCertificatePairs(xselector)); + } + return set; + } + + private Collection getCertificatesFromCrossCertificatePairs( + X509CertStoreSelector xselector) throws StoreException + { + Set set = new HashSet(); + X509CertPairStoreSelector ps = new X509CertPairStoreSelector(); + + ps.setForwardSelector(xselector); + ps.setReverseSelector(new X509CertStoreSelector()); + + Set crossCerts = new HashSet(helper.getCrossCertificatePairs(ps)); + Set forward = new HashSet(); + Set reverse = new HashSet(); + Iterator it = crossCerts.iterator(); + while (it.hasNext()) + { + X509CertificatePair pair = (X509CertificatePair)it.next(); + if (pair.getForward() != null) + { + forward.add(pair.getForward()); + } + if (pair.getReverse() != null) + { + reverse.add(pair.getReverse()); + } + } + set.addAll(forward); + set.addAll(reverse); + return set; + } +} |