diff options
Diffstat (limited to 'prov/src/main/jdk1.4/org/spongycastle')
25 files changed, 9897 insertions, 0 deletions
diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java new file mode 100644 index 00000000..5c91202b --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java @@ -0,0 +1,385 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROutputStream; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.sec.ECPrivateKeyStructure; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class BCECPrivateKey + implements ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder +{ + private String algorithm = "EC"; + private boolean withCompression; + + private transient BigInteger d; + private transient ECParameterSpec ecSpec; + private transient ProviderConfiguration configuration; + private transient DERBitString publicKey; + + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCECPrivateKey() + { + } + + BCECPrivateKey( + ECPrivateKey key, + ProviderConfiguration configuration) + { + this.d = key.getD(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParameters(); + this.configuration = configuration; + } + + public BCECPrivateKey( + String algorithm, + ECPrivateKeySpec spec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.d = spec.getD(); + this.ecSpec = spec.getParams(); + this.configuration = configuration; + } + + public BCECPrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCECPublicKey pubKey, + ECParameterSpec spec, + ProviderConfiguration configuration) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + this.configuration = configuration; + + if (spec == null) + { + this.ecSpec = new ECParameterSpec( + dp.getCurve(), + dp.getG(), + dp.getN(), + dp.getH(), + dp.getSeed()); + } + else + { + this.ecSpec = spec; + } + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCECPrivateKey( + String algorithm, + ECPrivateKeyParameters params, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.d = params.getD(); + this.ecSpec = null; + this.configuration = configuration; + } + + public BCECPrivateKey( + String algorithm, + BCECPrivateKey key) + { + this.algorithm = algorithm; + this.d = key.d; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.publicKey = key.publicKey; + this.attrCarrier = key.attrCarrier; + this.configuration = key.configuration; + } + + BCECPrivateKey( + PrivateKeyInfo info, + ProviderConfiguration configuration) + { + this.configuration = configuration; + + populateFromPrivKeyInfo(info); + } + + BCECPrivateKey( + String algorithm, + PrivateKeyInfo info, + ProviderConfiguration configuration) + { + this.configuration = configuration; + populateFromPrivKeyInfo(info); + this.algorithm = algorithm; + } + + private void populateFromPrivKeyInfo(PrivateKeyInfo info) + { + X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters()); + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + + ecSpec = new ECNamedCurveParameterSpec( + ECUtil.getCurveName(oid), + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + ecSpec = new ECParameterSpec(ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + } + + if (info.getPrivateKey() instanceof ASN1Integer) + { + ASN1Integer derD = ASN1Integer.getInstance(info.getPrivateKey()); + + this.d = derD.getValue(); + } + else + { + ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)info.getPrivateKey()); + + 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() + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + X962Parameters params = null; + + if (ecSpec instanceof ECNamedCurveParameterSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveParameterSpec)ecSpec).getName()); + + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecSpec; + + ECPoint pG = p.getG().normalize(); + ECPoint g = pG.getCurve().createPoint(pG.getAffineXCoord().toBigInteger(), pG.getAffineYCoord().toBigInteger()); + + X9ECParameters ecP = new X9ECParameters( + p.getCurve(), + g, + p.getN(), + p.getH(), + p.getSeed()); + + params = new X962Parameters(ecP); + } + + PrivateKeyInfo info; + ECPrivateKeyStructure keyStructure; + + if (publicKey != null) + { + keyStructure = new ECPrivateKeyStructure(this.getD(), publicKey, params); + } + else + { + keyStructure = new ECPrivateKeyStructure(this.getD(), params); + } + + try + { + if (algorithm.equals("ECGOST3410")) + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), keyStructure); + } + else + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure); + } + + return KeyUtil.getEncodedPrivateKeyInfo(info); + } + catch (IOException e) + { + return null; + } + } + + public ECParameterSpec getParams() + { + return (ECParameterSpec)ecSpec; + } + + public ECParameterSpec getParameters() + { + return (ECParameterSpec)ecSpec; + } + + 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)); + } + + ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return ecSpec; + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECPrivateKey)) + { + return false; + } + + BCECPrivateKey other = (BCECPrivateKey)o; + + return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getD().hashCode() ^ engineGetSpec().hashCode(); + } + + private DERBitString getPublicKeyDetails(BCECPublicKey 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 + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.configuration = BouncyCastleProvider.CONFIGURATION; + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java new file mode 100644 index 00000000..8f704ddc --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java @@ -0,0 +1,376 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class BCECPublicKey + implements ECPublicKey, ECPointEncoder +{ + private String algorithm = "EC"; + private boolean withCompression; + + private transient org.spongycastle.math.ec.ECPoint q; + private transient ECParameterSpec ecSpec; + private transient ProviderConfiguration configuration; + + public BCECPublicKey( + String algorithm, + BCECPublicKey key + ) + { + this.algorithm = algorithm; + this.q = key.q; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.configuration = key.configuration; + } + + public BCECPublicKey( + String algorithm, + ECPublicKeySpec spec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.q = spec.getQ(); + this.configuration = configuration; + + if (spec.getParams() != null) + { + this.ecSpec = spec.getParams(); + } + else + { + if (q.getCurve() == null) + { + org.spongycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); + } + this.ecSpec = null; + } + } + + public BCECPublicKey( + String algorithm, + ECPublicKeyParameters params, + ECParameterSpec spec, + ProviderConfiguration configuration) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + this.configuration = configuration; + + if (spec == null) + { + this.ecSpec = new ECParameterSpec( + dp.getCurve(), + dp.getG(), + dp.getN(), + dp.getH(), + dp.getSeed()); + } + else + { + this.ecSpec = spec; + } + } + + public BCECPublicKey( + String algorithm, + ECPublicKeyParameters params, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.q = params.getQ(); + this.ecSpec = null; + this.configuration = configuration; + } + + BCECPublicKey( + ECPublicKey key, + ProviderConfiguration configuration) + { + this.q = key.getQ(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParameters(); + this.configuration = configuration; + } + + BCECPublicKey( + String algorithm, + ECPoint q, + ECParameterSpec ecSpec, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.q = q; + this.ecSpec = ecSpec; + this.configuration = configuration; + } + + BCECPublicKey( + SubjectPublicKeyInfo info, + ProviderConfiguration configuration) + { + this.configuration = configuration; + + populateFromPubKeyInfo(info); + } + + BCECPublicKey( + String algorithm, + SubjectPublicKeyInfo info, + ProviderConfiguration configuration) + { + this.configuration = configuration; + populateFromPubKeyInfo(info); + this.algorithm = algorithm; + } + + private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) + { + X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters()); + ECCurve curve; + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + + ecSpec = new ECNamedCurveParameterSpec( + ECUtil.getCurveName(oid), + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + curve = ((ECParameterSpec)ecSpec).getCurve(); + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve(); + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + ecSpec = new ECParameterSpec( + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + curve = ((ECParameterSpec)ecSpec).getCurve(); + } + + 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() + { + SubjectPublicKeyInfo info; + + X962Parameters params = null; + if (ecSpec instanceof ECNamedCurveParameterSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveParameterSpec)ecSpec).getName()); + + if (curveOid == null) + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveParameterSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecSpec; + + ECCurve curve = p.getG().getCurve(); + ECPoint generator = curve.createPoint(p.getG().getX().toBigInteger(), p.getG().getY().toBigInteger(), withCompression); + + X9ECParameters ecP = new X9ECParameters( + p.getCurve(), generator, p.getN(), p.getH(), p.getSeed()); + + params = new X962Parameters(ecP); + } + + ECCurve curve = this.engineGetQ().getCurve(); + ECPoint point = curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression); + ASN1OctetString p = ASN1OctetString.getInstance(new X9ECPoint(point)); + + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); + + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + + public ECParameterSpec getParams() + { + return (ECParameterSpec)ecSpec; + } + + public ECParameterSpec getParameters() + { + return (ECParameterSpec)ecSpec; + } + + public org.spongycastle.math.ec.ECPoint getQ() + { + if (ecSpec == null) + { + if (q instanceof org.spongycastle.math.ec.ECPoint.Fp) + { + return new org.spongycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); + } + else + { + return new org.spongycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); + } + } + + return q; + } + + public org.spongycastle.math.ec.ECPoint engineGetQ() + { + return q; + } + + 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.getQ().getX().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.getQ().getY().toBigInteger().toString(16)).append(nl); + + return buf.toString(); + + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return (ECParameterSpec)ecSpec; + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECPublicKey)) + { + return false; + } + + BCECPublicKey other = (BCECPublicKey)o; + + return getQ().equals(other.getQ()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getQ().hashCode() ^ engineGetSpec().hashCode(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.configuration = BouncyCastleProvider.CONFIGURATION; + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java new file mode 100644 index 00000000..27c2eed1 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java @@ -0,0 +1,317 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; + +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DerivationFunction; +import org.spongycastle.crypto.agreement.ECDHBasicAgreement; +import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; +import org.spongycastle.crypto.agreement.ECMQVBasicAgreement; +import org.spongycastle.crypto.agreement.kdf.DHKDFParameters; +import org.spongycastle.crypto.agreement.kdf.ECDHKEKGenerator; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.MQVPrivateParameters; +import org.spongycastle.crypto.params.MQVPublicParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.MQVPrivateKey; +import org.spongycastle.jce.interfaces.MQVPublicKey; +import org.spongycastle.util.Integers; + +/** + * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363 + * both the simple one, and the simple one with cofactors are supported. + * + * Also, MQV key agreement per SEC-1 + */ +public class KeyAgreementSpi + extends javax.crypto.KeyAgreementSpi +{ + private static final X9IntegerConverter converter = new X9IntegerConverter(); + private static final Hashtable algorithms = new Hashtable(); + + static + { + Integer i128 = Integers.valueOf(128); + Integer i192 = Integers.valueOf(192); + Integer i256 = Integers.valueOf(256); + + algorithms.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128); + algorithms.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192); + algorithms.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256); + algorithms.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128); + algorithms.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192); + algorithms.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256); + algorithms.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192); + } + + private String kaAlgorithm; + private BigInteger result; + private ECDomainParameters parameters; + private BasicAgreement agreement; + private DerivationFunction kdf; + + private byte[] bigIntToBytes( + BigInteger r) + { + return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getX())); + } + + protected KeyAgreementSpi( + String kaAlgorithm, + BasicAgreement agreement, + DerivationFunction kdf) + { + this.kaAlgorithm = kaAlgorithm; + this.agreement = agreement; + this.kdf = kdf; + } + + protected Key engineDoPhase( + Key key, + boolean lastPhase) + throws InvalidKeyException, IllegalStateException + { + if (parameters == null) + { + throw new IllegalStateException(kaAlgorithm + " not initialised."); + } + + if (!lastPhase) + { + throw new IllegalStateException(kaAlgorithm + " can only be between two parties."); + } + + CipherParameters pubKey; + if (agreement instanceof ECMQVBasicAgreement) + { + if (!(key instanceof MQVPublicKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(MQVPublicKey.class) + " for doPhase"); + } + + MQVPublicKey mqvPubKey = (MQVPublicKey)key; + ECPublicKeyParameters staticKey = (ECPublicKeyParameters) + ECUtil.generatePublicKeyParameter(mqvPubKey.getStaticKey()); + ECPublicKeyParameters ephemKey = (ECPublicKeyParameters) + ECUtil.generatePublicKeyParameter(mqvPubKey.getEphemeralKey()); + + pubKey = new MQVPublicParameters(staticKey, ephemKey); + + // TODO Validate that all the keys are using the same parameters? + } + else + { + if (!(key instanceof ECPublicKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(ECPublicKey.class) + " for doPhase"); + } + + pubKey = ECUtil.generatePublicKeyParameter((PublicKey)key); + + // TODO Validate that all the keys are using the same parameters? + } + + result = agreement.calculateAgreement(pubKey); + + return null; + } + + protected byte[] engineGenerateSecret() + throws IllegalStateException + { + if (kdf != null) + { + throw new UnsupportedOperationException( + "KDF can only be used when algorithm is known"); + } + + return bigIntToBytes(result); + } + + protected int engineGenerateSecret( + byte[] sharedSecret, + int offset) + throws IllegalStateException, ShortBufferException + { + byte[] secret = engineGenerateSecret(); + + if (sharedSecret.length - offset < secret.length) + { + throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes"); + } + + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + + return secret.length; + } + + protected SecretKey engineGenerateSecret( + String algorithm) + throws NoSuchAlgorithmException + { + byte[] secret = bigIntToBytes(result); + + if (kdf != null) + { + if (!algorithms.containsKey(algorithm)) + { + throw new NoSuchAlgorithmException("unknown algorithm encountered: " + algorithm); + } + + int keySize = ((Integer)algorithms.get(algorithm)).intValue(); + + DHKDFParameters params = new DHKDFParameters(new ASN1ObjectIdentifier(algorithm), keySize, secret); + + byte[] keyBytes = new byte[keySize / 8]; + kdf.init(params); + kdf.generateBytes(keyBytes, 0, keyBytes.length); + secret = keyBytes; + } + else + { + // TODO Should we be ensuring the key is the right length? + } + + return new SecretKeySpec(secret, algorithm); + } + + protected void engineInit( + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + initFromKey(key); + } + + protected void engineInit( + Key key, + SecureRandom random) + throws InvalidKeyException + { + initFromKey(key); + } + + private void initFromKey(Key key) + throws InvalidKeyException + { + if (agreement instanceof ECMQVBasicAgreement) + { + if (!(key instanceof MQVPrivateKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(MQVPrivateKey.class) + " for initialisation"); + } + + MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key; + ECPrivateKeyParameters staticPrivKey = (ECPrivateKeyParameters) + ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey()); + ECPrivateKeyParameters ephemPrivKey = (ECPrivateKeyParameters) + ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey()); + + ECPublicKeyParameters ephemPubKey = null; + if (mqvPrivKey.getEphemeralPublicKey() != null) + { + ephemPubKey = (ECPublicKeyParameters) + ECUtil.generatePublicKeyParameter(mqvPrivKey.getEphemeralPublicKey()); + } + + MQVPrivateParameters localParams = new MQVPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey); + this.parameters = staticPrivKey.getParameters(); + + // TODO Validate that all the keys are using the same parameters? + + agreement.init(localParams); + } + else + { + if (!(key instanceof ECPrivateKey)) + { + throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + + getSimpleName(ECPrivateKey.class) + " for initialisation"); + } + + ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key); + this.parameters = privKey.getParameters(); + + agreement.init(privKey); + } + } + + private static String getSimpleName(Class clazz) + { + String fullName = clazz.getName(); + + return fullName.substring(fullName.lastIndexOf('.') + 1); + } + + public static class DH + extends KeyAgreementSpi + { + public DH() + { + super("ECDH", new ECDHBasicAgreement(), null); + } + } + + public static class DHC + extends KeyAgreementSpi + { + public DHC() + { + super("ECDHC", new ECDHCBasicAgreement(), null); + } + } + + public static class MQV + extends KeyAgreementSpi + { + public MQV() + { + super("ECMQV", new ECMQVBasicAgreement(), null); + } + } + + public static class DHwithSHA1KDF + extends KeyAgreementSpi + { + public DHwithSHA1KDF() + { + super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); + } + } + + public static class MQVwithSHA1KDF + extends KeyAgreementSpi + { + public MQVwithSHA1KDF() + { + super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest())); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java new file mode 100644 index 00000000..76c11e0b --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyFactorySpi.java @@ -0,0 +1,200 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi + implements AsymmetricKeyInfoConverter +{ + String algorithm; + ProviderConfiguration configuration; + + KeyFactorySpi( + String algorithm, + ProviderConfiguration configuration) + { + this.algorithm = algorithm; + this.configuration = configuration; + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + if (key instanceof ECPublicKey) + { + return new BCECPublicKey((ECPublicKey)key, configuration); + } + else if (key instanceof ECPrivateKey) + { + return new BCECPrivateKey((ECPrivateKey)key, configuration); + } + + throw new InvalidKeyException("key type unknown"); + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPublicKeySpec(k.getQ(), k.getParameters()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPublicKeySpec(k.getQ(), implicitSpec); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getD(), k.getParameters()); + } + else + { + ECParameterSpec implicitSpec = configuration.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getD(), implicitSpec); + } + } + return super.engineGetKeySpec(key, spec); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPrivateKeySpec) + { + return new BCECPrivateKey(algorithm, (ECPrivateKeySpec)keySpec, configuration); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPublicKeySpec) + { + return new BCECPublicKey(algorithm, (ECPublicKeySpec)keySpec, configuration); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + return new BCECPrivateKey(algorithm, keyInfo, configuration); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + return new BCECPublicKey(algorithm, keyInfo, configuration); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public static class EC + extends KeyFactorySpi + { + public EC() + { + super("EC", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDSA + extends KeyFactorySpi + { + public ECDSA() + { + super("ECDSA", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECGOST3410 + extends KeyFactorySpi + { + public ECGOST3410() + { + super("ECGOST3410", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDH + extends KeyFactorySpi + { + public ECDH() + { + super("ECDH", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDHC + extends KeyFactorySpi + { + public ECDHC() + { + super("ECDHC", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECMQV + extends KeyFactorySpi + { + public ECMQV() + { + super("ECMQV", BouncyCastleProvider.CONFIGURATION); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..8d74dd59 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java @@ -0,0 +1,259 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.asn1.x9.X962NamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.util.Integers; + +public abstract class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + public KeyPairGeneratorSpi(String algorithmName) + { + super(algorithmName); + } + + public static class EC + extends KeyPairGeneratorSpi + { + ECKeyGenerationParameters param; + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + ECParameterSpec ecParams = null; + int strength = 239; + int certainty = 50; + SecureRandom random = new SecureRandom(); + boolean initialised = false; + String algorithm; + ProviderConfiguration configuration; + + static private Hashtable ecParameters; + + static { + ecParameters = new Hashtable(); + + ecParameters.put(Integers.valueOf(192), + ECNamedCurveTable.getParameterSpec("prime192v1")); + ecParameters.put(Integers.valueOf(239), + ECNamedCurveTable.getParameterSpec("prime239v1")); + ecParameters.put(Integers.valueOf(256), + ECNamedCurveTable.getParameterSpec("prime256v1")); + } + + public EC() + { + super("EC"); + this.algorithm = "EC"; + this.configuration = BouncyCastleProvider.CONFIGURATION; + } + + public EC( + String algorithm, + ProviderConfiguration configuration) + { + super(algorithm); + this.algorithm = algorithm; + this.configuration = configuration; + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + this.ecParams = (ECParameterSpec)ecParameters.get(Integers.valueOf(strength)); + + if (ecParams != null) + { + param = new ECKeyGenerationParameters(new ECDomainParameters(ecParams.getCurve(), ecParams.getG(), ecParams.getN()), random); + + engine.init(param); + initialised = true; + } + else + { + throw new InvalidParameterException("unknown key size."); + } + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (params instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)params; + this.ecParams = (ECParameterSpec)params; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params instanceof ECNamedCurveGenParameterSpec) + { + String curveName; + + curveName = ((ECNamedCurveGenParameterSpec)params).getName(); + + X9ECParameters ecP = X962NamedCurves.getByName(curveName); + if (ecP == null) + { + ecP = SECNamedCurves.getByName(curveName); + if (ecP == null) + { + ecP = NISTNamedCurves.getByName(curveName); + } + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.getByName(curveName); + } + if (ecP == null) + { + // See if it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug) + try + { + ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(curveName); + ecP = X962NamedCurves.getByOID(oid); + if (ecP == null) + { + ecP = SECNamedCurves.getByOID(oid); + } + if (ecP == null) + { + ecP = NISTNamedCurves.getByOID(oid); + } + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.getByOID(oid); + } + if (ecP == null) + { + throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName); + } + } + catch (IllegalArgumentException ex) + { + throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + } + } + } + + this.ecParams = new ECNamedCurveParameterSpec( + curveName, + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + null); // ecP.getSeed()); Work-around JDK bug -- it won't look up named curves properly if seed is present + + param = new ECKeyGenerationParameters(new ECDomainParameters(ecParams.getCurve(), ecParams.getG(), ecParams.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params == null && configuration.getEcImplicitlyCa() != null) + { + ECParameterSpec p = configuration.getEcImplicitlyCa(); + this.ecParams = (ECParameterSpec)params; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params == null && configuration.getEcImplicitlyCa() == null) + { + throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set"); + } + else + { + throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec"); + } + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + throw new IllegalStateException("EC Key Pair Generator not initialised"); + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate(); + + if (ecParams == null) + { + return new KeyPair(new BCECPublicKey(algorithm, pub, configuration), + new BCECPrivateKey(algorithm, priv, configuration)); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecParams; + BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration); + + return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration)); + } + } + } + + public static class ECDSA + extends EC + { + public ECDSA() + { + super("ECDSA", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDH + extends EC + { + public ECDH() + { + super("ECDH", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECDHC + extends EC + { + public ECDHC() + { + super("ECDHC", BouncyCastleProvider.CONFIGURATION); + } + } + + public static class ECMQV + extends EC + { + public ECMQV() + { + super("ECMQV", BouncyCastleProvider.CONFIGURATION); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java new file mode 100644 index 00000000..9d61a76b --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java @@ -0,0 +1,355 @@ +package org.spongycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.NullDigest; +import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.ECDSASigner; +import org.spongycastle.crypto.signers.ECNRSigner; +import org.spongycastle.jcajce.provider.asymmetric.util.DSABase; +import org.spongycastle.jcajce.provider.asymmetric.util.DSAEncoder; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class SignatureSpi + extends DSABase +{ + SignatureSpi(Digest digest, DSA signer, DSAEncoder encoder) + { + super("ECDSA", digest, signer, encoder); + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + try + { + byte[] bytes = publicKey.getEncoded(); + + publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in ECDSA based signer"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("can't recognise key type in ECDSA based signer"); + } + } + + digest.reset(); + + signer.init(false, param); + } + + protected void doEngineInitSign( + PrivateKey privateKey, + SecureRandom random) + throws InvalidKeyException + { + CipherParameters param; + + if (privateKey instanceof ECKey) + { + param = ECUtil.generatePrivateKeyParameter(privateKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in ECDSA based signer"); + } + + digest.reset(); + + if (random != null) + { + signer.init(true, new ParametersWithRandom(param, random)); + } + else + { + signer.init(true, param); + } + } + + static public class ecDSA + extends SignatureSpi + { + public ecDSA() + { + super(new SHA1Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSAnone + extends SignatureSpi + { + public ecDSAnone() + { + super(new NullDigest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSA224 + extends SignatureSpi + { + public ecDSA224() + { + super(new SHA224Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSA256 + extends SignatureSpi + { + public ecDSA256() + { + super(new SHA256Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSA384 + extends SignatureSpi + { + public ecDSA384() + { + super(new SHA384Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSA512 + extends SignatureSpi + { + public ecDSA512() + { + super(new SHA512Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecDSARipeMD160 + extends SignatureSpi + { + public ecDSARipeMD160() + { + super(new RIPEMD160Digest(), new ECDSASigner(), new StdDSAEncoder()); + } + } + + static public class ecNR + extends SignatureSpi + { + public ecNR() + { + super(new SHA1Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR224 + extends SignatureSpi + { + public ecNR224() + { + super(new SHA224Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR256 + extends SignatureSpi + { + public ecNR256() + { + super(new SHA256Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR384 + extends SignatureSpi + { + public ecNR384() + { + super(new SHA384Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecNR512 + extends SignatureSpi + { + public ecNR512() + { + super(new SHA512Digest(), new ECNRSigner(), new StdDSAEncoder()); + } + } + + static public class ecCVCDSA + extends SignatureSpi + { + public ecCVCDSA() + { + super(new SHA1Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + + static public class ecCVCDSA224 + extends SignatureSpi + { + public ecCVCDSA224() + { + super(new SHA224Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + + static public class ecCVCDSA256 + extends SignatureSpi + { + public ecCVCDSA256() + { + super(new SHA256Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + + static public class ecCVCDSA384 + extends SignatureSpi + { + public ecCVCDSA384() + { + super(new SHA384Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + + static public class ecCVCDSA512 + extends SignatureSpi + { + public ecCVCDSA512() + { + super(new SHA512Digest(), new ECDSASigner(), new CVCDSAEncoder()); + } + } + + private static class StdDSAEncoder + implements DSAEncoder + { + public byte[] encode( + BigInteger r, + BigInteger s) + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new ASN1Integer(r)); + v.add(new ASN1Integer(s)); + + return new DERSequence(v).getEncoded(ASN1Encoding.DER); + } + + public BigInteger[] decode( + byte[] encoding) + throws IOException + { + ASN1Sequence s = (ASN1Sequence)ASN1Primitive.fromByteArray(encoding); + BigInteger[] sig = new BigInteger[2]; + + sig[0] = ((ASN1Integer)s.getObjectAt(0)).getValue(); + sig[1] = ((ASN1Integer)s.getObjectAt(1)).getValue(); + + return sig; + } + } + + private static class CVCDSAEncoder + implements DSAEncoder + { + public byte[] encode( + BigInteger r, + BigInteger s) + throws IOException + { + byte[] first = makeUnsigned(r); + byte[] second = makeUnsigned(s); + byte[] res; + + if (first.length > second.length) + { + res = new byte[first.length * 2]; + } + else + { + res = new byte[second.length * 2]; + } + + System.arraycopy(first, 0, res, res.length / 2 - first.length, first.length); + System.arraycopy(second, 0, res, res.length - second.length, second.length); + + return res; + } + + + private byte[] makeUnsigned(BigInteger val) + { + byte[] res = val.toByteArray(); + + if (res[0] == 0) + { + byte[] tmp = new byte[res.length - 1]; + + System.arraycopy(res, 1, tmp, 0, tmp.length); + + return tmp; + } + + return res; + } + + public BigInteger[] decode( + byte[] encoding) + throws IOException + { + BigInteger[] sig = new BigInteger[2]; + + byte[] first = new byte[encoding.length / 2]; + byte[] second = new byte[encoding.length / 2]; + + System.arraycopy(encoding, 0, first, 0, first.length); + System.arraycopy(encoding, first.length, second, 0, second.length); + + sig[0] = new BigInteger(1, first); + sig[1] = new BigInteger(1, second); + + return sig; + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java new file mode 100644 index 00000000..d322a172 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java @@ -0,0 +1,359 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROutputStream; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.sec.ECPrivateKeyStructure; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class BCECGOST3410PrivateKey + implements ECPrivateKey, PKCS12BagAttributeCarrier, ECPointEncoder +{ + private String algorithm = "ECGOST3410"; + private boolean withCompression; + + private transient BigInteger d; + private transient ECParameterSpec ecSpec; + private transient DERBitString publicKey; + private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); + + protected BCECGOST3410PrivateKey() + { + } + + BCECGOST3410PrivateKey( + ECPrivateKey key) + { + this.d = key.getD(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParameters(); + } + + public BCECGOST3410PrivateKey( + ECPrivateKeySpec spec) + { + this.d = spec.getD(); + this.ecSpec = spec.getParams(); + } + + public BCECGOST3410PrivateKey( + String algorithm, + ECPrivateKeyParameters params, + BCECGOST3410PublicKey pubKey, + ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.d = params.getD(); + + if (spec == null) + { + this.ecSpec = new ECParameterSpec( + dp.getCurve(), + dp.getG(), + dp.getN(), + dp.getH(), + dp.getSeed()); + } + else + { + this.ecSpec = spec; + } + + publicKey = getPublicKeyDetails(pubKey); + } + + public BCECGOST3410PrivateKey( + String algorithm, + ECPrivateKeyParameters params) + { + this.algorithm = algorithm; + this.d = params.getD(); + this.ecSpec = null; + } + + public BCECGOST3410PrivateKey( + String algorithm, + BCECGOST3410PrivateKey key) + { + this.algorithm = algorithm; + this.d = key.d; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.publicKey = key.publicKey; + this.attrCarrier = key.attrCarrier; + } + + BCECGOST3410PrivateKey( + PrivateKeyInfo info) + { + populateFromPrivKeyInfo(info); + } + + private void populateFromPrivKeyInfo(PrivateKeyInfo info) + { + X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters()); + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); + ECDomainParameters ecP = ECGOST3410NamedCurves.getByOID(oid); + + ecSpec = new ECNamedCurveParameterSpec( + ECUtil.getCurveName(oid), + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + ecSpec = new ECParameterSpec(ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + } + + if (info.getPrivateKey() instanceof ASN1Integer) + { + ASN1Integer derD = ASN1Integer.getInstance(info.getPrivateKey()); + + this.d = derD.getValue(); + } + else + { + ECPrivateKeyStructure ec = new ECPrivateKeyStructure((ASN1Sequence)info.getPrivateKey()); + + 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() + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + X962Parameters params = null; + + if (ecSpec instanceof ECNamedCurveParameterSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveParameterSpec)ecSpec).getName()); + + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecSpec; + + ECPoint pG = p.getG().normalize(); + ECPoint g = pG.getCurve().createPoint(pG.getAffineXCoord().toBigInteger(), pG.getAffineYCoord().toBigInteger()); + + X9ECParameters ecP = new X9ECParameters( + p.getCurve(), + g, + p.getN(), + p.getH(), + p.getSeed()); + + params = new X962Parameters(ecP); + } + + PrivateKeyInfo info; + ECPrivateKeyStructure keyStructure; + + if (publicKey != null) + { + keyStructure = new ECPrivateKeyStructure(this.getD(), publicKey, params); + } + else + { + keyStructure = new ECPrivateKeyStructure(this.getD(), params); + } + + try + { + if (algorithm.equals("ECGOST3410")) + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), keyStructure); + } + else + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure); + } + + return KeyUtil.getEncodedPrivateKeyInfo(info); + } + catch (IOException e) + { + return null; + } + } + + public ECParameterSpec getParams() + { + return (ECParameterSpec)ecSpec; + } + + public ECParameterSpec getParameters() + { + return (ECParameterSpec)ecSpec; + } + + 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)); + } + + ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return ecSpec; + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECGOST3410PrivateKey)) + { + return false; + } + + BCECGOST3410PrivateKey other = (BCECGOST3410PrivateKey)o; + + return getD().equals(other.getD()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getD().hashCode() ^ engineGetSpec().hashCode(); + } + + private DERBitString getPublicKeyDetails(BCECGOST3410PublicKey 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 + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPrivKeyInfo(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + + this.attrCarrier = new PKCS12BagAttributeCarrierImpl(); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java new file mode 100644 index 00000000..75e6e115 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java @@ -0,0 +1,454 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERBitString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X962Parameters; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.asn1.x9.X9ObjectIdentifiers; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jcajce.provider.asymmetric.util.KeyUtil; +import org.spongycastle.jce.ECGOST3410NamedCurveTable; +import org.spongycastle.jce.interfaces.ECPointEncoder; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +public class BCECGOST3410PublicKey + implements ECPublicKey, ECPointEncoder +{ + private String algorithm = "ECGOST3410"; + private boolean withCompression; + + private transient org.spongycastle.math.ec.ECPoint q; + private transient ECParameterSpec ecSpec; + private transient GOST3410PublicKeyAlgParameters gostParams; + + public BCECGOST3410PublicKey( + String algorithm, + BCECGOST3410PublicKey key) + { + this.algorithm = algorithm; + this.q = key.q; + this.ecSpec = key.ecSpec; + this.withCompression = key.withCompression; + this.gostParams = key.gostParams; + } + + public BCECGOST3410PublicKey( + ECPublicKeySpec spec) + { + this.q = spec.getQ(); + + if (spec.getParams() != null) + { + this.ecSpec = spec.getParams(); + } + else + { + if (q.getCurve() == null) + { + org.spongycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); + } + this.ecSpec = null; + } + } + + public BCECGOST3410PublicKey( + String algorithm, + ECPublicKeyParameters params, + ECParameterSpec spec) + { + ECDomainParameters dp = params.getParameters(); + + this.algorithm = algorithm; + this.q = params.getQ(); + + if (spec == null) + { + this.ecSpec = new ECParameterSpec( + dp.getCurve(), + dp.getG(), + dp.getN(), + dp.getH(), + dp.getSeed()); + } + else + { + this.ecSpec = spec; + } + } + + public BCECGOST3410PublicKey( + String algorithm, + ECPublicKeyParameters params) + { + this.algorithm = algorithm; + this.q = params.getQ(); + this.ecSpec = null; + } + + BCECGOST3410PublicKey( + ECPublicKey key) + { + this.q = key.getQ(); + this.algorithm = key.getAlgorithm(); + this.ecSpec = key.getParameters(); + } + + BCECGOST3410PublicKey( + String algorithm, + ECPoint q, + ECParameterSpec ecSpec) + { + this.algorithm = algorithm; + this.q = q; + this.ecSpec = ecSpec; + } + + BCECGOST3410PublicKey( + 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())); + + ecSpec = spec; + + this.q = spec.getCurve().createPoint(new BigInteger(1, x), new BigInteger(1, y), false); + } + else + { + X962Parameters params = X962Parameters.getInstance(info.getAlgorithmId().getParameters()); + ECCurve curve; + + if (params.isNamedCurve()) + { + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + + ecSpec = new ECNamedCurveParameterSpec( + ECUtil.getCurveName(oid), + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + curve = ((ECParameterSpec)ecSpec).getCurve(); + } + else if (params.isImplicitlyCA()) + { + ecSpec = null; + curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve(); + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + ecSpec = new ECParameterSpec( + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + curve = ((ECParameterSpec)ecSpec).getCurve(); + } + + 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() + { + SubjectPublicKeyInfo info; + + if (algorithm.equals("ECGOST3410")) + { + ASN1Encodable params = null; + if (gostParams != null) + { + params = gostParams; + } + else if (ecSpec instanceof ECNamedCurveParameterSpec) + { + params = new GOST3410PublicKeyAlgParameters( + ECGOST3410NamedCurves.getOID(((ECNamedCurveParameterSpec)ecSpec).getName()), + CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecSpec; + + ECCurve curve = p.getG().getCurve(); + ECPoint generator = curve.createPoint(p.getG().getX().toBigInteger(), p.getG().getY().toBigInteger(), withCompression); + + X9ECParameters ecP = new X9ECParameters( + p.getCurve(), generator, p.getN(), p.getH(), p.getSeed()); + + params = new X962Parameters(ecP); + } + + ECPoint qq = this.getQ(); + ECPoint point = qq.getCurve().createPoint(qq.getX().toBigInteger(), qq.getY().toBigInteger(), false); + ASN1OctetString p = ASN1OctetString.getInstance(new X9ECPoint(point)); + + BigInteger bX = this.q.getX().toBigInteger(); + BigInteger bY = this.q.getY().toBigInteger(); + byte[] encKey = new byte[64]; + + byte[] val = bX.toByteArray(); + + for (int i = 0; i != 32; i++) + { + encKey[i] = val[val.length - 1 - i]; + } + + val = bY.toByteArray(); + + for (int i = 0; i != 32; i++) + { + encKey[32 + i] = val[val.length - 1 - i]; + } + + try + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; + } + } + else + { + X962Parameters params = null; + if (ecSpec instanceof ECNamedCurveParameterSpec) + { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveParameterSpec)ecSpec).getName()); + + if (curveOid == null) + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveParameterSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecSpec; + + ECCurve curve = p.getG().getCurve(); + ECPoint generator = curve.createPoint(p.getG().getX().toBigInteger(), p.getG().getY().toBigInteger(), withCompression); + + X9ECParameters ecP = new X9ECParameters( + p.getCurve(), generator, p.getN(), p.getH(), p.getSeed()); + + params = new X962Parameters(ecP); + } + + ECCurve curve = this.engineGetQ().getCurve(); + ECPoint point = curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression); + ASN1OctetString p = ASN1OctetString.getInstance(new X9ECPoint(point)); + + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); + } + + return KeyUtil.getEncodedSubjectPublicKeyInfo(info); + } + + public ECParameterSpec getParams() + { + return (ECParameterSpec)ecSpec; + } + + public ECParameterSpec getParameters() + { + return (ECParameterSpec)ecSpec; + } + + public org.spongycastle.math.ec.ECPoint getQ() + { + if (ecSpec == null) + { + if (q instanceof org.spongycastle.math.ec.ECPoint.Fp) + { + return new org.spongycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); + } + else + { + return new org.spongycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); + } + } + + return q; + } + + public org.spongycastle.math.ec.ECPoint engineGetQ() + { + return q; + } + + 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.getQ().getX().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.getQ().getY().toBigInteger().toString(16)).append(nl); + + return buf.toString(); + + } + + public void setPointFormat(String style) + { + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + } + + ECParameterSpec engineGetSpec() + { + if (ecSpec != null) + { + return (ECParameterSpec)ecSpec; + } + + return BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + public boolean equals(Object o) + { + if (!(o instanceof BCECGOST3410PublicKey)) + { + return false; + } + + BCECGOST3410PublicKey other = (BCECGOST3410PublicKey)o; + + return getQ().equals(other.getQ()) && (engineGetSpec().equals(other.engineGetSpec())); + } + + public int hashCode() + { + return getQ().hashCode() ^ engineGetSpec().hashCode(); + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + populateFromPubKeyInfo(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(enc))); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java new file mode 100644 index 00000000..36847af2 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyFactorySpi.java @@ -0,0 +1,128 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; + +public class KeyFactorySpi + extends BaseKeyFactorySpi +{ + public KeyFactorySpi() + { + } + + protected KeySpec engineGetKeySpec( + Key key, + Class spec) + throws InvalidKeySpecException + { + if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPublicKeySpec.class) && key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPublicKeySpec(k.getQ(), k.getParameters()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPublicKeySpec(k.getQ(), implicitSpec); + } + } + else if (spec.isAssignableFrom(org.spongycastle.jce.spec.ECPrivateKeySpec.class) && key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + + if (k.getParams() != null) + { + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getD(), k.getParameters()); + } + else + { + ECParameterSpec implicitSpec = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new org.spongycastle.jce.spec.ECPrivateKeySpec(k.getD(), implicitSpec); + } + } + + return super.engineGetKeySpec(key, spec); + } + + protected Key engineTranslateKey( + Key key) + throws InvalidKeyException + { + throw new InvalidKeyException("key type unknown"); + } + + protected PrivateKey engineGeneratePrivate( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPrivateKeySpec) + { + return new BCECGOST3410PrivateKey((ECPrivateKeySpec)keySpec); + } + + return super.engineGeneratePrivate(keySpec); + } + + protected PublicKey engineGeneratePublic( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof ECPublicKeySpec) + { + return new BCECGOST3410PublicKey((ECPublicKeySpec)keySpec); + } + + return super.engineGeneratePublic(keySpec); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getPrivateKeyAlgorithm().getAlgorithm(); + + if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001)) + { + return new BCECGOST3410PrivateKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + ASN1ObjectIdentifier algOid = keyInfo.getAlgorithm().getAlgorithm(); + + if (algOid.equals(CryptoProObjectIdentifiers.gostR3410_2001)) + { + return new BCECGOST3410PublicKey(keyInfo); + } + else + { + throw new IOException("algorithm identifier " + algOid + " in key not recognised"); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java new file mode 100644 index 00000000..f950260e --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/KeyPairGeneratorSpi.java @@ -0,0 +1,144 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECParameterSpec; + +public class KeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + ECParameterSpec ecParams = null; + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + + String algorithm = "ECGOST3410"; + ECKeyGenerationParameters param; + int strength = 239; + SecureRandom random = null; + boolean initialised = false; + + public KeyPairGeneratorSpi() + { + super("ECGOST3410"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + + if (ecParams != null) + { + param = new ECKeyGenerationParameters(new ECDomainParameters(ecParams.getCurve(), ecParams.getG(), ecParams.getN()), random); + + engine.init(param); + initialised = true; + } + else + { + throw new InvalidParameterException("unknown key size."); + } + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (params instanceof ECParameterSpec) + { + ECParameterSpec p = (ECParameterSpec)params; + this.ecParams = p; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params instanceof ECNamedCurveGenParameterSpec) + { + String curveName; + + curveName = ((ECNamedCurveGenParameterSpec)params).getName(); + + ECDomainParameters ecP = ECGOST3410NamedCurves.getByName(curveName); + if (ecP == null) + { + throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + } + + this.ecParams = new ECNamedCurveParameterSpec( + curveName, + ecP.getCurve(), + ecP.getG(), + ecP.getN(), + ecP.getH(), + ecP.getSeed()); + + param = new ECKeyGenerationParameters(new ECDomainParameters(ecParams.getCurve(), ecParams.getG(), ecParams.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() != null) + { + ECParameterSpec p = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + this.ecParams = null; + + param = new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN()), random); + + engine.init(param); + initialised = true; + } + else if (params == null && BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa() == null) + { + throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set"); + } + else + { + throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec: " + params.getClass().getName()); + } + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + throw new IllegalStateException("EC Key Pair Generator not initialised"); + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters)pair.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters)pair.getPrivate(); + + if (ecParams == null) + { + return new KeyPair(new BCECGOST3410PublicKey(algorithm, pub), + new BCECGOST3410PrivateKey(algorithm, priv)); + } + else + { + ECParameterSpec p = (ECParameterSpec)ecParams; + + BCECGOST3410PublicKey pubKey = new BCECGOST3410PublicKey(algorithm, pub, p); + return new KeyPair(pubKey, + new BCECGOST3410PrivateKey(algorithm, priv, pubKey, p)); + } + } +} + diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java new file mode 100644 index 00000000..c3660fb1 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/ecgost/SignatureSpi.java @@ -0,0 +1,219 @@ +package org.spongycastle.jcajce.provider.asymmetric.ecgost; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.GOST3411Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.crypto.signers.ECGOST3410Signer; +import org.spongycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.spongycastle.jce.interfaces.ECKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.interfaces.GOST3410Key; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jcajce.provider.asymmetric.util.GOST3410Util; + +public class SignatureSpi + extends java.security.Signature + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + private Digest digest; + private DSA signer; + + public SignatureSpi() + { + super("ECGOST3410"); + this.digest = new GOST3411Digest(); + this.signer = new ECGOST3410Signer(); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else if (publicKey instanceof GOST3410Key) + { + param = GOST3410Util.generatePublicKeyParameter(publicKey); + } + else + { + try + { + byte[] bytes = publicKey.getEncoded(); + + publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes)); + + if (publicKey instanceof ECPublicKey) + { + param = ECUtil.generatePublicKeyParameter(publicKey); + } + else + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + catch (Exception e) + { + throw new InvalidKeyException("can't recognise key type in DSA based signer"); + } + } + + digest.reset(); + signer.init(false, param); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param; + + if (privateKey instanceof ECKey) + { + param = ECUtil.generatePrivateKeyParameter(privateKey); + } + else + { + param = GOST3410Util.generatePrivateKeyParameter(privateKey); + } + + digest.reset(); + + if (appRandom != null) + { + signer.init(true, new ParametersWithRandom(param, appRandom)); + } + else + { + signer.init(true, param); + } + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + byte[] sigBytes = new byte[64]; + BigInteger[] sig = signer.generateSignature(hash); + byte[] r = sig[0].toByteArray(); + byte[] s = sig[1].toByteArray(); + + if (s[0] != 0) + { + System.arraycopy(s, 0, sigBytes, 32 - s.length, s.length); + } + else + { + System.arraycopy(s, 1, sigBytes, 32 - (s.length - 1), s.length - 1); + } + + if (r[0] != 0) + { + System.arraycopy(r, 0, sigBytes, 64 - r.length, r.length); + } + else + { + System.arraycopy(r, 1, sigBytes, 64 - (r.length - 1), r.length - 1); + } + + return sigBytes; + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + byte[] r = new byte[32]; + byte[] s = new byte[32]; + + System.arraycopy(sigBytes, 0, s, 0, 32); + + System.arraycopy(sigBytes, 32, r, 0, 32); + + sig = new BigInteger[2]; + sig[0] = new BigInteger(1, r); + sig[1] = new BigInteger(1, s); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java new file mode 100644 index 00000000..7a6e69d3 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/elgamal/CipherSpi.java @@ -0,0 +1,299 @@ +package org.spongycastle.jcajce.provider.asymmetric.elgamal; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.interfaces.DHKey; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.BufferedAsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.encodings.ISO9796d1Encoding; +import org.spongycastle.crypto.encodings.OAEPEncoding; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseCipherSpi; +import org.spongycastle.jce.interfaces.ElGamalKey; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Strings; + +public class CipherSpi + extends BaseCipherSpi +{ + private BufferedAsymmetricBlockCipher cipher; + private AlgorithmParameterSpec paramSpec; + private AlgorithmParameters engineParams; + + public CipherSpi( + AsymmetricBlockCipher engine) + { + cipher = new BufferedAsymmetricBlockCipher(engine); + } + + protected int engineGetBlockSize() + { + return cipher.getInputBlockSize(); + } + + protected int engineGetKeySize( + Key key) + { + if (key instanceof ElGamalKey) + { + ElGamalKey k = (ElGamalKey)key; + + return k.getParameters().getP().bitLength(); + } + else if (key instanceof DHKey) + { + DHKey k = (DHKey)key; + + return k.getParams().getP().bitLength(); + } + + throw new IllegalArgumentException("not an ElGamal key!"); + } + + protected int engineGetOutputSize( + int inputLen) + { + return cipher.getOutputBlockSize(); + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (paramSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("OAEP", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(paramSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + String md = Strings.toUpperCase(mode); + + if (md.equals("NONE") || md.equals("ECB")) + { + return; + } + + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String pad = Strings.toUpperCase(padding); + + if (pad.equals("NOPADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new ElGamalEngine()); + } + else if (pad.equals("PKCS1PADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new PKCS1Encoding(new ElGamalEngine())); + } + else if (pad.equals("ISO9796-1PADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new ISO9796d1Encoding(new ElGamalEngine())); + } + else if (pad.equals("OAEPPADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new OAEPEncoding(new ElGamalEngine())); + } + else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING")) + { + cipher = new BufferedAsymmetricBlockCipher(new OAEPEncoding(new ElGamalEngine())); + } + else + { + throw new NoSuchPaddingException(padding + " unavailable with ElGamal."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException + { + CipherParameters param; + + if (params == null) + { + if (key instanceof ElGamalPublicKey) + { + param = ElGamalUtil.generatePublicKeyParameter((PublicKey)key); + } + else if (key instanceof ElGamalPrivateKey) + { + param = ElGamalUtil.generatePrivateKeyParameter((PrivateKey)key); + } + else + { + throw new InvalidKeyException("unknown key type passed to ElGamal"); + } + } + else + { + throw new IllegalArgumentException("unknown parameter type."); + } + + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + + switch (opmode) + { + case javax.crypto.Cipher.ENCRYPT_MODE: + case javax.crypto.Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case javax.crypto.Cipher.DECRYPT_MODE: + case javax.crypto.Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed to ElGamal"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("can't handle parameters in ElGamal"); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + cipher.processBytes(input, inputOffset, inputLen); + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + cipher.processBytes(input, inputOffset, inputLen); + return 0; + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + cipher.processBytes(input, inputOffset, inputLen); + try + { + return cipher.doFinal(); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + byte[] out; + + cipher.processBytes(input, inputOffset, inputLen); + + try + { + out = cipher.doFinal(); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + + for (int i = 0; i != out.length; i++) + { + output[outputOffset + i] = out[i]; + } + + return out.length; + } + + /** + * classes that inherit from us. + */ + static public class NoPadding + extends CipherSpi + { + public NoPadding() + { + super(new ElGamalEngine()); + } + } + + static public class PKCS1v1_5Padding + extends CipherSpi + { + public PKCS1v1_5Padding() + { + super(new PKCS1Encoding(new ElGamalEngine())); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java new file mode 100644 index 00000000..eb66bcf3 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java @@ -0,0 +1,217 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.PSSParameterSpec; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DEROutputStream; +import org.spongycastle.asn1.pkcs.RSAESOAEPparams; +import org.spongycastle.asn1.pkcs.RSASSAPSSparams; + +public abstract class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + protected AlgorithmParameterSpec engineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == null) + { + throw new NullPointerException("argument to getParameterSpec must not be null"); + } + + return localEngineGetParameterSpec(paramSpec); + } + + protected abstract AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException; + + public static class OAEP + extends AlgorithmParametersSpi + { + AlgorithmParameterSpec currentSpec; + + /** + * Return the PKCS#1 ASN.1 structure RSAES-OAEP-params. + */ + protected byte[] engineGetEncoded() + { + return null; + } + + protected byte[] engineGetEncoded( + String format) + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + throw new InvalidParameterSpecException("unknown parameter spec passed to OAEP parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + this.currentSpec = paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + RSAESOAEPparams oaepP = RSAESOAEPparams.getInstance(params); + + throw new IOException("Operation not supported"); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid OAEP Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid OAEP Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (format.equalsIgnoreCase("X.509") + || format.equalsIgnoreCase("ASN.1")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "OAEP Parameters"; + } + } + + public static class PSS + extends AlgorithmParametersSpi + { + PSSParameterSpec currentSpec; + + /** + * Return the PKCS#1 ASN.1 structure RSASSA-PSS-params. + */ + protected byte[] engineGetEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + PSSParameterSpec pssSpec = (PSSParameterSpec)currentSpec; + RSASSAPSSparams pssP = new RSASSAPSSparams(RSASSAPSSparams.DEFAULT_HASH_ALGORITHM, RSASSAPSSparams.DEFAULT_MASK_GEN_FUNCTION, new ASN1Integer(pssSpec.getSaltLength()), RSASSAPSSparams.DEFAULT_TRAILER_FIELD); + + dOut.writeObject(pssP); + dOut.close(); + + return bOut.toByteArray(); + } + + protected byte[] engineGetEncoded( + String format) + throws IOException + { + if (format.equalsIgnoreCase("X.509") + || format.equalsIgnoreCase("ASN.1")) + { + return engineGetEncoded(); + } + + return null; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec( + Class paramSpec) + throws InvalidParameterSpecException + { + if (paramSpec == PSSParameterSpec.class && currentSpec != null) + { + return currentSpec; + } + + throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object."); + } + + protected void engineInit( + AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (!(paramSpec instanceof PSSParameterSpec)) + { + throw new InvalidParameterSpecException("PSSParameterSpec required to initialise an PSS algorithm parameters object"); + } + + this.currentSpec = (PSSParameterSpec)paramSpec; + } + + protected void engineInit( + byte[] params) + throws IOException + { + try + { + RSASSAPSSparams pssP = RSASSAPSSparams.getInstance(params); + + currentSpec = new PSSParameterSpec( + pssP.getSaltLength().intValue()); + } + catch (ClassCastException e) + { + throw new IOException("Not a valid PSS Parameter encoding."); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new IOException("Not a valid PSS Parameter encoding."); + } + } + + protected void engineInit( + byte[] params, + String format) + throws IOException + { + if (isASN1FormatString(format) || format.equalsIgnoreCase("X.509")) + { + engineInit(params); + } + else + { + throw new IOException("Unknown parameter format " + format); + } + } + + protected String engineToString() + { + return "PSS Parameters"; + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java new file mode 100644 index 00000000..7609b4a7 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java @@ -0,0 +1,509 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.encodings.ISO9796d1Encoding; +import org.spongycastle.crypto.encodings.OAEPEncoding; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jcajce.provider.asymmetric.util.BaseCipherSpi; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Strings; + +public class CipherSpi + extends BaseCipherSpi +{ + private AsymmetricBlockCipher cipher; + private AlgorithmParameterSpec paramSpec; + private AlgorithmParameters engineParams; + private boolean publicKeyOnly = false; + private boolean privateKeyOnly = false; + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + public CipherSpi( + AsymmetricBlockCipher engine) + { + cipher = engine; + } + + public CipherSpi( + boolean publicKeyOnly, + boolean privateKeyOnly, + AsymmetricBlockCipher engine) + { + this.publicKeyOnly = publicKeyOnly; + this.privateKeyOnly = privateKeyOnly; + cipher = engine; + } + + protected int engineGetBlockSize() + { + try + { + return cipher.getInputBlockSize(); + } + catch (NullPointerException e) + { + throw new IllegalStateException("RSA Cipher not initialised"); + } + } + + protected int engineGetKeySize( + Key key) + { + if (key instanceof RSAPrivateKey) + { + RSAPrivateKey k = (RSAPrivateKey)key; + + return k.getModulus().bitLength(); + } + else if (key instanceof RSAPublicKey) + { + RSAPublicKey k = (RSAPublicKey)key; + + return k.getModulus().bitLength(); + } + + throw new IllegalArgumentException("not an RSA key!"); + } + + protected int engineGetOutputSize( + int inputLen) + { + try + { + return cipher.getOutputBlockSize(); + } + catch (NullPointerException e) + { + throw new IllegalStateException("RSA Cipher not initialised"); + } + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (paramSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("OAEP", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(paramSpec); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + String md = Strings.toUpperCase(mode); + + if (md.equals("NONE") || md.equals("ECB")) + { + return; + } + + if (md.equals("1")) + { + privateKeyOnly = true; + publicKeyOnly = false; + return; + } + else if (md.equals("2")) + { + privateKeyOnly = false; + publicKeyOnly = true; + return; + } + + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String pad = Strings.toUpperCase(padding); + + if (pad.equals("NOPADDING")) + { + cipher = new RSABlindedEngine(); + } + else if (pad.equals("PKCS1PADDING")) + { + cipher = new PKCS1Encoding(new RSABlindedEngine()); + } + else if (pad.equals("ISO9796-1PADDING")) + { + cipher = new ISO9796d1Encoding(new RSABlindedEngine()); + } + else if (pad.equals("OAEPPADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine()); + } + else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine()); + } + else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine(), new SHA224Digest()); + } + else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine(), new SHA256Digest()); + } + else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine(), new SHA384Digest()); + } + else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine(), new SHA512Digest()); + } + else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING")) + { + cipher = new OAEPEncoding(new RSABlindedEngine(), new MD5Digest()); + } + else + { + throw new NoSuchPaddingException(padding + " unavailable with RSA."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + if (params == null) + { + if (key instanceof RSAPublicKey) + { + if (privateKeyOnly && opmode == Cipher.ENCRYPT_MODE) + { + throw new InvalidKeyException( + "mode 1 requires RSAPrivateKey"); + } + + param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)key); + } + else if (key instanceof RSAPrivateKey) + { + if (publicKeyOnly && opmode == Cipher.ENCRYPT_MODE) + { + throw new InvalidKeyException( + "mode 2 requires RSAPublicKey"); + } + + param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)key); + } + else + { + throw new InvalidKeyException("unknown key type passed to RSA"); + } + } + else + { + throw new IllegalArgumentException("unknown parameter type."); + } + + if (!(cipher instanceof RSABlindedEngine)) + { + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + else + { + param = new ParametersWithRandom(param, new SecureRandom()); + } + } + + switch (opmode) + { + case javax.crypto.Cipher.ENCRYPT_MODE: + case javax.crypto.Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case javax.crypto.Cipher.DECRYPT_MODE: + case javax.crypto.Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed to RSA"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + throw new InvalidAlgorithmParameterException("cannot recognise parameters."); + } + + 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) + { + // this shouldn't happen + throw new InvalidKeyException("Eeeek! " + e.toString()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + bOut.write(input, inputOffset, inputLen); + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + bOut.write(input, inputOffset, inputLen); + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + return 0; + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + if (input != null) + { + bOut.write(input, inputOffset, inputLen); + } + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + try + { + byte[] bytes = bOut.toByteArray(); + + bOut.reset(); + + return cipher.processBlock(bytes, 0, bytes.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + if (input != null) + { + bOut.write(input, inputOffset, inputLen); + } + + if (cipher instanceof RSABlindedEngine) + { + if (bOut.size() > cipher.getInputBlockSize() + 1) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + else + { + if (bOut.size() > cipher.getInputBlockSize()) + { + throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); + } + } + + byte[] out; + + try + { + byte[] bytes = bOut.toByteArray(); + bOut.reset(); + + out = cipher.processBlock(bytes, 0, bytes.length); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + + for (int i = 0; i != out.length; i++) + { + output[outputOffset + i] = out[i]; + } + + return out.length; + } + + /** + * classes that inherit from us. + */ + + static public class NoPadding + extends CipherSpi + { + public NoPadding() + { + super(new RSABlindedEngine()); + } + } + + static public class PKCS1v1_5Padding + extends CipherSpi + { + public PKCS1v1_5Padding() + { + super(new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class PKCS1v1_5Padding_PrivateOnly + extends CipherSpi + { + public PKCS1v1_5Padding_PrivateOnly() + { + super(false, true, new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class PKCS1v1_5Padding_PublicOnly + extends CipherSpi + { + public PKCS1v1_5Padding_PublicOnly() + { + super(true, false, new PKCS1Encoding(new RSABlindedEngine())); + } + } + + static public class OAEPPadding + extends CipherSpi + { + public OAEPPadding() + { + super(new OAEPEncoding(new RSABlindedEngine())); + } + } + + static public class ISO9796d1Padding + extends CipherSpi + { + public ISO9796d1Padding() + { + super(new ISO9796d1Encoding(new RSABlindedEngine())); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java new file mode 100644 index 00000000..23dd0100 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java @@ -0,0 +1,405 @@ +package org.spongycastle.jcajce.provider.asymmetric.rsa; + +import java.io.ByteArrayOutputStream; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.PSSParameterSpec; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class PSSSignatureSpi + extends Signature +{ + private AlgorithmParameters engineParams; + private PSSParameterSpec paramSpec; + private AsymmetricBlockCipher signer; + private Digest contentDigest; + private Digest mgfDigest; + private int saltLength; + private byte trailer; + private boolean isRaw; + + private org.spongycastle.crypto.signers.PSSSigner pss; + + private byte getTrailer( + int trailerField) + { + if (trailerField == 1) + { + return org.spongycastle.crypto.signers.PSSSigner.TRAILER_IMPLICIT; + } + + throw new IllegalArgumentException("unknown trailer field"); + } + + private void setupContentDigest() + { + if (isRaw) + { + this.contentDigest = new NullPssDigest(mgfDigest); + } + else + { + this.contentDigest = mgfDigest; + } + } + + protected PSSSignatureSpi( + String name, + AsymmetricBlockCipher signer, + Digest digest) + { + super(name); + + this.signer = signer; + this.mgfDigest = digest; + + if (digest != null) + { + this.saltLength = digest.getDigestSize(); + } + else + { + this.saltLength = 20; + } + + if (paramSpec != null) + { + this.saltLength = paramSpec.getSaltLength(); + } + this.isRaw = false; + + setupContentDigest(); + } + + // care - this constructor is actually used by outside organisations + protected PSSSignatureSpi( + String name, + AsymmetricBlockCipher signer, + Digest digest, + boolean isRaw) + { + super(name); + + this.signer = signer; + this.mgfDigest = digest; + + if (digest != null) + { + this.saltLength = digest.getDigestSize(); + } + else + { + this.saltLength = 20; + } + + if (paramSpec != null) + { + this.saltLength = paramSpec.getSaltLength(); + } + + this.isRaw = isRaw; + + setupContentDigest(); + } + + protected void engineInitVerify( + PublicKey publicKey) + throws InvalidKeyException + { + if (!(publicKey instanceof RSAPublicKey)) + { + throw new InvalidKeyException("Supplied key is not a RSAPublicKey instance"); + } + + pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength); + pss.init(false, + RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey)); + } + + protected void engineInitSign( + PrivateKey privateKey, + SecureRandom random) + throws InvalidKeyException + { + if (!(privateKey instanceof RSAPrivateKey)) + { + throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance"); + } + + pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength); + pss.init(true, new ParametersWithRandom(RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey), random)); + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + if (!(privateKey instanceof RSAPrivateKey)) + { + throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance"); + } + + pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength); + pss.init(true, RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey)); + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + pss.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + pss.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + return pss.generateSignature(); + } + catch (CryptoException e) + { + throw new SignatureException(e.getMessage()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + return pss.verifySignature(sigBytes); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + throws InvalidParameterException + { + if (params instanceof PSSParameterSpec) + { + PSSParameterSpec newParamSpec = (PSSParameterSpec)params; + + this.engineParams = null; + this.paramSpec = newParamSpec; + this.saltLength = paramSpec.getSaltLength(); + + if (mgfDigest == null) + { + switch (saltLength) + { + case 20: + this.mgfDigest = new SHA1Digest(); + break; + case 28: + this.mgfDigest = new SHA224Digest(); + break; + case 32: + this.mgfDigest = new SHA256Digest(); + break; + case 48: + this.mgfDigest = new SHA384Digest(); + break; + case 64: + this.mgfDigest = new SHA512Digest(); + break; + } + setupContentDigest(); + } + } + else + { + throw new InvalidParameterException("Only PSSParameterSpec supported"); + } + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + try + { + engineParams = AlgorithmParameters.getInstance("PSS", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(new PSSParameterSpec(saltLength)); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + + return engineParams; + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineGetParameter unsupported"); + } + + static public class nonePSS + extends PSSSignatureSpi + { + public nonePSS() + { + super("NONEwithRSAandMGF1", new RSABlindedEngine(), null, true); + } + } + + static public class PSSwithRSA + extends PSSSignatureSpi + { + public PSSwithRSA() + { + super("SHA1withRSAandMGF1", new RSABlindedEngine(), null); + } + } + + static public class SHA1withRSA + extends PSSSignatureSpi + { + public SHA1withRSA() + { + super("SHA1withRSAandMGF1", new RSABlindedEngine(), new SHA1Digest()); + } + } + + static public class SHA224withRSA + extends PSSSignatureSpi + { + public SHA224withRSA() + { + super("SHA224withRSAandMGF1", new RSABlindedEngine(), new SHA224Digest()); + } + } + + static public class SHA256withRSA + extends PSSSignatureSpi + { + public SHA256withRSA() + { + super("SHA256withRSAandMGF1", new RSABlindedEngine(), new SHA256Digest()); + } + } + + static public class SHA384withRSA + extends PSSSignatureSpi + { + public SHA384withRSA() + { + super("SHA384withRSAandMGF1", new RSABlindedEngine(), new SHA384Digest()); + } + } + + static public class SHA512withRSA + extends PSSSignatureSpi + { + public SHA512withRSA() + { + super("SHA512withRSAandMGF1", new RSABlindedEngine(), new SHA512Digest()); + } + } + + private class NullPssDigest + implements Digest + { + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + private Digest baseDigest; + private boolean oddTime = true; + + public NullPssDigest(Digest mgfDigest) + { + this.baseDigest = mgfDigest; + } + + public String getAlgorithmName() + { + return "NULL"; + } + + public int getDigestSize() + { + return baseDigest.getDigestSize(); + } + + public void update(byte in) + { + bOut.write(in); + } + + public void update(byte[] in, int inOff, int len) + { + bOut.write(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + byte[] res = bOut.toByteArray(); + + if (oddTime) + { + System.arraycopy(res, 0, out, outOff, res.length); + } + else + { + baseDigest.update(res, 0, res.length); + + baseDigest.doFinal(out, outOff); + } + + reset(); + + oddTime = !oddTime; + + return res.length; + } + + public void reset() + { + bOut.reset(); + baseDigest.reset(); + } + + public int getByteLength() + { + return 0; + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java new file mode 100644 index 00000000..bdc65b04 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/util/DSABase.java @@ -0,0 +1,128 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.DSA; +import org.spongycastle.crypto.Digest; + +public abstract class DSABase + extends Signature + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + protected Digest digest; + protected DSA signer; + protected DSAEncoder encoder; + + protected DSABase( + String name, + Digest digest, + DSA signer, + DSAEncoder encoder) + { + super(name); + + this.digest = digest; + this.signer = signer; + this.encoder = encoder; + } + + protected void engineInitSign( + PrivateKey privateKey) + throws InvalidKeyException + { + doEngineInitSign(privateKey, appRandom); + } + + protected void engineUpdate( + byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate( + byte[] b, + int off, + int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + try + { + BigInteger[] sig = signer.generateSignature(hash); + + return encoder.encode(sig[0], sig[1]); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify( + byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + + digest.doFinal(hash, 0); + + BigInteger[] sig; + + try + { + sig = encoder.decode(sigBytes); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes."); + } + + return signer.verifySignature(hash, sig[0], sig[1]); + } + + protected void engineSetParameter( + AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)"> + */ + protected void engineSetParameter( + String param, + Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter( + String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + protected abstract void doEngineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException; +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java new file mode 100644 index 00000000..8cfba71f --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/asymmetric/util/ECUtil.java @@ -0,0 +1,220 @@ +package org.spongycastle.jcajce.provider.asymmetric.util; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.asn1.x9.X962NamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.spongycastle.jce.spec.ECParameterSpec; + +/** + * utility class for converting jce/jca ECDSA, ECDH, and ECDHC + * objects into their org.spongycastle.crypto counterparts. + */ +public class ECUtil +{ + /** + * Returns a sorted array of middle terms of the reduction polynomial. + * @param k The unsorted array of middle terms of the reduction polynomial + * of length 1 or 3. + * @return the sorted array of middle terms of the reduction polynomial. + * This array always has length 3. + */ + static int[] convertMidTerms( + int[] k) + { + int[] res = new int[3]; + + if (k.length == 1) + { + res[0] = k[0]; + } + else + { + if (k.length != 3) + { + throw new IllegalArgumentException("Only Trinomials and pentanomials supported"); + } + + if (k[0] < k[1] && k[0] < k[2]) + { + res[0] = k[0]; + if (k[1] < k[2]) + { + res[1] = k[1]; + res[2] = k[2]; + } + else + { + res[1] = k[2]; + res[2] = k[1]; + } + } + else if (k[1] < k[2]) + { + res[0] = k[1]; + if (k[0] < k[2]) + { + res[1] = k[0]; + res[2] = k[2]; + } + else + { + res[1] = k[2]; + res[2] = k[0]; + } + } + else + { + res[0] = k[2]; + if (k[0] < k[1]) + { + res[1] = k[0]; + res[2] = k[1]; + } + else + { + res[1] = k[1]; + res[2] = k[0]; + } + } + } + + return res; + } + + public static AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof ECPublicKey) + { + ECPublicKey k = (ECPublicKey)key; + ECParameterSpec s = k.getParameters(); + + if (s == null) + { + s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + + return new ECPublicKeyParameters( + ((BCECPublicKey)k).engineGetQ(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + else + { + return new ECPublicKeyParameters( + k.getQ(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + } + + throw new InvalidKeyException("cannot identify EC public key."); + } + + public static AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof ECPrivateKey) + { + ECPrivateKey k = (ECPrivateKey)key; + ECParameterSpec s = k.getParameters(); + + if (s == null) + { + s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); + } + + return new ECPrivateKeyParameters( + k.getD(), + new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed())); + } + + throw new InvalidKeyException("can't identify EC private key."); + } + + public static ASN1ObjectIdentifier getNamedCurveOid( + String name) + { + ASN1ObjectIdentifier oid = X962NamedCurves.getOID(name); + + if (oid == null) + { + oid = SECNamedCurves.getOID(name); + if (oid == null) + { + oid = NISTNamedCurves.getOID(name); + } + if (oid == null) + { + oid = TeleTrusTNamedCurves.getOID(name); + } + if (oid == null) + { + oid = ECGOST3410NamedCurves.getOID(name); + } + } + + return oid; + } + + public static X9ECParameters getNamedCurveByOid( + ASN1ObjectIdentifier oid) + { + X9ECParameters params = X962NamedCurves.getByOID(oid); + + if (params == null) + { + params = SECNamedCurves.getByOID(oid); + if (params == null) + { + params = NISTNamedCurves.getByOID(oid); + } + if (params == null) + { + params = TeleTrusTNamedCurves.getByOID(oid); + } + } + + return params; + } + + public static String getCurveName( + ASN1ObjectIdentifier oid) + { + String name = X962NamedCurves.getName(oid); + + if (name == null) + { + name = SECNamedCurves.getName(oid); + if (name == null) + { + name = NISTNamedCurves.getName(oid); + } + if (name == null) + { + name = TeleTrusTNamedCurves.getName(oid); + } + if (name == null) + { + name = ECGOST3410NamedCurves.getName(oid); + } + } + + return name; + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java new file mode 100644 index 00000000..dbad7772 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java @@ -0,0 +1,1637 @@ +package org.spongycastle.jcajce.provider.keystore.pkcs12; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.KeyStoreSpi; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Set; +import org.spongycastle.asn1.BEROctetString; +import org.spongycastle.asn1.BEROutputStream; +import org.spongycastle.asn1.DERBMPString; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.DEROutputStream; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSet; +import org.spongycastle.asn1.pkcs.AuthenticatedSafe; +import org.spongycastle.asn1.pkcs.CertBag; +import org.spongycastle.asn1.pkcs.ContentInfo; +import org.spongycastle.asn1.pkcs.EncryptedData; +import org.spongycastle.asn1.pkcs.MacData; +import org.spongycastle.asn1.pkcs.PBES2Parameters; +import org.spongycastle.asn1.pkcs.PBKDF2Params; +import org.spongycastle.asn1.pkcs.PKCS12PBEParams; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.Pfx; +import org.spongycastle.asn1.pkcs.SafeBag; +import org.spongycastle.asn1.util.ASN1Dump; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.AuthorityKeyIdentifier; +import org.spongycastle.asn1.x509.DigestInfo; +import org.spongycastle.asn1.x509.Extension; +import org.spongycastle.asn1.x509.SubjectKeyIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.jcajce.provider.symmetric.util.BCPBEKey; +import org.spongycastle.jcajce.provider.util.SecretKeyUtil; +import org.spongycastle.jce.interfaces.BCKeyStore; +import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; + +public class PKCS12KeyStoreSpi + extends KeyStoreSpi + implements PKCSObjectIdentifiers, X509ObjectIdentifiers, BCKeyStore +{ + private static final int SALT_SIZE = 20; + private static final int MIN_ITERATIONS = 1024; + + private static final Provider bcProvider = new BouncyCastleProvider(); + + private IgnoresCaseHashtable keys = new IgnoresCaseHashtable(); + private Hashtable localIds = new Hashtable(); + private IgnoresCaseHashtable certs = new IgnoresCaseHashtable(); + private Hashtable chainCerts = new Hashtable(); + private Hashtable keyCerts = new Hashtable(); + + // + // generic object types + // + static final int NULL = 0; + static final int CERTIFICATE = 1; + static final int KEY = 2; + static final int SECRET = 3; + static final int SEALED = 4; + + // + // key types + // + static final int KEY_PRIVATE = 0; + static final int KEY_PUBLIC = 1; + static final int KEY_SECRET = 2; + + protected SecureRandom random = new SecureRandom(); + + // use of final causes problems with JDK 1.2 compiler + private CertificateFactory certFact; + private ASN1ObjectIdentifier keyAlgorithm; + private ASN1ObjectIdentifier certAlgorithm; + + private class CertId + { + byte[] id; + + CertId( + PublicKey key) + { + this.id = createSubjectKeyId(key).getKeyIdentifier(); + } + + CertId( + byte[] id) + { + this.id = id; + } + + public int hashCode() + { + return Arrays.hashCode(id); + } + + public boolean equals( + Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof CertId)) + { + return false; + } + + CertId cId = (CertId)o; + + return Arrays.areEqual(id, cId.id); + } + } + + public PKCS12KeyStoreSpi( + Provider provider, + ASN1ObjectIdentifier keyAlgorithm, + ASN1ObjectIdentifier certAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.certAlgorithm = certAlgorithm; + + try + { + if (provider != null) + { + certFact = CertificateFactory.getInstance("X.509", provider); + } + else + { + certFact = CertificateFactory.getInstance("X.509"); + } + } + catch (Exception e) + { + throw new IllegalArgumentException("can't create cert factory - " + e.toString()); + } + } + + private SubjectKeyIdentifier createSubjectKeyId( + PublicKey pubKey) + { + try + { + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + (ASN1Sequence)ASN1Primitive.fromByteArray(pubKey.getEncoded())); + + return new SubjectKeyIdentifier(getDigest(info)); + } + catch (Exception e) + { + throw new RuntimeException("error creating key"); + } + } + + private static byte[] getDigest(SubjectPublicKeyInfo spki) + { + Digest digest = new SHA1Digest(); + byte[] resBuf = new byte[digest.getDigestSize()]; + + byte[] bytes = spki.getPublicKeyData().getBytes(); + digest.update(bytes, 0, bytes.length); + digest.doFinal(resBuf, 0); + return resBuf; + } + + public void setRandom( + SecureRandom rand) + { + this.random = rand; + } + + public Enumeration engineAliases() + { + Hashtable tab = new Hashtable(); + + Enumeration e = certs.keys(); + while (e.hasMoreElements()) + { + tab.put(e.nextElement(), "cert"); + } + + e = keys.keys(); + while (e.hasMoreElements()) + { + String a = (String)e.nextElement(); + if (tab.get(a) == null) + { + tab.put(a, "key"); + } + } + + return tab.keys(); + } + + public boolean engineContainsAlias( + String alias) + { + return (certs.get(alias) != null || keys.get(alias) != null); + } + + /** + * this is not quite complete - we should follow up on the chain, a bit + * tricky if a certificate appears in more than one chain... + */ + public void engineDeleteEntry( + String alias) + throws KeyStoreException + { + Key k = (Key)keys.remove(alias); + + Certificate c = (Certificate)certs.remove(alias); + + if (c != null) + { + chainCerts.remove(new CertId(c.getPublicKey())); + } + + if (k != null) + { + String id = (String)localIds.remove(alias); + if (id != null) + { + c = (Certificate)keyCerts.remove(id); + } + if (c != null) + { + chainCerts.remove(new CertId(c.getPublicKey())); + } + } + } + + /** + * simply return the cert for the private key + */ + public Certificate engineGetCertificate( + String alias) + { + if (alias == null) + { + throw new IllegalArgumentException("null alias passed to getCertificate."); + } + + Certificate c = (Certificate)certs.get(alias); + + // + // look up the key table - and try the local key id + // + if (c == null) + { + String id = (String)localIds.get(alias); + if (id != null) + { + c = (Certificate)keyCerts.get(id); + } + else + { + c = (Certificate)keyCerts.get(alias); + } + } + + return c; + } + + public String engineGetCertificateAlias( + Certificate cert) + { + Enumeration c = certs.elements(); + Enumeration k = certs.keys(); + + while (c.hasMoreElements()) + { + Certificate tc = (Certificate)c.nextElement(); + String ta = (String)k.nextElement(); + + if (tc.equals(cert)) + { + return ta; + } + } + + c = keyCerts.elements(); + k = keyCerts.keys(); + + while (c.hasMoreElements()) + { + Certificate tc = (Certificate)c.nextElement(); + String ta = (String)k.nextElement(); + + if (tc.equals(cert)) + { + return ta; + } + } + + return null; + } + + public Certificate[] engineGetCertificateChain( + String alias) + { + if (alias == null) + { + throw new IllegalArgumentException("null alias passed to getCertificateChain."); + } + + if (!engineIsKeyEntry(alias)) + { + return null; + } + + Certificate c = engineGetCertificate(alias); + + if (c != null) + { + Vector cs = new Vector(); + + while (c != null) + { + X509Certificate x509c = (X509Certificate)c; + Certificate nextC = null; + + byte[] bytes = x509c.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (bytes != null) + { + try + { + ASN1InputStream aIn = new ASN1InputStream(bytes); + + byte[] authBytes = ((ASN1OctetString)aIn.readObject()).getOctets(); + aIn = new ASN1InputStream(authBytes); + + AuthorityKeyIdentifier id = AuthorityKeyIdentifier.getInstance(aIn.readObject()); + if (id.getKeyIdentifier() != null) + { + nextC = (Certificate)chainCerts.get(new CertId(id.getKeyIdentifier())); + } + + } + catch (IOException e) + { + throw new RuntimeException(e.toString()); + } + } + + if (nextC == null) + { + // + // no authority key id, try the Issuer DN + // + Principal i = x509c.getIssuerDN(); + Principal s = x509c.getSubjectDN(); + + if (!i.equals(s)) + { + Enumeration e = chainCerts.keys(); + + while (e.hasMoreElements()) + { + X509Certificate crt = (X509Certificate)chainCerts.get(e.nextElement()); + Principal sub = crt.getSubjectDN(); + if (sub.equals(i)) + { + try + { + x509c.verify(crt.getPublicKey()); + nextC = crt; + break; + } + catch (Exception ex) + { + // continue + } + } + } + } + } + + cs.addElement(c); + if (nextC != c) // self signed - end of the chain + { + c = nextC; + } + else + { + c = null; + } + } + + Certificate[] certChain = new Certificate[cs.size()]; + + for (int i = 0; i != certChain.length; i++) + { + certChain[i] = (Certificate)cs.elementAt(i); + } + + return certChain; + } + + return null; + } + + public Date engineGetCreationDate(String alias) + { + if (alias == null) + { + throw new NullPointerException("alias == null"); + } + if (keys.get(alias) == null && certs.get(alias) == null) + { + return null; + } + return new Date(); + } + + public Key engineGetKey( + String alias, + char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException + { + if (alias == null) + { + throw new IllegalArgumentException("null alias passed to getKey."); + } + + return (Key)keys.get(alias); + } + + public boolean engineIsCertificateEntry( + String alias) + { + return (certs.get(alias) != null && keys.get(alias) == null); + } + + public boolean engineIsKeyEntry( + String alias) + { + return (keys.get(alias) != null); + } + + public void engineSetCertificateEntry( + String alias, + Certificate cert) + throws KeyStoreException + { + if (keys.get(alias) != null) + { + throw new KeyStoreException("There is a key entry with the name " + alias + "."); + } + + certs.put(alias, cert); + chainCerts.put(new CertId(cert.getPublicKey()), cert); + } + + public void engineSetKeyEntry( + String alias, + byte[] key, + Certificate[] chain) + throws KeyStoreException + { + throw new RuntimeException("operation not supported"); + } + + public void engineSetKeyEntry( + String alias, + Key key, + char[] password, + Certificate[] chain) + throws KeyStoreException + { + if (!(key instanceof PrivateKey)) + { + throw new KeyStoreException("PKCS12 does not support non-PrivateKeys"); + } + + if ((key instanceof PrivateKey) && (chain == null)) + { + throw new KeyStoreException("no certificate chain for private key"); + } + + if (keys.get(alias) != null) + { + engineDeleteEntry(alias); + } + + keys.put(alias, key); + if (chain != null) + { + certs.put(alias, chain[0]); + + for (int i = 0; i != chain.length; i++) + { + chainCerts.put(new CertId(chain[i].getPublicKey()), chain[i]); + } + } + } + + public int engineSize() + { + Hashtable tab = new Hashtable(); + + Enumeration e = certs.keys(); + while (e.hasMoreElements()) + { + tab.put(e.nextElement(), "cert"); + } + + e = keys.keys(); + while (e.hasMoreElements()) + { + String a = (String)e.nextElement(); + if (tab.get(a) == null) + { + tab.put(a, "key"); + } + } + + return tab.size(); + } + + protected PrivateKey unwrapKey( + AlgorithmIdentifier algId, + byte[] data, + char[] password, + boolean wrongPKCS12Zero) + throws IOException + { + ASN1ObjectIdentifier algorithm = algId.getAlgorithm(); + try + { + if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)) + { + PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters()); + + PBEKeySpec pbeSpec = new PBEKeySpec(password); + PrivateKey out; + + SecretKeyFactory keyFact = SecretKeyFactory.getInstance( + algorithm.getId(), bcProvider); + PBEParameterSpec defParams = new PBEParameterSpec( + pbeParams.getIV(), + pbeParams.getIterations().intValue()); + + SecretKey k = keyFact.generateSecret(pbeSpec); + + ((BCPBEKey)k).setTryWrongPKCS12Zero(wrongPKCS12Zero); + + Cipher cipher = Cipher.getInstance(algorithm.getId(), bcProvider); + + cipher.init(Cipher.UNWRAP_MODE, k, defParams); + + // we pass "" as the key algorithm type as it is unknown at this point + return (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY); + } + else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) + { + PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters()); + PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters()); + + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider); + + SecretKey k = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), SecretKeyUtil.getKeySize(alg.getEncryptionScheme().getAlgorithm()))); + + Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId(), bcProvider); + + cipher.init(Cipher.UNWRAP_MODE, k, new IvParameterSpec(ASN1OctetString.getInstance(alg.getEncryptionScheme().getParameters()).getOctets())); + + // we pass "" as the key algorithm type as it is unknown at this point + return (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY); + } + } + catch (Exception e) + { + throw new IOException("exception unwrapping private key - " + e.toString()); + } + + throw new IOException("exception unwrapping private key - cannot recognise: " + algorithm); + } + + protected byte[] wrapKey( + String algorithm, + Key key, + PKCS12PBEParams pbeParams, + char[] password) + throws IOException + { + PBEKeySpec pbeSpec = new PBEKeySpec(password); + byte[] out; + + try + { + SecretKeyFactory keyFact = SecretKeyFactory.getInstance( + algorithm, bcProvider); + PBEParameterSpec defParams = new PBEParameterSpec( + pbeParams.getIV(), + pbeParams.getIterations().intValue()); + + Cipher cipher = Cipher.getInstance(algorithm, bcProvider); + + cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), defParams); + + out = cipher.wrap(key); + } + catch (Exception e) + { + throw new IOException("exception encrypting data - " + e.toString()); + } + + return out; + } + + protected byte[] cryptData( + boolean forEncryption, + AlgorithmIdentifier algId, + char[] password, + boolean wrongPKCS12Zero, + byte[] data) + throws IOException + { + String algorithm = algId.getAlgorithm().getId(); + PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters()); + PBEKeySpec pbeSpec = new PBEKeySpec(password); + + try + { + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, bcProvider); + PBEParameterSpec defParams = new PBEParameterSpec( + pbeParams.getIV(), + pbeParams.getIterations().intValue()); + BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec); + + key.setTryWrongPKCS12Zero(wrongPKCS12Zero); + + Cipher cipher = Cipher.getInstance(algorithm, bcProvider); + int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; + cipher.init(mode, key, defParams); + return cipher.doFinal(data); + } + catch (Exception e) + { + throw new IOException("exception decrypting data - " + e.toString()); + } + } + + public void engineLoad( + InputStream stream, + char[] password) + throws IOException + { + if (stream == null) // just initialising + { + return; + } + + if (password == null) + { + throw new NullPointerException("No password supplied for PKCS#12 KeyStore."); + } + + BufferedInputStream bufIn = new BufferedInputStream(stream); + + bufIn.mark(10); + + int head = bufIn.read(); + + if (head != 0x30) + { + throw new IOException("stream does not represent a PKCS12 key store"); + } + + bufIn.reset(); + + ASN1InputStream bIn = new ASN1InputStream(bufIn); + ASN1Sequence obj = (ASN1Sequence)bIn.readObject(); + Pfx bag = Pfx.getInstance(obj); + ContentInfo info = bag.getAuthSafe(); + Vector chain = new Vector(); + boolean unmarkedKey = false; + boolean wrongPKCS12Zero = false; + + if (bag.getMacData() != null) // check the mac code + { + MacData mData = bag.getMacData(); + DigestInfo dInfo = mData.getMac(); + AlgorithmIdentifier algId = dInfo.getAlgorithmId(); + byte[] salt = mData.getSalt(); + int itCount = mData.getIterationCount().intValue(); + + byte[] data = ((ASN1OctetString)info.getContent()).getOctets(); + + try + { + byte[] res = calculatePbeMac(algId.getAlgorithm(), salt, itCount, password, false, data); + byte[] dig = dInfo.getDigest(); + + if (!Arrays.constantTimeAreEqual(res, dig)) + { + if (password.length > 0) + { + throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file."); + } + + // Try with incorrect zero length password + res = calculatePbeMac(algId.getAlgorithm(), salt, itCount, password, true, data); + + if (!Arrays.constantTimeAreEqual(res, dig)) + { + throw new IOException("PKCS12 key store mac invalid - wrong password or corrupted file."); + } + + wrongPKCS12Zero = true; + } + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new IOException("error constructing MAC: " + e.toString()); + } + } + + keys = new IgnoresCaseHashtable(); + localIds = new Hashtable(); + + if (info.getContentType().equals(data)) + { + bIn = new ASN1InputStream(((ASN1OctetString)info.getContent()).getOctets()); + + AuthenticatedSafe authSafe = AuthenticatedSafe.getInstance(bIn.readObject()); + ContentInfo[] c = authSafe.getContentInfo(); + + for (int i = 0; i != c.length; i++) + { + if (c[i].getContentType().equals(data)) + { + ASN1InputStream dIn = new ASN1InputStream(((ASN1OctetString)c[i].getContent()).getOctets()); + ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); + + for (int j = 0; j != seq.size(); j++) + { + SafeBag b = SafeBag.getInstance(seq.getObjectAt(j)); + if (b.getBagId().equals(pkcs8ShroudedKeyBag)) + { + org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue()); + PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero); + + // + // set the attributes on the key + // + PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey; + String alias = null; + ASN1OctetString localId = null; + + if (b.getBagAttributes() != null) + { + Enumeration e = b.getBagAttributes().getObjects(); + while (e.hasMoreElements()) + { + ASN1Sequence sq = (ASN1Sequence)e.nextElement(); + ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0); + ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1); + ASN1Primitive attr = null; + + if (attrSet.size() > 0) + { + attr = (ASN1Primitive)attrSet.getObjectAt(0); + + ASN1Encodable existing = bagAttr.getBagAttribute(aOid); + if (existing != null) + { + // OK, but the value has to be the same + if (!existing.toASN1Primitive().equals(attr)) + { + throw new IOException( + "attempt to add existing attribute with different value"); + } + } + else + { + bagAttr.setBagAttribute(aOid, attr); + } + } + + if (aOid.equals(pkcs_9_at_friendlyName)) + { + alias = ((DERBMPString)attr).getString(); + keys.put(alias, privKey); + } + else if (aOid.equals(pkcs_9_at_localKeyId)) + { + localId = (ASN1OctetString)attr; + } + } + } + + if (localId != null) + { + String name = new String(Hex.encode(localId.getOctets())); + + if (alias == null) + { + keys.put(name, privKey); + } + else + { + localIds.put(alias, name); + } + } + else + { + unmarkedKey = true; + keys.put("unmarked", privKey); + } + } + else if (b.getBagId().equals(certBag)) + { + chain.addElement(b); + } + else + { + System.out.println("extra in data " + b.getBagId()); + System.out.println(ASN1Dump.dumpAsString(b)); + } + } + } + else if (c[i].getContentType().equals(encryptedData)) + { + EncryptedData d = EncryptedData.getInstance(c[i].getContent()); + byte[] octets = cryptData(false, d.getEncryptionAlgorithm(), + password, wrongPKCS12Zero, d.getContent().getOctets()); + ASN1Sequence seq = (ASN1Sequence)ASN1Primitive.fromByteArray(octets); + + for (int j = 0; j != seq.size(); j++) + { + SafeBag b = SafeBag.getInstance(seq.getObjectAt(j)); + + if (b.getBagId().equals(certBag)) + { + chain.addElement(b); + } + else if (b.getBagId().equals(pkcs8ShroudedKeyBag)) + { + org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn = org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(b.getBagValue()); + PrivateKey privKey = unwrapKey(eIn.getEncryptionAlgorithm(), eIn.getEncryptedData(), password, wrongPKCS12Zero); + + // + // set the attributes on the key + // + PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey; + String alias = null; + ASN1OctetString localId = null; + + Enumeration e = b.getBagAttributes().getObjects(); + while (e.hasMoreElements()) + { + ASN1Sequence sq = (ASN1Sequence)e.nextElement(); + ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0); + ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1); + ASN1Primitive attr = null; + + if (attrSet.size() > 0) + { + attr = (ASN1Primitive)attrSet.getObjectAt(0); + + ASN1Encodable existing = bagAttr.getBagAttribute(aOid); + if (existing != null) + { + // OK, but the value has to be the same + if (!existing.toASN1Primitive().equals(attr)) + { + throw new IOException( + "attempt to add existing attribute with different value"); + } + } + else + { + bagAttr.setBagAttribute(aOid, attr); + } + } + + if (aOid.equals(pkcs_9_at_friendlyName)) + { + alias = ((DERBMPString)attr).getString(); + keys.put(alias, privKey); + } + else if (aOid.equals(pkcs_9_at_localKeyId)) + { + localId = (ASN1OctetString)attr; + } + } + + String name = new String(Hex.encode(localId.getOctets())); + + if (alias == null) + { + keys.put(name, privKey); + } + else + { + localIds.put(alias, name); + } + } + else if (b.getBagId().equals(keyBag)) + { + org.spongycastle.asn1.pkcs.PrivateKeyInfo kInfo = org.spongycastle.asn1.pkcs.PrivateKeyInfo.getInstance(b.getBagValue()); + PrivateKey privKey = BouncyCastleProvider.getPrivateKey(kInfo); + + // + // set the attributes on the key + // + PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)privKey; + String alias = null; + ASN1OctetString localId = null; + + Enumeration e = b.getBagAttributes().getObjects(); + while (e.hasMoreElements()) + { + ASN1Sequence sq = (ASN1Sequence)e.nextElement(); + ASN1ObjectIdentifier aOid = (ASN1ObjectIdentifier)sq.getObjectAt(0); + ASN1Set attrSet = (ASN1Set)sq.getObjectAt(1); + ASN1Primitive attr = null; + + if (attrSet.size() > 0) + { + attr = (ASN1Primitive)attrSet.getObjectAt(0); + + ASN1Encodable existing = bagAttr.getBagAttribute(aOid); + if (existing != null) + { + // OK, but the value has to be the same + if (!existing.toASN1Primitive().equals(attr)) + { + throw new IOException( + "attempt to add existing attribute with different value"); + } + } + else + { + bagAttr.setBagAttribute(aOid, attr); + } + } + + if (aOid.equals(pkcs_9_at_friendlyName)) + { + alias = ((DERBMPString)attr).getString(); + keys.put(alias, privKey); + } + else if (aOid.equals(pkcs_9_at_localKeyId)) + { + localId = (ASN1OctetString)attr; + } + } + + String name = new String(Hex.encode(localId.getOctets())); + + if (alias == null) + { + keys.put(name, privKey); + } + else + { + localIds.put(alias, name); + } + } + else + { + System.out.println("extra in encryptedData " + b.getBagId()); + System.out.println(ASN1Dump.dumpAsString(b)); + } + } + } + else + { + System.out.println("extra " + c[i].getContentType().getId()); + System.out.println("extra " + ASN1Dump.dumpAsString(c[i].getContent())); + } + } + } + + certs = new IgnoresCaseHashtable(); + chainCerts = new Hashtable(); + keyCerts = new Hashtable(); + + for (int i = 0; i != chain.size(); i++) + { + SafeBag b = (SafeBag)chain.elementAt(i); + CertBag cb = CertBag.getInstance(b.getBagValue()); + + if (!cb.getCertId().equals(x509Certificate)) + { + throw new RuntimeException("Unsupported certificate type: " + cb.getCertId()); + } + + Certificate cert; + + try + { + ByteArrayInputStream cIn = new ByteArrayInputStream( + ((ASN1OctetString)cb.getCertValue()).getOctets()); + cert = certFact.generateCertificate(cIn); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + + // + // set the attributes + // + ASN1OctetString localId = null; + String alias = null; + + if (b.getBagAttributes() != null) + { + Enumeration e = b.getBagAttributes().getObjects(); + while (e.hasMoreElements()) + { + ASN1Sequence sq = (ASN1Sequence)e.nextElement(); + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)sq.getObjectAt(0); + ASN1Primitive attr = (ASN1Primitive)((ASN1Set)sq.getObjectAt(1)).getObjectAt(0); + PKCS12BagAttributeCarrier bagAttr = null; + + if (cert instanceof PKCS12BagAttributeCarrier) + { + bagAttr = (PKCS12BagAttributeCarrier)cert; + + ASN1Encodable existing = bagAttr.getBagAttribute(oid); + if (existing != null) + { + // OK, but the value has to be the same + if (!existing.toASN1Primitive().equals(attr)) + { + throw new IOException( + "attempt to add existing attribute with different value"); + } + } + else + { + bagAttr.setBagAttribute(oid, attr); + } + } + + if (oid.equals(pkcs_9_at_friendlyName)) + { + alias = ((DERBMPString)attr).getString(); + } + else if (oid.equals(pkcs_9_at_localKeyId)) + { + localId = (ASN1OctetString)attr; + } + } + } + + chainCerts.put(new CertId(cert.getPublicKey()), cert); + + if (unmarkedKey) + { + if (keyCerts.isEmpty()) + { + String name = new String(Hex.encode(createSubjectKeyId(cert.getPublicKey()).getKeyIdentifier())); + + keyCerts.put(name, cert); + keys.put(name, keys.remove("unmarked")); + } + } + else + { + // + // the local key id needs to override the friendly name + // + if (localId != null) + { + String name = new String(Hex.encode(localId.getOctets())); + + keyCerts.put(name, cert); + } + if (alias != null) + { + certs.put(alias, cert); + } + } + } + } + + public void engineStore(OutputStream stream, char[] password) + throws IOException + { + doStore(stream, password, false); + } + + private void doStore(OutputStream stream, char[] password, boolean useDEREncoding) + throws IOException + { + if (password == null) + { + throw new NullPointerException("No password supplied for PKCS#12 KeyStore."); + } + + // + // handle the key + // + ASN1EncodableVector keyS = new ASN1EncodableVector(); + + + Enumeration ks = keys.keys(); + + while (ks.hasMoreElements()) + { + byte[] kSalt = new byte[SALT_SIZE]; + + random.nextBytes(kSalt); + + String name = (String)ks.nextElement(); + PrivateKey privKey = (PrivateKey)keys.get(name); + PKCS12PBEParams kParams = new PKCS12PBEParams(kSalt, MIN_ITERATIONS); + byte[] kBytes = wrapKey(keyAlgorithm.getId(), privKey, kParams, password); + AlgorithmIdentifier kAlgId = new AlgorithmIdentifier(keyAlgorithm, kParams.toASN1Primitive()); + org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo kInfo = new org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo(kAlgId, kBytes); + boolean attrSet = false; + ASN1EncodableVector kName = new ASN1EncodableVector(); + + if (privKey instanceof PKCS12BagAttributeCarrier) + { + PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)privKey; + // + // make sure we are using the local alias on store + // + DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName); + if (nm == null || !nm.getString().equals(name)) + { + bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name)); + } + + // + // make sure we have a local key-id + // + if (bagAttrs.getBagAttribute(pkcs_9_at_localKeyId) == null) + { + Certificate ct = engineGetCertificate(name); + + bagAttrs.setBagAttribute(pkcs_9_at_localKeyId, createSubjectKeyId(ct.getPublicKey())); + } + + Enumeration e = bagAttrs.getBagAttributeKeys(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + ASN1EncodableVector kSeq = new ASN1EncodableVector(); + + kSeq.add(oid); + kSeq.add(new DERSet(bagAttrs.getBagAttribute(oid))); + + attrSet = true; + + kName.add(new DERSequence(kSeq)); + } + } + + if (!attrSet) + { + // + // set a default friendly name (from the key id) and local id + // + ASN1EncodableVector kSeq = new ASN1EncodableVector(); + Certificate ct = engineGetCertificate(name); + + kSeq.add(pkcs_9_at_localKeyId); + kSeq.add(new DERSet(createSubjectKeyId(ct.getPublicKey()))); + + kName.add(new DERSequence(kSeq)); + + kSeq = new ASN1EncodableVector(); + + kSeq.add(pkcs_9_at_friendlyName); + kSeq.add(new DERSet(new DERBMPString(name))); + + kName.add(new DERSequence(kSeq)); + } + + SafeBag kBag = new SafeBag(pkcs8ShroudedKeyBag, kInfo.toASN1Primitive(), new DERSet(kName)); + keyS.add(kBag); + } + + byte[] keySEncoded = new DERSequence(keyS).getEncoded(ASN1Encoding.DER); + BEROctetString keyString = new BEROctetString(keySEncoded); + + // + // certificate processing + // + byte[] cSalt = new byte[SALT_SIZE]; + + random.nextBytes(cSalt); + + ASN1EncodableVector certSeq = new ASN1EncodableVector(); + PKCS12PBEParams cParams = new PKCS12PBEParams(cSalt, MIN_ITERATIONS); + AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.toASN1Primitive()); + Hashtable doneCerts = new Hashtable(); + + Enumeration cs = keys.keys(); + while (cs.hasMoreElements()) + { + try + { + String name = (String)cs.nextElement(); + Certificate cert = engineGetCertificate(name); + boolean cAttrSet = false; + CertBag cBag = new CertBag( + x509Certificate, + new DEROctetString(cert.getEncoded())); + ASN1EncodableVector fName = new ASN1EncodableVector(); + + if (cert instanceof PKCS12BagAttributeCarrier) + { + PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert; + // + // make sure we are using the local alias on store + // + DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName); + if (nm == null || !nm.getString().equals(name)) + { + bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(name)); + } + + // + // make sure we have a local key-id + // + if (bagAttrs.getBagAttribute(pkcs_9_at_localKeyId) == null) + { + bagAttrs.setBagAttribute(pkcs_9_at_localKeyId, createSubjectKeyId(cert.getPublicKey())); + } + + Enumeration e = bagAttrs.getBagAttributeKeys(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + ASN1EncodableVector fSeq = new ASN1EncodableVector(); + + fSeq.add(oid); + fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid))); + fName.add(new DERSequence(fSeq)); + + cAttrSet = true; + } + } + + if (!cAttrSet) + { + ASN1EncodableVector fSeq = new ASN1EncodableVector(); + + fSeq.add(pkcs_9_at_localKeyId); + fSeq.add(new DERSet(createSubjectKeyId(cert.getPublicKey()))); + fName.add(new DERSequence(fSeq)); + + fSeq = new ASN1EncodableVector(); + + fSeq.add(pkcs_9_at_friendlyName); + fSeq.add(new DERSet(new DERBMPString(name))); + + fName.add(new DERSequence(fSeq)); + } + + SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName)); + + certSeq.add(sBag); + + doneCerts.put(cert, cert); + } + catch (CertificateEncodingException e) + { + throw new IOException("Error encoding certificate: " + e.toString()); + } + } + + cs = certs.keys(); + while (cs.hasMoreElements()) + { + try + { + String certId = (String)cs.nextElement(); + Certificate cert = (Certificate)certs.get(certId); + boolean cAttrSet = false; + + if (keys.get(certId) != null) + { + continue; + } + + CertBag cBag = new CertBag( + x509Certificate, + new DEROctetString(cert.getEncoded())); + ASN1EncodableVector fName = new ASN1EncodableVector(); + + if (cert instanceof PKCS12BagAttributeCarrier) + { + PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert; + // + // make sure we are using the local alias on store + // + DERBMPString nm = (DERBMPString)bagAttrs.getBagAttribute(pkcs_9_at_friendlyName); + if (nm == null || !nm.getString().equals(certId)) + { + bagAttrs.setBagAttribute(pkcs_9_at_friendlyName, new DERBMPString(certId)); + } + + Enumeration e = bagAttrs.getBagAttributeKeys(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + + // a certificate not immediately linked to a key doesn't require + // a localKeyID and will confuse some PKCS12 implementations. + // + // If we find one, we'll prune it out. + if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId)) + { + continue; + } + + ASN1EncodableVector fSeq = new ASN1EncodableVector(); + + fSeq.add(oid); + fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid))); + fName.add(new DERSequence(fSeq)); + + cAttrSet = true; + } + } + + if (!cAttrSet) + { + ASN1EncodableVector fSeq = new ASN1EncodableVector(); + + fSeq.add(pkcs_9_at_friendlyName); + fSeq.add(new DERSet(new DERBMPString(certId))); + + fName.add(new DERSequence(fSeq)); + } + + SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName)); + + certSeq.add(sBag); + + doneCerts.put(cert, cert); + } + catch (CertificateEncodingException e) + { + throw new IOException("Error encoding certificate: " + e.toString()); + } + } + + cs = chainCerts.keys(); + while (cs.hasMoreElements()) + { + try + { + CertId certId = (CertId)cs.nextElement(); + Certificate cert = (Certificate)chainCerts.get(certId); + + if (doneCerts.get(cert) != null) + { + continue; + } + + CertBag cBag = new CertBag( + x509Certificate, + new DEROctetString(cert.getEncoded())); + ASN1EncodableVector fName = new ASN1EncodableVector(); + + if (cert instanceof PKCS12BagAttributeCarrier) + { + PKCS12BagAttributeCarrier bagAttrs = (PKCS12BagAttributeCarrier)cert; + Enumeration e = bagAttrs.getBagAttributeKeys(); + + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + + // a certificate not immediately linked to a key doesn't require + // a localKeyID and will confuse some PKCS12 implementations. + // + // If we find one, we'll prune it out. + if (oid.equals(PKCSObjectIdentifiers.pkcs_9_at_localKeyId)) + { + continue; + } + + ASN1EncodableVector fSeq = new ASN1EncodableVector(); + + fSeq.add(oid); + fSeq.add(new DERSet(bagAttrs.getBagAttribute(oid))); + fName.add(new DERSequence(fSeq)); + } + } + + SafeBag sBag = new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName)); + + certSeq.add(sBag); + } + catch (CertificateEncodingException e) + { + throw new IOException("Error encoding certificate: " + e.toString()); + } + } + + byte[] certSeqEncoded = new DERSequence(certSeq).getEncoded(ASN1Encoding.DER); + byte[] certBytes = cryptData(true, cAlgId, password, false, certSeqEncoded); + EncryptedData cInfo = new EncryptedData(data, cAlgId, new BEROctetString(certBytes)); + + ContentInfo[] info = new ContentInfo[] + { + new ContentInfo(data, keyString), + new ContentInfo(encryptedData, cInfo.toASN1Primitive()) + }; + + AuthenticatedSafe auth = new AuthenticatedSafe(info); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream asn1Out; + if (useDEREncoding) + { + asn1Out = new DEROutputStream(bOut); + } + else + { + asn1Out = new BEROutputStream(bOut); + } + + asn1Out.writeObject(auth); + + byte[] pkg = bOut.toByteArray(); + + ContentInfo mainInfo = new ContentInfo(data, new BEROctetString(pkg)); + + // + // create the mac + // + byte[] mSalt = new byte[20]; + int itCount = MIN_ITERATIONS; + + random.nextBytes(mSalt); + + byte[] data = ((ASN1OctetString)mainInfo.getContent()).getOctets(); + + MacData mData; + + try + { + byte[] res = calculatePbeMac(id_SHA1, mSalt, itCount, password, false, data); + + AlgorithmIdentifier algId = new AlgorithmIdentifier(id_SHA1, DERNull.INSTANCE); + DigestInfo dInfo = new DigestInfo(algId, res); + + mData = new MacData(dInfo, mSalt, itCount); + } + catch (Exception e) + { + throw new IOException("error constructing MAC: " + e.toString()); + } + + // + // output the Pfx + // + Pfx pfx = new Pfx(mainInfo, mData); + + if (useDEREncoding) + { + asn1Out = new DEROutputStream(stream); + } + else + { + asn1Out = new BEROutputStream(stream); + } + + asn1Out.writeObject(pfx); + } + + private static byte[] calculatePbeMac( + ASN1ObjectIdentifier oid, + byte[] salt, + int itCount, + char[] password, + boolean wrongPkcs12Zero, + byte[] data) + throws Exception + { + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(oid.getId(), bcProvider); + PBEParameterSpec defParams = new PBEParameterSpec(salt, itCount); + PBEKeySpec pbeSpec = new PBEKeySpec(password); + BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec); + key.setTryWrongPKCS12Zero(wrongPkcs12Zero); + + Mac mac = Mac.getInstance(oid.getId(), bcProvider); + mac.init(key, defParams); + mac.update(data); + return mac.doFinal(); + } + + public static class BCPKCS12KeyStore + extends PKCS12KeyStoreSpi + { + public BCPKCS12KeyStore() + { + super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC); + } + } + + public static class BCPKCS12KeyStore3DES + extends PKCS12KeyStoreSpi + { + public BCPKCS12KeyStore3DES() + { + super(bcProvider, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC); + } + } + + public static class DefPKCS12KeyStore + extends PKCS12KeyStoreSpi + { + public DefPKCS12KeyStore() + { + super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd40BitRC2_CBC); + } + } + + public static class DefPKCS12KeyStore3DES + extends PKCS12KeyStoreSpi + { + public DefPKCS12KeyStore3DES() + { + super(null, pbeWithSHAAnd3_KeyTripleDES_CBC, pbeWithSHAAnd3_KeyTripleDES_CBC); + } + } + + private static class IgnoresCaseHashtable + { + private Hashtable orig = new Hashtable(); + private Hashtable keys = new Hashtable(); + + public void put(String key, Object value) + { + String lower = (key == null) ? null : Strings.toLowerCase(key); + String k = (String)keys.get(lower); + if (k != null) + { + orig.remove(k); + } + + keys.put(lower, key); + orig.put(key, value); + } + + public Enumeration keys() + { + return orig.keys(); + } + + public Object remove(String alias) + { + String k = (String)keys.remove(alias == null ? null : Strings.toLowerCase(alias)); + if (k == null) + { + return null; + } + + return orig.remove(k); + } + + public Object get(String alias) + { + String k = (String)keys.get(alias == null ? null : Strings.toLowerCase(alias)); + if (k == null) + { + return null; + } + + return orig.get(k); + } + + public Enumeration elements() + { + return orig.elements(); + } + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jce/interfaces/ECKey.java b/prov/src/main/jdk1.4/org/spongycastle/jce/interfaces/ECKey.java new file mode 100644 index 00000000..1cee721d --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jce/interfaces/ECKey.java @@ -0,0 +1,22 @@ +package org.spongycastle.jce.interfaces; + +import org.spongycastle.jce.spec.ECParameterSpec; + +/** + * generic interface for an Elliptic Curve Key. + */ +public interface ECKey +{ + /** + * return a parameter specification representing the EC domain parameters + * for the key. + * @deprecated this method vanises in JDK 1.5. Use getParameters(). + */ + public ECParameterSpec getParams(); + + /** + * return a parameter specification representing the EC domain parameters + * for the key. + */ + public ECParameterSpec getParameters(); +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/jce/provider/BouncyCastleProviderConfiguration.java b/prov/src/main/jdk1.4/org/spongycastle/jce/provider/BouncyCastleProviderConfiguration.java new file mode 100644 index 00000000..17adb311 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jce/provider/BouncyCastleProviderConfiguration.java @@ -0,0 +1,166 @@ +package org.spongycastle.jce.provider; + +import java.security.Permission; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jcajce.provider.config.ProviderConfigurationPermission; +import org.spongycastle.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 + { + throw new IllegalArgumentException("not a valid ECParameterSpec"); + } + + if (curveSpec == null) + { + ecThreadSpec.set(null); + } + 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 + { + throw new IllegalArgumentException("not a valid ECParameterSpec"); + } + } + 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.set(null); + } + 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/jdk1.4/org/spongycastle/jce/provider/CertPathValidatorUtilities.java b/prov/src/main/jdk1.4/org/spongycastle/jce/provider/CertPathValidatorUtilities.java new file mode 100644 index 00000000..e90b3895 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jce/provider/CertPathValidatorUtilities.java @@ -0,0 +1,1439 @@ +package org.spongycastle.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.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1OutputStream; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.ASN1Enumerated; +import org.spongycastle.asn1.ASN1GeneralizedTime; +import org.spongycastle.asn1.DERIA5String; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.isismtt.ISISMTTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.CRLDistPoint; +import org.spongycastle.asn1.x509.CRLReason; +import org.spongycastle.asn1.x509.CertificateList; +import org.spongycastle.asn1.x509.DistributionPoint; +import org.spongycastle.asn1.x509.DistributionPointName; +import org.spongycastle.asn1.x509.GeneralName; +import org.spongycastle.asn1.x509.GeneralNames; +import org.spongycastle.asn1.x509.PolicyInformation; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x509.X509Extension; +import org.spongycastle.asn1.x509.X509Extensions; +import org.spongycastle.jce.X509LDAPCertStoreParameters; +import org.spongycastle.jce.exception.ExtCertPathValidatorException; +import org.spongycastle.util.Selector; +import org.spongycastle.util.StoreException; +import org.spongycastle.util.Integers; +import org.spongycastle.x509.ExtendedPKIXBuilderParameters; +import org.spongycastle.x509.ExtendedPKIXParameters; +import org.spongycastle.x509.X509AttributeCertStoreSelector; +import org.spongycastle.x509.X509AttributeCertificate; +import org.spongycastle.x509.X509CRLStoreSelector; +import org.spongycastle.x509.X509CertStoreSelector; +import org.spongycastle.x509.X509Store; + +public class CertPathValidatorUtilities +{ + protected static final PKIXCRLUtil CRL_UTIL = new PKIXCRLUtil(); + + protected static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId(); + protected static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId(); + protected static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId(); + protected static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId(); + protected static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId(); + protected static final String KEY_USAGE = X509Extensions.KeyUsage.getId(); + protected static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId(); + protected static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId(); + protected static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId(); + protected static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId(); + protected static final String FRESHEST_CRL = X509Extensions.FreshestCRL.getId(); + protected static final String CRL_DISTRIBUTION_POINTS = X509Extensions.CRLDistributionPoints.getId(); + protected static final String AUTHORITY_KEY_IDENTIFIER = X509Extensions.AuthorityKeyIdentifier.getId(); + + protected static final String ANY_POLICY = "2.5.29.32.0"; + + protected static final String CRL_NUMBER = X509Extensions.CRLNumber.getId(); + + /* + * key usage bits + */ + protected static final int KEY_CERT_SIGN = 5; + protected static final int CRL_SIGN = 6; + + protected static final String[] crlReasons = new String[]{ + "unspecified", + "keyCompromise", + "cACompromise", + "affiliationChanged", + "superseded", + "cessationOfOperation", + "certificateHold", + "unknown", + "removeFromCRL", + "privilegeWithdrawn", + "aACompromise"}; + + /** + * Search the given Set of TrustAnchor's for one that is the + * issuer of the given X509 certificate. Uses the default provider + * for signature verification. + * + * @param cert the X509 certificate + * @param trustAnchors a Set of TrustAnchor's + * @return the <code>TrustAnchor</code> object if found or + * <code>null</code> if not. + * @throws AnnotatedException if a TrustAnchor was found but the signature verification + * on the given certificate has thrown an exception. + */ + protected static TrustAnchor findTrustAnchor( + X509Certificate cert, + Set trustAnchors) + throws AnnotatedException + { + return findTrustAnchor(cert, trustAnchors, null); + } + + /** + * Search the given Set of TrustAnchor's for one that is the + * issuer of the given X509 certificate. Uses the specified + * provider for signature verification, or the default provider + * if null. + * + * @param cert the X509 certificate + * @param trustAnchors a Set of TrustAnchor's + * @param sigProvider the provider to use for signature verification + * @return the <code>TrustAnchor</code> object if found or + * <code>null</code> if not. + * @throws AnnotatedException if a TrustAnchor was found but the signature verification + * on the given certificate has thrown an exception. + */ + protected static TrustAnchor findTrustAnchor( + X509Certificate cert, + Set trustAnchors, + String sigProvider) + throws AnnotatedException + { + TrustAnchor trust = null; + PublicKey trustPublicKey = null; + Exception invalidKeyEx = null; + + X509CertSelector certSelectX509 = new X509CertSelector(); + X500Principal certIssuer = getEncodedIssuerPrincipal(cert); + + try + { + certSelectX509.setSubject(certIssuer.getEncoded()); + } + catch (IOException ex) + { + throw new AnnotatedException("Cannot set subject search criteria for trust anchor.", ex); + } + + Iterator iter = trustAnchors.iterator(); + while (iter.hasNext() && trust == null) + { + trust = (TrustAnchor)iter.next(); + if (trust.getTrustedCert() != null) + { + if (certSelectX509.match(trust.getTrustedCert())) + { + trustPublicKey = trust.getTrustedCert().getPublicKey(); + } + else + { + trust = null; + } + } + else if (trust.getCAName() != null + && trust.getCAPublicKey() != null) + { + try + { + X500Principal caName = new X500Principal(trust.getCAName()); + if (certIssuer.equals(caName)) + { + trustPublicKey = trust.getCAPublicKey(); + } + else + { + trust = null; + } + } + catch (IllegalArgumentException ex) + { + trust = null; + } + } + else + { + trust = null; + } + + if (trustPublicKey != null) + { + try + { + verifyX509Certificate(cert, trustPublicKey, sigProvider); + } + catch (Exception ex) + { + invalidKeyEx = ex; + trust = null; + trustPublicKey = null; + } + } + } + + if (trust == null && invalidKeyEx != null) + { + throw new AnnotatedException("TrustAnchor found but certificate validation failed.", invalidKeyEx); + } + + return trust; + } + + protected static void addAdditionalStoresFromAltNames( + X509Certificate cert, + ExtendedPKIXParameters pkixParams) + throws CertificateParsingException + { + // if in the IssuerAltName extension an URI + // is given, add an additinal X.509 store + if (cert.getIssuerAlternativeNames() != null) + { + Iterator it = cert.getIssuerAlternativeNames().iterator(); + while (it.hasNext()) + { + // look for URI + List list = (List)it.next(); + 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, + ASN1ObjectIdentifier 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, + ASN1ObjectIdentifier _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.spongycastle.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) + { + if (!(crl instanceof X509CRLObject)) + { + try + { + crl = new X509CRLObject(CertificateList.getInstance(crl.getEncoded())); + } + catch (CRLException exception) + { + throw new AnnotatedException("Failed to recode indirect CRL.", exception); + } + } + + crl_entry = crl.getRevokedCertificate(getSerialNumber(cert)); + + if (crl_entry == null) + { + return; + } + + X500Principal certIssuer = ((X509CRLEntryObject)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; + } + } + + ASN1Enumerated reasonCode = null; + if (crl_entry.hasExtensions()) + { + try + { + reasonCode = ASN1Enumerated + .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.spongycastle.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) + { + ASN1GeneralizedTime dateOfCertgen = null; + try + { + byte[] extBytes = ((X509Certificate)certPath.getCertificates().get(index - 1)).getExtensionValue(ISISMTTObjectIdentifiers.id_isismtt_at_dateOfCertGen.getId()); + if (extBytes != null) + { + dateOfCertgen = ASN1GeneralizedTime.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/jdk1.4/org/spongycastle/jce/provider/X509SignatureUtil.java b/prov/src/main/jdk1.4/org/spongycastle/jce/provider/X509SignatureUtil.java new file mode 100644 index 00000000..93cce7a4 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/jce/provider/X509SignatureUtil.java @@ -0,0 +1,125 @@ +package org.spongycastle.jce.provider; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Null; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.spongycastle.asn1.nist.NISTObjectIdentifiers; +import org.spongycastle.asn1.oiw.OIWObjectIdentifiers; +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.pkcs.RSASSAPSSparams; +import org.spongycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; + +class X509SignatureUtil +{ + private static final ASN1Null derNull = new DERNull(); + + 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.getDERObject().getDEREncoded()); + } + catch (IOException e) + { + throw new SignatureException("IOException decoding parameters: " + e.getMessage()); + } + + try + { + signature.setParameters(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"; + } + } + + 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( + ASN1ObjectIdentifier digestAlgOID) + { + if (PKCSObjectIdentifiers.md5.equals(digestAlgOID)) + { + return "MD5"; + } + else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID)) + { + return "SHA512"; + } + 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/jdk1.4/org/spongycastle/x509/X509CRLStoreSelector.java b/prov/src/main/jdk1.4/org/spongycastle/x509/X509CRLStoreSelector.java new file mode 100644 index 00000000..4748bfc5 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/x509/X509CRLStoreSelector.java @@ -0,0 +1,330 @@ +package org.spongycastle.x509; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.x509.X509Extensions; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Selector; +import org.spongycastle.x509.extension.X509ExtensionUtil; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.CRL; +import java.security.cert.X509CRL; +import java.security.cert.X509CRLSelector; + +/** + * This class is a Selector implementation for X.509 certificate revocation + * lists. + * + * @see org.spongycastle.util.Selector + * @see org.spongycastle.x509.X509Store + * @see org.spongycastle.jce.provider.X509StoreCRLCollection + */ +public class X509CRLStoreSelector + extends X509CRLSelector + implements Selector +{ + private boolean deltaCRLIndicator = false; + + private boolean completeCRLEnabled = false; + + private BigInteger maxBaseCRLNumber = null; + + private byte[] issuingDistributionPoint = null; + + private boolean issuingDistributionPointEnabled = false; + + private X509AttributeCertificate attrCertChecking; + + /** + * Returns if the issuing distribution point criteria should be applied. + * Defaults to <code>false</code>. + * <p> + * You may also set the issuing distribution point criteria if not a missing + * issuing distribution point should be assumed. + * + * @return Returns if the issuing distribution point check is enabled. + */ + public boolean isIssuingDistributionPointEnabled() + { + return issuingDistributionPointEnabled; + } + + /** + * Enables or disables the issuing distribution point check. + * + * @param issuingDistributionPointEnabled <code>true</code> to enable the + * issuing distribution point check. + */ + public void setIssuingDistributionPointEnabled( + boolean issuingDistributionPointEnabled) + { + this.issuingDistributionPointEnabled = issuingDistributionPointEnabled; + } + + /** + * Sets the attribute certificate being checked. This is not a criterion. + * Rather, it is optional information that may help a {@link X509Store} find + * CRLs that would be relevant when checking revocation for the specified + * attribute certificate. If <code>null</code> is specified, then no such + * optional information is provided. + * + * @param attrCert the <code>X509AttributeCertificate</code> being checked (or + * <code>null</code>) + * @see #getAttrCertificateChecking() + */ + public void setAttrCertificateChecking(X509AttributeCertificate attrCert) + { + attrCertChecking = attrCert; + } + + /** + * Returns the attribute certificate being checked. + * + * @return Returns the attribute certificate being checked. + * @see #setAttrCertificateChecking(X509AttributeCertificate) + */ + public X509AttributeCertificate getAttrCertificateChecking() + { + return attrCertChecking; + } + + public boolean match(Object obj) + { + if (!(obj instanceof X509CRL)) + { + return false; + } + X509CRL crl = (X509CRL)obj; + ASN1Integer dci = null; + try + { + byte[] bytes = crl + .getExtensionValue(X509Extensions.DeltaCRLIndicator.getId()); + if (bytes != null) + { + dci = ASN1Integer.getInstance(X509ExtensionUtil + .fromExtensionValue(bytes)); + } + } + catch (Exception e) + { + return false; + } + if (isDeltaCRLIndicatorEnabled()) + { + if (dci == null) + { + return false; + } + } + if (isCompleteCRLEnabled()) + { + if (dci != null) + { + return false; + } + } + if (dci != null) + { + + if (maxBaseCRLNumber != null) + { + if (dci.getPositiveValue().compareTo(maxBaseCRLNumber) == 1) + { + return false; + } + } + } + if (issuingDistributionPointEnabled) + { + byte[] idp = crl + .getExtensionValue(X509Extensions.IssuingDistributionPoint + .getId()); + if (issuingDistributionPoint == null) + { + if (idp != null) + { + return false; + } + } + else + { + if (!Arrays.areEqual(idp, issuingDistributionPoint)) + { + return false; + } + } + + } + return super.match((X509CRL)obj); + } + + public boolean match(CRL crl) + { + return match((Object)crl); + } + + /** + * Returns if this selector must match CRLs with the delta CRL indicator + * extension set. Defaults to <code>false</code>. + * + * @return Returns <code>true</code> if only CRLs with the delta CRL + * indicator extension are selected. + */ + public boolean isDeltaCRLIndicatorEnabled() + { + return deltaCRLIndicator; + } + + /** + * If this is set to <code>true</code> the CRL reported contains the delta + * CRL indicator CRL extension. + * <p> + * {@link #setCompleteCRLEnabled(boolean)} and + * {@link #setDeltaCRLIndicatorEnabled(boolean)} excluded each other. + * + * @param deltaCRLIndicator <code>true</code> if the delta CRL indicator + * extension must be in the CRL. + */ + public void setDeltaCRLIndicatorEnabled(boolean deltaCRLIndicator) + { + this.deltaCRLIndicator = deltaCRLIndicator; + } + + /** + * Returns an instance of this from a <code>X509CRLSelector</code>. + * + * @param selector A <code>X509CRLSelector</code> instance. + * @return An instance of an <code>X509CRLStoreSelector</code>. + * @exception IllegalArgumentException if selector is null or creation + * fails. + */ + public static X509CRLStoreSelector getInstance(X509CRLSelector selector) + { + if (selector == null) + { + throw new IllegalArgumentException( + "cannot create from null selector"); + } + X509CRLStoreSelector cs = new X509CRLStoreSelector(); + cs.setCertificateChecking(selector.getCertificateChecking()); + cs.setDateAndTime(selector.getDateAndTime()); + try + { + cs.setIssuerNames(selector.getIssuerNames()); + } + catch (IOException e) + { + // cannot happen + throw new IllegalArgumentException(e.getMessage()); + } + //cs.setIssuers(selector.getIssuers()); + cs.setMaxCRLNumber(selector.getMaxCRL()); + cs.setMinCRLNumber(selector.getMinCRL()); + return cs; + } + + public Object clone() + { + X509CRLStoreSelector sel = X509CRLStoreSelector.getInstance(this); + sel.deltaCRLIndicator = deltaCRLIndicator; + sel.completeCRLEnabled = completeCRLEnabled; + sel.maxBaseCRLNumber = maxBaseCRLNumber; + sel.attrCertChecking = attrCertChecking; + sel.issuingDistributionPointEnabled = issuingDistributionPointEnabled; + sel.issuingDistributionPoint = Arrays.clone(issuingDistributionPoint); + return sel; + } + + /** + * If <code>true</code> only complete CRLs are returned. Defaults to + * <code>false</code>. + * + * @return <code>true</code> if only complete CRLs are returned. + */ + public boolean isCompleteCRLEnabled() + { + return completeCRLEnabled; + } + + /** + * If set to <code>true</code> only complete CRLs are returned. + * <p> + * {@link #setCompleteCRLEnabled(boolean)} and + * {@link #setDeltaCRLIndicatorEnabled(boolean)} excluded each other. + * + * @param completeCRLEnabled <code>true</code> if only complete CRLs + * should be returned. + */ + public void setCompleteCRLEnabled(boolean completeCRLEnabled) + { + this.completeCRLEnabled = completeCRLEnabled; + } + + /** + * Get the maximum base CRL number. Defaults to <code>null</code>. + * + * @return Returns the maximum base CRL number. + * @see #setMaxBaseCRLNumber(BigInteger) + */ + public BigInteger getMaxBaseCRLNumber() + { + return maxBaseCRLNumber; + } + + /** + * Sets the maximum base CRL number. Setting to <code>null</code> disables + * this cheack. + * <p> + * This is only meaningful for delta CRLs. Complete CRLs must have a CRL + * number which is greater or equal than the base number of the + * corresponding CRL. + * + * @param maxBaseCRLNumber The maximum base CRL number to set. + */ + public void setMaxBaseCRLNumber(BigInteger maxBaseCRLNumber) + { + this.maxBaseCRLNumber = maxBaseCRLNumber; + } + + /** + * Returns the issuing distribution point. Defaults to <code>null</code>, + * which is a missing issuing distribution point extension. + * <p> + * The internal byte array is cloned before it is returned. + * <p> + * The criteria must be enable with + * {@link #setIssuingDistributionPointEnabled(boolean)}. + * + * @return Returns the issuing distribution point. + * @see #setIssuingDistributionPoint(byte[]) + */ + public byte[] getIssuingDistributionPoint() + { + return Arrays.clone(issuingDistributionPoint); + } + + /** + * Sets the issuing distribution point. + * <p> + * The issuing distribution point extension is a CRL extension which + * identifies the scope and the distribution point of a CRL. The scope + * contains among others information about revocation reasons contained in + * the CRL. Delta CRLs and complete CRLs must have matching issuing + * distribution points. + * <p> + * The byte array is cloned to protect against subsequent modifications. + * <p> + * You must also enable or disable this criteria with + * {@link #setIssuingDistributionPointEnabled(boolean)}. + * + * @param issuingDistributionPoint The issuing distribution point to set. + * This is the DER encoded OCTET STRING extension value. + * @see #getIssuingDistributionPoint() + */ + public void setIssuingDistributionPoint(byte[] issuingDistributionPoint) + { + this.issuingDistributionPoint = Arrays.clone(issuingDistributionPoint); + } +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/x509/X509CertStoreSelector.java b/prov/src/main/jdk1.4/org/spongycastle/x509/X509CertStoreSelector.java new file mode 100644 index 00000000..61664c44 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/x509/X509CertStoreSelector.java @@ -0,0 +1,86 @@ +package org.spongycastle.x509; + +import org.spongycastle.util.Selector; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; + +/** + * This class is a Selector implementation for X.509 certificates. + * + * @see org.spongycastle.util.Selector + * @see org.spongycastle.x509.X509Store + * @see org.spongycastle.jce.provider.X509StoreCertCollection + */ +public class X509CertStoreSelector + extends X509CertSelector + implements Selector +{ + public boolean match(Object obj) + { + if (!(obj instanceof X509Certificate)) + { + return false; + } + + X509Certificate other = (X509Certificate)obj; + + return super.match(other); + } + + public boolean match(Certificate cert) + { + return match((Object)cert); + } + + public Object clone() + { + X509CertStoreSelector selector = (X509CertStoreSelector)super.clone(); + + return selector; + } + + /** + * Returns an instance of this from a <code>X509CertSelector</code>. + * + * @param selector A <code>X509CertSelector</code> instance. + * @return An instance of an <code>X509CertStoreSelector</code>. + * @exception IllegalArgumentException if selector is null or creation fails. + */ + public static X509CertStoreSelector getInstance(X509CertSelector selector) + { + if (selector == null) + { + throw new IllegalArgumentException("cannot create from null selector"); + } + X509CertStoreSelector cs = new X509CertStoreSelector(); + cs.setAuthorityKeyIdentifier(selector.getAuthorityKeyIdentifier()); + cs.setBasicConstraints(selector.getBasicConstraints()); + cs.setCertificate(selector.getCertificate()); + cs.setCertificateValid(selector.getCertificateValid()); + cs.setMatchAllSubjectAltNames(selector.getMatchAllSubjectAltNames()); + try + { + cs.setPathToNames(selector.getPathToNames()); + cs.setExtendedKeyUsage(selector.getExtendedKeyUsage()); + cs.setNameConstraints(selector.getNameConstraints()); + cs.setPolicy(selector.getPolicy()); + cs.setSubjectPublicKeyAlgID(selector.getSubjectPublicKeyAlgID()); + cs.setIssuer(selector.getIssuerAsBytes()); + cs.setSubject(selector.getSubjectAsBytes()); + } + catch (IOException e) + { + throw new IllegalArgumentException("error in passed in selector: " + e); + } + cs.setKeyUsage(selector.getKeyUsage()); + cs.setPrivateKeyValid(selector.getPrivateKeyValid()); + cs.setSerialNumber(selector.getSerialNumber()); + cs.setSubjectKeyIdentifier(selector.getSubjectKeyIdentifier()); + cs.setSubjectPublicKey(selector.getSubjectPublicKey()); + return cs; + } + +} diff --git a/prov/src/main/jdk1.4/org/spongycastle/x509/util/LDAPStoreHelper.java b/prov/src/main/jdk1.4/org/spongycastle/x509/util/LDAPStoreHelper.java new file mode 100644 index 00000000..b78c4a76 --- /dev/null +++ b/prov/src/main/jdk1.4/org/spongycastle/x509/util/LDAPStoreHelper.java @@ -0,0 +1,1118 @@ +package org.spongycastle.x509.util; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.Principal; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.sql.Date; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +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.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.x509.Certificate; +import org.spongycastle.asn1.x509.CertificatePair; +import org.spongycastle.jce.X509LDAPCertStoreParameters; +import org.spongycastle.jce.provider.X509AttrCertParser; +import org.spongycastle.jce.provider.X509CRLParser; +import org.spongycastle.jce.provider.X509CertPairParser; +import org.spongycastle.jce.provider.X509CertParser; +import org.spongycastle.util.StoreException; +import org.spongycastle.x509.X509AttributeCertStoreSelector; +import org.spongycastle.x509.X509AttributeCertificate; +import org.spongycastle.x509.X509CRLStoreSelector; +import org.spongycastle.x509.X509CertPairStoreSelector; +import org.spongycastle.x509.X509CertStoreSelector; +import org.spongycastle.x509.X509CertificatePair; + +/** + * This is a general purpose implementation to get X.509 certificates, CRLs, + * attribute certificates and cross certificates from a LDAP location. + * <p/> + * At first a search is performed in the ldap*AttributeNames of the + * {@link org.spongycastle.jce.X509LDAPCertStoreParameters} with the given + * information of the subject (for all kind of certificates) or issuer (for + * CRLs), respectively, if a {@link org.spongycastle.x509.X509CertStoreSelector} or + * {@link org.spongycastle.x509.X509AttributeCertificate} is given with that + * details. + * <p/> + * For the used schemes see: + * <ul> + * <li><a href="http://www.ietf.org/rfc/rfc2587.txt">RFC 2587</a> + * <li><a + * href="http://www3.ietf.org/proceedings/01mar/I-D/pkix-ldap-schema-01.txt">Internet + * X.509 Public Key Infrastructure Additional LDAP Schema for PKIs and PMIs</a> + * </ul> + */ +public class LDAPStoreHelper +{ + + // TODO: cache results + + private X509LDAPCertStoreParameters params; + + public LDAPStoreHelper(X509LDAPCertStoreParameters params) + { + this.params = 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 dNAttributeName) + { + String temp = subject; + int begin = temp.toLowerCase().indexOf( + dNAttributeName.toLowerCase() + "="); + if (begin == -1) + { + return ""; + } + temp = temp.substring(begin + dNAttributeName.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; + } + + private Set createCerts(List list, X509CertStoreSelector xselector) + throws StoreException + { + Set certSet = new HashSet(); + + Iterator it = list.iterator(); + X509CertParser parser = new X509CertParser(); + while (it.hasNext()) + { + try + { + parser.engineInit(new ByteArrayInputStream((byte[])it + .next())); + X509Certificate cert = (X509Certificate)parser + .engineRead(); + if (xselector.match((Object)cert)) + { + certSet.add(cert); + } + + } + catch (Exception e) + { + + } + } + + return certSet; + } + + /** + * Can use the subject and serial and the subject and serialNumber of the + * certificate of the given of the X509CertStoreSelector. If a certificate + * for checking is given this has higher precedence. + * + * @param xselector The selector with the search criteria. + * @param attrs Attributes which contain the certificates in the LDAP + * directory. + * @param attrNames Attribute names in teh LDAP directory which correspond to the + * subjectAttributeNames. + * @param subjectAttributeNames Subject attribute names (like "CN", "O", "OU") to use to + * search in the LDAP directory + * @return A list of found DER encoded certificates. + * @throws StoreException if an error occurs while searching. + */ + private List certSubjectSerialSearch(X509CertStoreSelector xselector, + String[] attrs, String attrNames[], String subjectAttributeNames[]) + throws StoreException + { + // TODO: support also subjectAltNames? + List list = new ArrayList(); + + String subject = null; + String serial = null; + + subject = getSubjectAsString(xselector); + + if (xselector.getSerialNumber() != null) + { + serial = xselector.getSerialNumber().toString(); + } + if (xselector.getCertificate() != null) + { + subject = xselector.getCertificate().getSubjectX500Principal().getName("RFC1779"); + serial = xselector.getCertificate().getSerialNumber().toString(); + } + + String attrValue = null; + if (subject != null) + { + for (int i = 0; i < subjectAttributeNames.length; i++) + { + attrValue = parseDN(subject, subjectAttributeNames[i]); + list + .addAll(search(attrNames, "*" + attrValue + "*", + attrs)); + } + } + if (serial != null && params.getSearchForSerialNumberIn() != null) + { + attrValue = serial; + list.addAll(search( + splitString(params.getSearchForSerialNumberIn()), + attrValue, attrs)); + } + if (serial == null && subject == null) + { + list.addAll(search(attrNames, "*", attrs)); + } + + return list; + } + + + + /** + * Can use the subject of the forward certificate of the set certificate + * pair or the subject of the forward + * {@link org.spongycastle.x509.X509CertStoreSelector} of the given + * selector. + * + * @param xselector The selector with the search criteria. + * @param attrs Attributes which contain the attribute certificates in the + * LDAP directory. + * @param attrNames Attribute names in the LDAP directory which correspond to the + * subjectAttributeNames. + * @param subjectAttributeNames Subject attribute names (like "CN", "O", "OU") to use to + * search in the LDAP directory + * @return A list of found DER encoded certificate pairs. + * @throws StoreException if an error occurs while searching. + */ + private List crossCertificatePairSubjectSearch( + X509CertPairStoreSelector xselector, String[] attrs, + String attrNames[], String subjectAttributeNames[]) + throws StoreException + { + List list = new ArrayList(); + + // search for subject + String subject = null; + + if (xselector.getForwardSelector() != null) + { + subject = getSubjectAsString(xselector.getForwardSelector()); + } + if (xselector.getCertPair() != null) + { + if (xselector.getCertPair().getForward() != null) + { + subject = xselector.getCertPair().getForward() + .getSubjectX500Principal().getName("RFC1779"); + } + } + String attrValue = null; + if (subject != null) + { + for (int i = 0; i < subjectAttributeNames.length; i++) + { + attrValue = parseDN(subject, subjectAttributeNames[i]); + list + .addAll(search(attrNames, "*" + attrValue + "*", + attrs)); + } + } + if (subject == null) + { + list.addAll(search(attrNames, "*", attrs)); + } + + return list; + } + + /** + * Can use the entityName of the holder of the attribute certificate, the + * serialNumber of attribute certificate and the serialNumber of the + * associated certificate of the given of the X509AttributeCertSelector. + * + * @param xselector The selector with the search criteria. + * @param attrs Attributes which contain the attribute certificates in the + * LDAP directory. + * @param attrNames Attribute names in the LDAP directory which correspond to the + * subjectAttributeNames. + * @param subjectAttributeNames Subject attribute names (like "CN", "O", "OU") to use to + * search in the LDAP directory + * @return A list of found DER encoded attribute certificates. + * @throws StoreException if an error occurs while searching. + */ + private List attrCertSubjectSerialSearch( + X509AttributeCertStoreSelector xselector, String[] attrs, + String attrNames[], String subjectAttributeNames[]) + throws StoreException + { + List list = new ArrayList(); + + // search for serialNumber of associated cert, + // serialNumber of the attribute certificate or DN in the entityName + // of the holder + + String subject = null; + String serial = null; + + Collection serials = new HashSet(); + Principal principals[] = null; + if (xselector.getHolder() != null) + { + // serialNumber of associated cert + if (xselector.getHolder().getSerialNumber() != null) + { + serials.add(xselector.getHolder().getSerialNumber() + .toString()); + } + // DN in the entityName of the holder + if (xselector.getHolder().getEntityNames() != null) + { + principals = xselector.getHolder().getEntityNames(); + } + } + + if (xselector.getAttributeCert() != null) + { + if (xselector.getAttributeCert().getHolder().getEntityNames() != null) + { + principals = xselector.getAttributeCert().getHolder() + .getEntityNames(); + } + // serialNumber of the attribute certificate + serials.add(xselector.getAttributeCert().getSerialNumber() + .toString()); + } + if (principals != null) + { + // only first should be relevant + if (principals[0] instanceof X500Principal) + { + subject = ((X500Principal)principals[0]) + .getName("RFC1779"); + } + else + { + // strange ... + subject = principals[0].getName(); + } + } + if (xselector.getSerialNumber() != null) + { + serials.add(xselector.getSerialNumber().toString()); + } + + String attrValue = null; + if (subject != null) + { + for (int i = 0; i < subjectAttributeNames.length; i++) + { + attrValue = parseDN(subject, subjectAttributeNames[i]); + list + .addAll(search(attrNames, "*" + attrValue + "*", + attrs)); + } + } + if (serials.size() > 0 + && params.getSearchForSerialNumberIn() != null) + { + Iterator it = serials.iterator(); + while (it.hasNext()) + { + serial = (String)it.next(); + list.addAll(search(splitString(params.getSearchForSerialNumberIn()), serial, attrs)); + } + } + if (serials.size() == 0 && subject == null) + { + list.addAll(search(attrNames, "*", attrs)); + } + + return list; + } + + /** + * Can use the issuer of the given of the X509CRLStoreSelector. + * + * @param xselector The selector with the search criteria. + * @param attrs Attributes which contain the attribute certificates in the + * LDAP directory. + * @param attrNames Attribute names in the LDAP directory which correspond to the + * subjectAttributeNames. + * @param issuerAttributeNames Issuer attribute names (like "CN", "O", "OU") to use to search + * in the LDAP directory + * @return A list of found DER encoded CRLs. + * @throws StoreException if an error occurs while searching. + */ + private List cRLIssuerSearch(X509CRLStoreSelector xselector, + String[] attrs, String attrNames[], String issuerAttributeNames[]) + throws StoreException + { + List list = new ArrayList(); + + String issuer = null; + Collection issuers = new HashSet(); +/* + if (xselector.getIssuers() != null) + { + issuers.addAll(xselector.getIssuers()); + } +*/ + if (xselector.getCertificateChecking() != null) + { + issuers.add(getCertificateIssuer(xselector.getCertificateChecking())); + } + if (xselector.getAttrCertificateChecking() != null) + { + Principal principals[] = xselector.getAttrCertificateChecking().getIssuer().getPrincipals(); + for (int i=0; i<principals.length; i++) + { + if (principals[i] instanceof X500Principal) + { + issuers.add(principals[i]); + } + } + } + Iterator it = issuers.iterator(); + while (it.hasNext()) + { + issuer = ((X500Principal)it.next()).getName("RFC1779"); + String attrValue = null; + + for (int i = 0; i < issuerAttributeNames.length; i++) + { + attrValue = parseDN(issuer, issuerAttributeNames[i]); + list + .addAll(search(attrNames, "*" + attrValue + "*", + attrs)); + } + } + if (issuer == null) + { + list.addAll(search(attrNames, "*", attrs)); + } + + return list; + } + + /** + * Returns a <code>List</code> of encodings of the certificates, attribute + * certificates, CRL or certificate pairs. + * + * @param attributeNames The attribute names 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, + * attribute certificate, certificate pair or CRL in a found + * entry. + * @return A <code>List</code> of byte arrays with the encodings. + * @throws StoreException if an error occurs getting the results from the LDAP + * directory. + */ + private List search(String attributeNames[], String attributeValue, + String[] attrs) throws StoreException + { + String filter = null; + if (attributeNames == null) + { + filter = null; + } + else + { + filter = ""; + if (attributeValue.equals("**")) + { + attributeValue = "*"; + } + for (int i = 0; i < attributeNames.length; i++) + { + filter += "(" + attributeNames[i] + "=" + attributeValue + ")"; + } + filter = "(|" + filter + ")"; + } + String filter2 = ""; + for (int i = 0; i < attrs.length; i++) + { + filter2 += "(" + attrs[i] + "=*)"; + } + filter2 = "(|" + filter2 + ")"; + + String filter3 = "(&" + filter + "" + filter2 + ")"; + if (filter == null) + { + filter3 = filter2; + } + List list; + list = getFromCache(filter3); + if (list != null) + { + return list; + } + DirContext ctx = null; + list = new ArrayList(); + try + { + + ctx = connectLDAP(); + + SearchControls constraints = new SearchControls(); + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + constraints.setCountLimit(0); + constraints.setReturningAttributes(attrs); + NamingEnumeration results = ctx.search(params.getBaseDN(), filter3, + constraints); + while (results.hasMoreElements()) + { + SearchResult sr = (SearchResult)results.next(); + NamingEnumeration enumeration = ((Attribute)(sr + .getAttributes().getAll().next())).getAll(); + while (enumeration.hasMore()) + { + list.add(enumeration.next()); + } + } + addToCache(filter3, list); + } + catch (NamingException e) + { + // skip exception, unfortunately if an attribute type is not + // supported an exception is thrown + + } + finally + { + try + { + if (null != ctx) + { + ctx.close(); + } + } + catch (Exception e) + { + } + } + return list; + } + + private Set createCRLs(List list, X509CRLStoreSelector xselector) + throws StoreException + { + Set crlSet = new HashSet(); + + X509CRLParser parser = new X509CRLParser(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + try + { + parser.engineInit(new ByteArrayInputStream((byte[])it + .next())); + X509CRL crl = (X509CRL)parser.engineRead(); + if (xselector.match((Object)crl)) + { + crlSet.add(crl); + } + } + catch (StreamParsingException e) + { + + } + } + + return crlSet; + } + + private Set createCrossCertificatePairs(List list, + X509CertPairStoreSelector xselector) throws StoreException + { + Set certPairSet = new HashSet(); + + int i = 0; + while (i < list.size()) + { + X509CertificatePair pair; + try + { + // first try to decode it as certificate pair + try + { + X509CertPairParser parser = new X509CertPairParser(); + parser.engineInit(new ByteArrayInputStream( + (byte[])list.get(i))); + pair = (X509CertificatePair)parser.engineRead(); + } + catch (StreamParsingException e) + { + // now try it to construct it the forward and reverse + // certificate + byte[] forward = (byte[])list.get(i); + byte[] reverse = (byte[])list.get(i + 1); + pair = new X509CertificatePair(new CertificatePair( + Certificate + .getInstance(new ASN1InputStream( + forward).readObject()), + Certificate + .getInstance(new ASN1InputStream( + reverse).readObject()))); + i++; + } + if (xselector.match((Object)pair)) + { + certPairSet.add(pair); + } + } + catch (CertificateParsingException e) + { + // try next + } + catch (IOException e) + { + // try next + } + i++; + } + + return certPairSet; + } + + private Set createAttributeCertificates(List list, + X509AttributeCertStoreSelector xselector) throws StoreException + { + Set certSet = new HashSet(); + + Iterator it = list.iterator(); + X509AttrCertParser parser = new X509AttrCertParser(); + while (it.hasNext()) + { + try + { + parser.engineInit(new ByteArrayInputStream((byte[])it + .next())); + X509AttributeCertificate cert = (X509AttributeCertificate)parser + .engineRead(); + if (xselector.match((Object)cert)) + { + certSet.add(cert); + } + } + catch (StreamParsingException e) + { + + } + } + + return certSet; + } + + /** + * Returns the CRLs for issued certificates for other CAs matching the given + * selector. <br> + * The authorityRevocationList attribute includes revocation information + * regarding certificates issued to other CAs. + * + * @param selector The CRL selector to use to find the CRLs. + * @return A possible empty collection with CRLs + * @throws StoreException + */ + public Collection getAuthorityRevocationLists(X509CRLStoreSelector selector) + throws StoreException + { + String[] attrs = splitString(params.getAuthorityRevocationListAttribute()); + String attrNames[] = splitString(params + .getLdapAuthorityRevocationListAttributeName()); + String issuerAttributeNames[] = splitString(params + .getAuthorityRevocationListIssuerAttributeName()); + + List list = cRLIssuerSearch(selector, attrs, attrNames, + issuerAttributeNames); + Set resultSet = createCRLs(list, selector); + if (resultSet.size() == 0) + { + X509CRLStoreSelector emptySelector = new X509CRLStoreSelector(); + list = cRLIssuerSearch(emptySelector, attrs, attrNames, + issuerAttributeNames); + + resultSet.addAll(createCRLs(list, selector)); + } + return resultSet; + } + + /** + * Returns the revocation list for revoked attribute certificates. + * <p/> + * The attributeCertificateRevocationList holds a list of attribute + * certificates that have been revoked. + * + * @param selector The CRL selector to use to find the CRLs. + * @return A possible empty collection with CRLs. + * @throws StoreException + */ + public Collection getAttributeCertificateRevocationLists( + X509CRLStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params + .getAttributeCertificateRevocationListAttribute()); + String attrNames[] = splitString(params + .getLdapAttributeCertificateRevocationListAttributeName()); + String issuerAttributeNames[] = splitString(params + .getAttributeCertificateRevocationListIssuerAttributeName()); + + List list = cRLIssuerSearch(selector, attrs, attrNames, + issuerAttributeNames); + Set resultSet = createCRLs(list, selector); + if (resultSet.size() == 0) + { + X509CRLStoreSelector emptySelector = new X509CRLStoreSelector(); + list = cRLIssuerSearch(emptySelector, attrs, attrNames, + issuerAttributeNames); + + resultSet.addAll(createCRLs(list, selector)); + } + return resultSet; + } + + /** + * Returns the revocation list for revoked attribute certificates for an + * attribute authority + * <p/> + * The attributeAuthorityList holds a list of AA certificates that have been + * revoked. + * + * @param selector The CRL selector to use to find the CRLs. + * @return A possible empty collection with CRLs + * @throws StoreException + */ + public Collection getAttributeAuthorityRevocationLists( + X509CRLStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params.getAttributeAuthorityRevocationListAttribute()); + String attrNames[] = splitString(params + .getLdapAttributeAuthorityRevocationListAttributeName()); + String issuerAttributeNames[] = splitString(params + .getAttributeAuthorityRevocationListIssuerAttributeName()); + + List list = cRLIssuerSearch(selector, attrs, attrNames, + issuerAttributeNames); + Set resultSet = createCRLs(list, selector); + if (resultSet.size() == 0) + { + X509CRLStoreSelector emptySelector = new X509CRLStoreSelector(); + list = cRLIssuerSearch(emptySelector, attrs, attrNames, + issuerAttributeNames); + + resultSet.addAll(createCRLs(list, selector)); + } + return resultSet; + } + + /** + * Returns cross certificate pairs. + * + * @param selector The selector to use to find the cross certificates. + * @return A possible empty collection with {@link X509CertificatePair}s + * @throws StoreException + */ + public Collection getCrossCertificatePairs( + X509CertPairStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params.getCrossCertificateAttribute()); + String attrNames[] = splitString(params.getLdapCrossCertificateAttributeName()); + String subjectAttributeNames[] = splitString(params + .getCrossCertificateSubjectAttributeName()); + List list = crossCertificatePairSubjectSearch(selector, attrs, + attrNames, subjectAttributeNames); + Set resultSet = createCrossCertificatePairs(list, selector); + if (resultSet.size() == 0) + { + X509CertStoreSelector emptyCertselector = new X509CertStoreSelector(); + X509CertPairStoreSelector emptySelector = new X509CertPairStoreSelector(); + + emptySelector.setForwardSelector(emptyCertselector); + emptySelector.setReverseSelector(emptyCertselector); + list = crossCertificatePairSubjectSearch(emptySelector, attrs, + attrNames, subjectAttributeNames); + resultSet.addAll(createCrossCertificatePairs(list, selector)); + } + return resultSet; + } + + /** + * Returns end certificates. + * <p/> + * The attributeDescriptorCertificate is self signed by a source of + * authority and holds a description of the privilege and its delegation + * rules. + * + * @param selector The selector to find the certificates. + * @return A possible empty collection with certificates. + * @throws StoreException + */ + public Collection getUserCertificates(X509CertStoreSelector selector) + throws StoreException + { + String[] attrs = splitString(params.getUserCertificateAttribute()); + String attrNames[] = splitString(params.getLdapUserCertificateAttributeName()); + String subjectAttributeNames[] = splitString(params + .getUserCertificateSubjectAttributeName()); + + List list = certSubjectSerialSearch(selector, attrs, attrNames, + subjectAttributeNames); + Set resultSet = createCerts(list, selector); + if (resultSet.size() == 0) + { + X509CertStoreSelector emptySelector = new X509CertStoreSelector(); + list = certSubjectSerialSearch(emptySelector, attrs, attrNames, + subjectAttributeNames); + resultSet.addAll(createCerts(list, selector)); + } + + return resultSet; + } + + /** + * Returns attribute certificates for an attribute authority + * <p/> + * The aAcertificate holds the privileges of an attribute authority. + * + * @param selector The selector to find the attribute certificates. + * @return A possible empty collection with attribute certificates. + * @throws StoreException + */ + public Collection getAACertificates(X509AttributeCertStoreSelector selector) + throws StoreException + { + String[] attrs = splitString(params.getAACertificateAttribute()); + String attrNames[] = splitString(params.getLdapAACertificateAttributeName()); + String subjectAttributeNames[] = splitString(params.getAACertificateSubjectAttributeName()); + + List list = attrCertSubjectSerialSearch(selector, attrs, attrNames, + subjectAttributeNames); + Set resultSet = createAttributeCertificates(list, selector); + if (resultSet.size() == 0) + { + X509AttributeCertStoreSelector emptySelector = new X509AttributeCertStoreSelector(); + list = attrCertSubjectSerialSearch(emptySelector, attrs, attrNames, + subjectAttributeNames); + resultSet.addAll(createAttributeCertificates(list, selector)); + } + + return resultSet; + } + + /** + * Returns an attribute certificate for an authority + * <p/> + * The attributeDescriptorCertificate is self signed by a source of + * authority and holds a description of the privilege and its delegation + * rules. + * + * @param selector The selector to find the attribute certificates. + * @return A possible empty collection with attribute certificates. + * @throws StoreException + */ + public Collection getAttributeDescriptorCertificates( + X509AttributeCertStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params.getAttributeDescriptorCertificateAttribute()); + String attrNames[] = splitString(params + .getLdapAttributeDescriptorCertificateAttributeName()); + String subjectAttributeNames[] = splitString(params + .getAttributeDescriptorCertificateSubjectAttributeName()); + + List list = attrCertSubjectSerialSearch(selector, attrs, attrNames, + subjectAttributeNames); + Set resultSet = createAttributeCertificates(list, selector); + if (resultSet.size() == 0) + { + X509AttributeCertStoreSelector emptySelector = new X509AttributeCertStoreSelector(); + list = attrCertSubjectSerialSearch(emptySelector, attrs, attrNames, + subjectAttributeNames); + resultSet.addAll(createAttributeCertificates(list, selector)); + } + + return resultSet; + } + + /** + * Returns CA certificates. + * <p/> + * The cACertificate attribute of a CA's directory entry shall be used to + * store self-issued certificates (if any) and certificates issued to this + * CA by CAs in the same realm as this CA. + * + * @param selector The selector to find the certificates. + * @return A possible empty collection with certificates. + * @throws StoreException + */ + public Collection getCACertificates(X509CertStoreSelector selector) + throws StoreException + { + String[] attrs = splitString(params.getCACertificateAttribute()); + String attrNames[] = splitString(params.getLdapCACertificateAttributeName()); + String subjectAttributeNames[] = splitString(params + .getCACertificateSubjectAttributeName()); + List list = certSubjectSerialSearch(selector, attrs, attrNames, + subjectAttributeNames); + Set resultSet = createCerts(list, selector); + if (resultSet.size() == 0) + { + X509CertStoreSelector emptySelector = new X509CertStoreSelector(); + list = certSubjectSerialSearch(emptySelector, attrs, attrNames, + subjectAttributeNames); + resultSet.addAll(createCerts(list, selector)); + } + return resultSet; + } + + /** + * Returns the delta revocation list for revoked certificates. + * + * @param selector The CRL selector to use to find the CRLs. + * @return A possible empty collection with CRLs. + * @throws StoreException + */ + public Collection getDeltaCertificateRevocationLists( + X509CRLStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params.getDeltaRevocationListAttribute()); + String attrNames[] = splitString(params.getLdapDeltaRevocationListAttributeName()); + String issuerAttributeNames[] = splitString(params + .getDeltaRevocationListIssuerAttributeName()); + List list = cRLIssuerSearch(selector, attrs, attrNames, + issuerAttributeNames); + Set resultSet = createCRLs(list, selector); + if (resultSet.size() == 0) + { + X509CRLStoreSelector emptySelector = new X509CRLStoreSelector(); + list = cRLIssuerSearch(emptySelector, attrs, attrNames, + issuerAttributeNames); + + resultSet.addAll(createCRLs(list, selector)); + } + return resultSet; + } + + /** + * Returns an attribute certificate for an user. + * <p/> + * The attributeCertificateAttribute holds the privileges of a user + * + * @param selector The selector to find the attribute certificates. + * @return A possible empty collection with attribute certificates. + * @throws StoreException + */ + public Collection getAttributeCertificateAttributes( + X509AttributeCertStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params.getAttributeCertificateAttributeAttribute()); + String attrNames[] = splitString(params + .getLdapAttributeCertificateAttributeAttributeName()); + String subjectAttributeNames[] = splitString(params + .getAttributeCertificateAttributeSubjectAttributeName()); + List list = attrCertSubjectSerialSearch(selector, attrs, attrNames, + subjectAttributeNames); + Set resultSet = createAttributeCertificates(list, selector); + if (resultSet.size() == 0) + { + X509AttributeCertStoreSelector emptySelector = new X509AttributeCertStoreSelector(); + list = attrCertSubjectSerialSearch(emptySelector, attrs, attrNames, + subjectAttributeNames); + resultSet.addAll(createAttributeCertificates(list, selector)); + } + + return resultSet; + } + + /** + * Returns the certificate revocation lists for revoked certificates. + * + * @param selector The CRL selector to use to find the CRLs. + * @return A possible empty collection with CRLs. + * @throws StoreException + */ + public Collection getCertificateRevocationLists( + X509CRLStoreSelector selector) throws StoreException + { + String[] attrs = splitString(params.getCertificateRevocationListAttribute()); + String attrNames[] = splitString(params + .getLdapCertificateRevocationListAttributeName()); + String issuerAttributeNames[] = splitString(params + .getCertificateRevocationListIssuerAttributeName()); + List list = cRLIssuerSearch(selector, attrs, attrNames, + issuerAttributeNames); + Set resultSet = createCRLs(list, selector); + if (resultSet.size() == 0) + { + X509CRLStoreSelector emptySelector = new X509CRLStoreSelector(); + list = cRLIssuerSearch(emptySelector, attrs, attrNames, + issuerAttributeNames); + + resultSet.addAll(createCRLs(list, selector)); + } + return resultSet; + } + + private Map cacheMap = new HashMap(cacheSize); + + private static int cacheSize = 32; + + private static long lifeTime = 60 * 1000; + + private synchronized void addToCache(String searchCriteria, List list) + { + Date now = new Date(System.currentTimeMillis()); + List cacheEntry = new ArrayList(); + cacheEntry.add(now); + cacheEntry.add(list); + if (cacheMap.containsKey(searchCriteria)) + { + cacheMap.put(searchCriteria, cacheEntry); + } + else + { + if (cacheMap.size() >= cacheSize) + { + // replace oldest + Iterator it = cacheMap.entrySet().iterator(); + long oldest = now.getTime(); + Object replace = null; + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry)it.next(); + long current = ((Date)((List)entry.getValue()).get(0)) + .getTime(); + if (current < oldest) + { + oldest = current; + replace = entry.getKey(); + } + } + cacheMap.remove(replace); + } + cacheMap.put(searchCriteria, cacheEntry); + } + } + + private List getFromCache(String searchCriteria) + { + List entry = (List)cacheMap.get(searchCriteria); + long now = System.currentTimeMillis(); + if (entry != null) + { + // too old + if (((Date)entry.get(0)).getTime() < (now - lifeTime)) + { + return null; + } + return (List)entry.get(1); + } + return null; + } + + /* + * spilt string based on spaces + */ + private String[] splitString(String str) + { + return str.split("\\s+"); + } + + private String getSubjectAsString(X509CertStoreSelector xselector) + { + try + { + byte[] encSubject = xselector.getSubjectAsBytes(); + if (encSubject != null) + { + return new X500Principal(encSubject).getName("RFC1779"); + } + } + catch (IOException e) + { + throw new StoreException("exception processing name: " + e.getMessage(), e); + } + return null; + } + + private X500Principal getCertificateIssuer(X509Certificate cert) + { + return cert.getIssuerX500Principal(); + } +} |