Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'prov/src/main/jdk1.3/org/spongycastle/jcajce/provider')
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java201
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java428
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java397
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java379
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/SignatureUtil.java134
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java293
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java556
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java858
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java125
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java1636
-rw-r--r--prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java1031
11 files changed, 6038 insertions, 0 deletions
diff --git a/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
new file mode 100644
index 00000000..390708bc
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/AlgorithmParametersSpi.java
@@ -0,0 +1,201 @@
+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 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 (this.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
+ {
+ /**
+ * Return the PKCS#1 ASN.1 structure RSASSA-PSS-params.
+ */
+ protected byte[] engineGetEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ RSASSAPSSparams pssP = new RSASSAPSSparams(RSASSAPSSparams.DEFAULT_HASH_ALGORITHM, RSASSAPSSparams.DEFAULT_MASK_GEN_FUNCTION, new ASN1Integer(20), 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
+ {
+ throw new InvalidParameterSpecException("unknown parameter spec passed to PSS parameters object.");
+ }
+
+ protected void engineInit(
+ AlgorithmParameterSpec paramSpec)
+ throws InvalidParameterSpecException
+ {
+ throw new InvalidParameterSpecException("Not implemented");
+ }
+
+ protected void engineInit(
+ byte[] params)
+ throws IOException
+ {
+ try
+ {
+ RSASSAPSSparams pssP = RSASSAPSSparams.getInstance(params);
+
+ }
+ 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 (this.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.3/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java
new file mode 100644
index 00000000..be4083e3
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/rsa/PSSSignatureSpi.java
@@ -0,0 +1,428 @@
+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 org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+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;
+
+public class PSSSignatureSpi
+ extends Signature
+{
+ private AlgorithmParameters engineParams;
+ private AsymmetricBlockCipher signer;
+ private Digest contentDigest;
+ private Digest mgfDigest;
+ private int saltLength;
+ private byte trailer;
+ private boolean isRaw;
+ private ByteArrayOutputStream bOut;
+ private org.spongycastle.crypto.signers.PSSSigner pss;
+ private CipherParameters sigParams;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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");
+ }
+
+ sigParams = RSAUtil.generatePublicKeyParameter((RSAPublicKey)publicKey);
+
+ if (isRaw)
+ {
+ bOut = new ByteArrayOutputStream();
+ }
+ else
+ {
+ pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength);
+ pss.init(false,
+ sigParams);
+ }
+ }
+
+ protected void engineInitSign(
+ PrivateKey privateKey,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ if (!(privateKey instanceof RSAPrivateKey))
+ {
+ throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance");
+ }
+
+ sigParams = new ParametersWithRandom(RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey), random);
+
+ if (isRaw)
+ {
+ bOut = new ByteArrayOutputStream();
+ }
+ else
+ {
+ pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength);
+ pss.init(true, sigParams);
+ }
+ }
+
+ protected void engineInitSign(
+ PrivateKey privateKey)
+ throws InvalidKeyException
+ {
+ if (!(privateKey instanceof RSAPrivateKey))
+ {
+ throw new InvalidKeyException("Supplied key is not a RSAPrivateKey instance");
+ }
+
+ sigParams = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)privateKey);
+
+ if (isRaw)
+ {
+ bOut = new ByteArrayOutputStream();
+ }
+ else
+ {
+ pss = new org.spongycastle.crypto.signers.PSSSigner(signer, contentDigest, mgfDigest, saltLength);
+ pss.init(true, sigParams);
+ }
+ }
+
+ protected void engineUpdate(
+ byte b)
+ throws SignatureException
+ {
+ if (isRaw)
+ {
+ bOut.write(b);
+ }
+ else
+ {
+ pss.update(b);
+ }
+ }
+
+ protected void engineUpdate(
+ byte[] b,
+ int off,
+ int len)
+ throws SignatureException
+ {
+ if (isRaw)
+ {
+ bOut.write(b, off, len);
+ }
+ else
+ {
+ pss.update(b, off, len);
+ }
+ }
+
+ protected byte[] engineSign()
+ throws SignatureException
+ {
+ try
+ {
+ if (isRaw)
+ {
+ byte[] hash = bOut.toByteArray();
+ contentDigest = mgfDigest = guessDigest(hash.length);
+ saltLength = contentDigest.getDigestSize();
+ pss = new org.spongycastle.crypto.signers.PSSSigner(signer, new NullPssDigest(contentDigest), mgfDigest, saltLength);
+
+ pss.init(true, sigParams);
+ }
+ return pss.generateSignature();
+ }
+ catch (CryptoException e)
+ {
+ throw new SignatureException(e.getMessage());
+ }
+ }
+
+ protected boolean engineVerify(
+ byte[] sigBytes)
+ throws SignatureException
+ {
+ if (isRaw)
+ {
+ byte[] hash = bOut.toByteArray();
+ contentDigest = mgfDigest = guessDigest(hash.length);
+ saltLength = contentDigest.getDigestSize();
+ pss = new org.spongycastle.crypto.signers.PSSSigner(signer, new NullPssDigest(contentDigest), mgfDigest, saltLength);
+
+ pss.init(false, sigParams);
+
+ pss.update(hash, 0, hash.length);
+ }
+ return pss.verifySignature(sigBytes);
+ }
+
+ protected void engineSetParameter(
+ AlgorithmParameterSpec params)
+ throws InvalidParameterException
+ {
+ throw new InvalidParameterException("Only PSSParameterSpec supported");
+ }
+
+ protected AlgorithmParameters engineGetParameters()
+ {
+ 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");
+ }
+
+ private Digest guessDigest(int size)
+ {
+ switch (size)
+ {
+ case 20:
+ return new SHA1Digest();
+ case 28:
+ return new SHA224Digest();
+ case 32:
+ return new SHA256Digest();
+ case 48:
+ return new SHA384Digest();
+ case 64:
+ return new SHA512Digest();
+ }
+
+ return null;
+ }
+
+ 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.3/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
new file mode 100644
index 00000000..467893f3
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/CertificateFactory.java
@@ -0,0 +1,397 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import org.spongycastle.jce.cert.CertPath;
+import java.security.cert.CertificateException;
+import org.spongycastle.jce.cert.CertificateFactorySpi;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.ASN1TaggedObject;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.SignedData;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.jce.provider.X509CRLObject;
+import org.spongycastle.jce.provider.X509CertificateObject;
+
+/**
+ * class for dealing with X509 certificates.
+ * <p>
+ * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----"
+ * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7
+ * objects.
+ */
+public class CertificateFactory
+ extends CertificateFactorySpi
+{
+ private static final PEMUtil PEM_CERT_PARSER = new PEMUtil("CERTIFICATE");
+ private static final PEMUtil PEM_CRL_PARSER = new PEMUtil("CRL");
+
+ private ASN1Set sData = null;
+ private int sDataObjectCount = 0;
+ private InputStream currentStream = null;
+
+ private ASN1Set sCrlData = null;
+ private int sCrlDataObjectCount = 0;
+ private InputStream currentCrlStream = null;
+
+ private java.security.cert.Certificate readDERCertificate(
+ ASN1InputStream dIn)
+ throws IOException, CertificateParsingException
+ {
+ ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
+
+ if (seq.size() > 1
+ && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier)
+ {
+ if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
+ {
+ sData = SignedData.getInstance(ASN1Sequence.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates();
+
+ return getCertificate();
+ }
+ }
+
+ return new X509CertificateObject(
+ Certificate.getInstance(seq));
+ }
+
+ private java.security.cert.Certificate getCertificate()
+ throws CertificateParsingException
+ {
+ if (sData != null)
+ {
+ while (sDataObjectCount < sData.size())
+ {
+ Object obj = sData.getObjectAt(sDataObjectCount++);
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new X509CertificateObject(
+ Certificate.getInstance(obj));
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private java.security.cert.Certificate readPEMCertificate(
+ InputStream in)
+ throws IOException, CertificateParsingException
+ {
+ ASN1Sequence seq = PEM_CERT_PARSER.readPEMObject(in);
+
+ if (seq != null)
+ {
+ return new X509CertificateObject(
+ Certificate.getInstance(seq));
+ }
+
+ return null;
+ }
+
+ protected CRL createCRL(CertificateList c)
+ throws CRLException
+ {
+ return new X509CRLObject(c);
+ }
+
+ private CRL readPEMCRL(
+ InputStream in)
+ throws IOException, CRLException
+ {
+ ASN1Sequence seq = PEM_CRL_PARSER.readPEMObject(in);
+
+ if (seq != null)
+ {
+ return createCRL(
+ CertificateList.getInstance(seq));
+ }
+
+ return null;
+ }
+
+ private CRL readDERCRL(
+ ASN1InputStream aIn)
+ throws IOException, CRLException
+ {
+ ASN1Sequence seq = (ASN1Sequence)aIn.readObject();
+
+ if (seq.size() > 1
+ && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier)
+ {
+ if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData))
+ {
+ sCrlData = SignedData.getInstance(ASN1Sequence.getInstance(
+ (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs();
+
+ return getCRL();
+ }
+ }
+
+ return createCRL(
+ CertificateList.getInstance(seq));
+ }
+
+ private CRL getCRL()
+ throws CRLException
+ {
+ if (sCrlData == null || sCrlDataObjectCount >= sCrlData.size())
+ {
+ return null;
+ }
+
+ return createCRL(
+ CertificateList.getInstance(
+ sCrlData.getObjectAt(sCrlDataObjectCount++)));
+ }
+
+ /**
+ * Generates a certificate object and initializes it with the data
+ * read from the input stream inStream.
+ */
+ public java.security.cert.Certificate engineGenerateCertificate(
+ InputStream in)
+ throws CertificateException
+ {
+ if (currentStream == null)
+ {
+ currentStream = in;
+ sData = null;
+ sDataObjectCount = 0;
+ }
+ else if (currentStream != in) // reset if input stream has changed
+ {
+ currentStream = in;
+ sData = null;
+ sDataObjectCount = 0;
+ }
+
+ try
+ {
+ if (sData != null)
+ {
+ if (sDataObjectCount != sData.size())
+ {
+ return getCertificate();
+ }
+ else
+ {
+ sData = null;
+ sDataObjectCount = 0;
+ return null;
+ }
+ }
+
+ PushbackInputStream pis = new PushbackInputStream(in);
+ int tag = pis.read();
+
+ if (tag == -1)
+ {
+ return null;
+ }
+
+ pis.unread(tag);
+
+ if (tag != 0x30) // assume ascii PEM encoded.
+ {
+ return readPEMCertificate(pis);
+ }
+ else
+ {
+ return readDERCertificate(new ASN1InputStream(pis));
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ExCertificateException(e);
+ }
+ }
+
+ /**
+ * Returns a (possibly empty) collection view of the certificates
+ * read from the given input stream inStream.
+ */
+ public Collection engineGenerateCertificates(
+ InputStream inStream)
+ throws CertificateException
+ {
+ java.security.cert.Certificate cert;
+ List certs = new ArrayList();
+
+ while ((cert = engineGenerateCertificate(inStream)) != null)
+ {
+ certs.add(cert);
+ }
+
+ return certs;
+ }
+
+ /**
+ * Generates a certificate revocation list (CRL) object and initializes
+ * it with the data read from the input stream inStream.
+ */
+ public CRL engineGenerateCRL(
+ InputStream inStream)
+ throws CRLException
+ {
+ if (currentCrlStream == null)
+ {
+ currentCrlStream = inStream;
+ sCrlData = null;
+ sCrlDataObjectCount = 0;
+ }
+ else if (currentCrlStream != inStream) // reset if input stream has changed
+ {
+ currentCrlStream = inStream;
+ sCrlData = null;
+ sCrlDataObjectCount = 0;
+ }
+
+ try
+ {
+ if (sCrlData != null)
+ {
+ if (sCrlDataObjectCount != sCrlData.size())
+ {
+ return getCRL();
+ }
+ else
+ {
+ sCrlData = null;
+ sCrlDataObjectCount = 0;
+ return null;
+ }
+ }
+
+ PushbackInputStream pis = new PushbackInputStream(inStream);
+ int tag = pis.read();
+
+ if (tag == -1)
+ {
+ return null;
+ }
+
+ pis.unread(tag);
+
+ if (tag != 0x30) // assume ascii PEM encoded.
+ {
+ return readPEMCRL(pis);
+ }
+ else
+ { // lazy evaluate to help processing of large CRLs
+ return readDERCRL(new ASN1InputStream(pis, true));
+ }
+ }
+ catch (CRLException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ /**
+ * Returns a (possibly empty) collection view of the CRLs read from
+ * the given input stream inStream.
+ *
+ * The inStream may contain a sequence of DER-encoded CRLs, or
+ * a PKCS#7 CRL set. This is a PKCS#7 SignedData object, with the
+ * only signficant field being crls. In particular the signature
+ * and the contents are ignored.
+ */
+ public Collection engineGenerateCRLs(
+ InputStream inStream)
+ throws CRLException
+ {
+ CRL crl;
+ List crls = new ArrayList();
+
+ while ((crl = engineGenerateCRL(inStream)) != null)
+ {
+ crls.add(crl);
+ }
+
+ return crls;
+ }
+
+ public Iterator engineGetCertPathEncodings()
+ {
+ return null; // TODO: PKIXCertPath.certPathEncodings.iterator();
+ }
+
+ public CertPath engineGenerateCertPath(
+ InputStream inStream)
+ throws CertificateException
+ {
+ return engineGenerateCertPath(inStream, "PkiPath");
+ }
+
+ public CertPath engineGenerateCertPath(
+ InputStream inStream,
+ String encoding)
+ throws CertificateException
+ {
+ return new PKIXCertPath(inStream, encoding);
+ }
+
+ public CertPath engineGenerateCertPath(
+ List certificates)
+ throws CertificateException
+ {
+ Iterator iter = certificates.iterator();
+ Object obj;
+ while (iter.hasNext())
+ {
+ obj = iter.next();
+ if (obj != null)
+ {
+ if (!(obj instanceof X509Certificate))
+ {
+ throw new CertificateException("list contains non X509Certificate object while creating CertPath\n" + obj.toString());
+ }
+ }
+ }
+ return new PKIXCertPath(certificates);
+ }
+
+ private class ExCertificateException
+ extends CertificateException
+ {
+ private Throwable cause;
+
+ public ExCertificateException(Throwable cause)
+ {
+ this.cause = cause;
+ }
+
+ public ExCertificateException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java
new file mode 100644
index 00000000..0bc93832
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/PKIXCertPath.java
@@ -0,0 +1,379 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.security.NoSuchProviderException;
+import org.spongycastle.jce.cert.CertPath;
+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.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.spongycastle.jce.X509Principal;
+import org.spongycastle.jce.PrincipalUtil;
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.pkcs.ContentInfo;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.SignedData;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.util.io.pem.PemObject;
+import org.spongycastle.util.io.pem.PemWriter;
+
+/**
+ * CertPath implementation for X.509 certificates.
+ * <br />
+ **/
+public class PKIXCertPath
+ extends CertPath
+{
+ static final List certPathEncodings;
+
+ static
+ {
+ List encodings = new ArrayList();
+ encodings.add("PkiPath");
+ encodings.add("PEM");
+ encodings.add("PKCS7");
+ certPathEncodings = Collections.unmodifiableList(encodings);
+ }
+
+ private List certificates;
+
+ /**
+ * @param certs
+ */
+ private List sortCerts(
+ List certs)
+ {
+ try
+ {
+ if (certs.size() < 2)
+ {
+ return certs;
+ }
+
+ X509Principal issuer = PrincipalUtil.getIssuerX509Principal(((X509Certificate)certs.get(0)));
+ boolean okay = true;
+
+ for (int i = 1; i != certs.size(); i++)
+ {
+ X509Certificate cert = (X509Certificate)certs.get(i);
+
+ if (issuer.equals(PrincipalUtil.getSubjectX509Principal(cert)))
+ {
+ issuer = PrincipalUtil.getIssuerX509Principal(((X509Certificate)certs.get(i)));
+ }
+ else
+ {
+ okay = false;
+ break;
+ }
+ }
+
+ if (okay)
+ {
+ return certs;
+ }
+
+ // find end-entity cert
+ List retList = new ArrayList(certs.size());
+ List orig = new ArrayList(certs);
+
+ for (int i = 0; i < certs.size(); i++)
+ {
+ X509Certificate cert = (X509Certificate)certs.get(i);
+ boolean found = false;
+
+ X509Principal subject = PrincipalUtil.getSubjectX509Principal(cert);
+
+ for (int j = 0; j != certs.size(); j++)
+ {
+ X509Certificate c = (X509Certificate)certs.get(j);
+ if (PrincipalUtil.getIssuerX509Principal(c).equals(subject))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ retList.add(cert);
+ certs.remove(i);
+ }
+ }
+
+ // can only have one end entity cert - something's wrong, give up.
+ if (retList.size() > 1)
+ {
+ return orig;
+ }
+
+ for (int i = 0; i != retList.size(); i++)
+ {
+ issuer = PrincipalUtil.getIssuerX509Principal(((X509Certificate)retList.get(i)));
+
+ for (int j = 0; j < certs.size(); j++)
+ {
+ X509Certificate c = (X509Certificate)certs.get(j);
+ if (issuer.equals(PrincipalUtil.getSubjectX509Principal(c)))
+ {
+ retList.add(c);
+ certs.remove(j);
+ break;
+ }
+ }
+ }
+
+ // make sure all certificates are accounted for.
+ if (certs.size() > 0)
+ {
+ return orig;
+ }
+
+ return retList;
+ }
+ catch (Exception e)
+ {
+ return certs;
+ }
+ }
+
+ PKIXCertPath(List certificates)
+ {
+ super("X.509");
+ this.certificates = sortCerts(new ArrayList(certificates));
+ }
+
+ /**
+ * Creates a CertPath of the specified type.
+ * This constructor is protected because most users should use
+ * a CertificateFactory to create CertPaths.
+ **/
+ PKIXCertPath(
+ InputStream inStream,
+ String encoding)
+ throws CertificateException
+ {
+ super("X.509");
+ try
+ {
+ if (encoding.equalsIgnoreCase("PkiPath"))
+ {
+ ASN1InputStream derInStream = new ASN1InputStream(inStream);
+ ASN1Primitive derObject = derInStream.readObject();
+ if (!(derObject instanceof ASN1Sequence))
+ {
+ throw new CertificateException("input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
+ }
+ Enumeration e = ((ASN1Sequence)derObject).getObjects();
+ certificates = new ArrayList();
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
+ while (e.hasMoreElements())
+ {
+ ASN1Encodable element = (ASN1Encodable)e.nextElement();
+ byte[] encoded = element.toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ certificates.add(0, certFactory.generateCertificate(
+ new ByteArrayInputStream(encoded)));
+ }
+ }
+ else if (encoding.equalsIgnoreCase("PKCS7") || encoding.equalsIgnoreCase("PEM"))
+ {
+ inStream = new BufferedInputStream(inStream);
+ certificates = new ArrayList();
+ CertificateFactory certFactory= CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
+ Certificate cert;
+ while ((cert = certFactory.generateCertificate(inStream)) != null)
+ {
+ certificates.add(cert);
+ }
+ }
+ else
+ {
+ throw new CertificateException("unsupported encoding: " + encoding);
+ }
+ }
+ catch (IOException ex)
+ {
+ throw new CertificateException("IOException throw while decoding CertPath:\n" + ex.toString());
+ }
+ catch (NoSuchProviderException ex)
+ {
+ throw new CertificateException("BouncyCastle provider not found while trying to get a CertificateFactory:\n" + ex.toString());
+ }
+
+ this.certificates = sortCerts(certificates);
+ }
+
+ /**
+ * Returns an iteration of the encodings supported by this
+ * certification path, with the default encoding
+ * first. Attempts to modify the returned Iterator via its
+ * remove method result in an UnsupportedOperationException.
+ *
+ * @return an Iterator over the names of the supported encodings (as Strings)
+ **/
+ public Iterator getEncodings()
+ {
+ return certPathEncodings.iterator();
+ }
+
+ /**
+ * Returns the encoded form of this certification path, using
+ * the default encoding.
+ *
+ * @return the encoded bytes
+ * @exception java.security.cert.CertificateEncodingException if an encoding error occurs
+ **/
+ public byte[] getEncoded()
+ throws CertificateEncodingException
+ {
+ Iterator iter = getEncodings();
+ if (iter.hasNext())
+ {
+ Object enc = iter.next();
+ if (enc instanceof String)
+ {
+ return getEncoded((String)enc);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the encoded form of this certification path, using
+ * the specified encoding.
+ *
+ * @param encoding the name of the encoding to use
+ * @return the encoded bytes
+ * @exception java.security.cert.CertificateEncodingException if an encoding error
+ * occurs or the encoding requested is not supported
+ *
+ **/
+ public byte[] getEncoded(String encoding)
+ throws CertificateEncodingException
+ {
+ if (encoding.equalsIgnoreCase("PkiPath"))
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ ListIterator iter = certificates.listIterator(certificates.size());
+ while (iter.hasPrevious())
+ {
+ v.add(toASN1Object((X509Certificate)iter.previous()));
+ }
+
+ return toDEREncoded(new DERSequence(v));
+ }
+ else if (encoding.equalsIgnoreCase("PKCS7"))
+ {
+ ContentInfo encInfo = new ContentInfo(PKCSObjectIdentifiers.data, null);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ for (int i = 0; i != certificates.size(); i++)
+ {
+ v.add(toASN1Object((X509Certificate)certificates.get(i)));
+ }
+
+ SignedData sd = new SignedData(
+ new ASN1Integer(1),
+ new DERSet(),
+ encInfo,
+ new DERSet(v),
+ null,
+ new DERSet());
+
+ return toDEREncoded(new ContentInfo(
+ PKCSObjectIdentifiers.signedData, sd));
+ }
+ else if (encoding.equalsIgnoreCase("PEM"))
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
+
+ try
+ {
+ for (int i = 0; i != certificates.size(); i++)
+ {
+ pWrt.writeObject(new PemObject("CERTIFICATE", ((X509Certificate)certificates.get(i)).getEncoded()));
+ }
+
+ pWrt.close();
+ }
+ catch (Exception e)
+ {
+ throw new CertificateEncodingException("can't encode certificate for PEM encoded path");
+ }
+
+ return bOut.toByteArray();
+ }
+ else
+ {
+ throw new CertificateEncodingException("unsupported encoding: " + encoding);
+ }
+ }
+
+ /**
+ * Returns the list of certificates in this certification
+ * path. The List returned must be immutable and thread-safe.
+ *
+ * @return an immutable List of Certificates (may be empty, but not null)
+ **/
+ public List getCertificates()
+ {
+ return Collections.unmodifiableList(new ArrayList(certificates));
+ }
+
+ /**
+ * Return a DERObject containing the encoded certificate.
+ *
+ * @param cert the X509Certificate object to be encoded
+ *
+ * @return the DERObject
+ **/
+ private ASN1Primitive toASN1Object(
+ X509Certificate cert)
+ throws CertificateEncodingException
+ {
+ try
+ {
+ return new ASN1InputStream(cert.getEncoded()).readObject();
+ }
+ catch (Exception e)
+ {
+ throw new CertificateEncodingException("Exception while encoding certificate: " + e.toString());
+ }
+ }
+
+ private byte[] toDEREncoded(ASN1Encodable obj)
+ throws CertificateEncodingException
+ {
+ try
+ {
+ return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CertificateEncodingException("Exception thrown: " + e);
+ }
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/SignatureUtil.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/SignatureUtil.java
new file mode 100644
index 00000000..96a1529c
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/SignatureUtil.java
@@ -0,0 +1,134 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1Null;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERNull;
+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;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+
+class SignatureUtil
+{
+ private static final ASN1Null derNull = new DERNull();
+
+ static void setSignatureParameters(
+ Signature signature,
+ ASN1Encodable params)
+ throws NoSuchAlgorithmException, SignatureException, InvalidKeyException
+ {
+ if (params != null && !derNull.equals(params.toASN1Primitive()))
+ {
+ try
+ {
+ AlgorithmParameters sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider().getName());
+
+ try
+ {
+ sigParams.init(params.toASN1Primitive().getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new SignatureException("IOException decoding parameters: " + e.getMessage());
+ }
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new SignatureException("cannot find provider: " + e.getMessage());
+ }
+ }
+ }
+
+ static String getSignatureName(
+ AlgorithmIdentifier sigAlgId)
+ {
+ ASN1Encodable params = sigAlgId.getParameters();
+
+ if (params != null && !derNull.equals(params))
+ {
+ if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
+ {
+ RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
+
+ return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "withRSAandMGF1";
+ }
+ if (sigAlgId.getAlgorithm().equals(X9ObjectIdentifiers.ecdsa_with_SHA2))
+ {
+ ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params);
+
+ return getDigestAlgName((ASN1ObjectIdentifier)ecDsaParams.getObjectAt(0)) + "withECDSA";
+ }
+ }
+
+ return sigAlgId.getAlgorithm().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.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java
new file mode 100644
index 00000000..dac30008
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java
@@ -0,0 +1,293 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.CRLException;
+import java.security.cert.X509CRLEntry;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Enumerated;
+import org.spongycastle.asn1.util.ASN1Dump;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.CRLReason;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.asn1.x509.X509Extension;
+import org.spongycastle.x509.extension.X509ExtensionUtil;
+import org.spongycastle.jce.X509Principal;
+
+/**
+ * The following extensions are listed in RFC 2459 as relevant to CRL Entries
+ *
+ * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer
+ * (critical)
+ */
+class X509CRLEntryObject extends X509CRLEntry
+{
+ private TBSCertList.CRLEntry c;
+
+ private X500Name certificateIssuer;
+ private int hashValue;
+ private boolean isHashValueSet;
+
+ public X509CRLEntryObject(TBSCertList.CRLEntry c)
+ {
+ this.c = c;
+ this.certificateIssuer = null;
+ }
+
+ /**
+ * Constructor for CRLEntries of indirect CRLs. If <code>isIndirect</code>
+ * is <code>false</code> {@link #getCertificateIssuer()} will always
+ * return <code>null</code>, <code>previousCertificateIssuer</code> is
+ * ignored. If this <code>isIndirect</code> is specified and this CRLEntry
+ * has no certificate issuer CRL entry extension
+ * <code>previousCertificateIssuer</code> is returned by
+ * {@link #getCertificateIssuer()}.
+ *
+ * @param c
+ * TBSCertList.CRLEntry object.
+ * @param isIndirect
+ * <code>true</code> if the corresponding CRL is a indirect
+ * CRL.
+ * @param previousCertificateIssuer
+ * Certificate issuer of the previous CRLEntry.
+ */
+ public X509CRLEntryObject(
+ TBSCertList.CRLEntry c,
+ boolean isIndirect,
+ X500Name previousCertificateIssuer)
+ {
+ this.c = c;
+ this.certificateIssuer = loadCertificateIssuer(isIndirect, previousCertificateIssuer);
+ }
+
+ /**
+ * Will return true if any extensions are present and marked as critical as
+ * we currently don't handle any extensions!
+ */
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ Set extns = getCriticalExtensionOIDs();
+
+ return extns != null && !extns.isEmpty();
+ }
+
+ private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer)
+ {
+ if (!isIndirect)
+ {
+ return null;
+ }
+
+ byte[] ext = getExtensionValue(X509Extension.certificateIssuer.getId());
+ if (ext == null)
+ {
+ return previousCertificateIssuer;
+ }
+
+ try
+ {
+ GeneralName[] names = GeneralNames.getInstance(
+ X509ExtensionUtil.fromExtensionValue(ext)).getNames();
+ for (int i = 0; i < names.length; i++)
+ {
+ if (names[i].getTagNo() == GeneralName.directoryName)
+ {
+ return X500Name.getInstance(names[i].getName());
+ }
+ }
+ return null;
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ X509Principal getCertificateIssuer()
+ {
+ if (certificateIssuer == null)
+ {
+ return null;
+ }
+ try
+ {
+ return new X509Principal(certificateIssuer.getEncoded());
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e.toString());
+ }
+ }
+ private Set getExtensionOIDs(boolean critical)
+ {
+ Extensions extensions = c.getExtensions();
+
+ if (extensions != null)
+ {
+ Set set = new HashSet();
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (critical == ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+
+ return null;
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public byte[] getExtensionValue(String oid)
+ {
+ Extensions exts = c.getExtensions();
+
+ if (exts != null)
+ {
+ Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+
+ if (ext != null)
+ {
+ try
+ {
+ return ext.getExtnValue().getEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("error encoding " + e.toString());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Cache the hashCode value - calculating it with the standard method.
+ * @return calculated hashCode.
+ */
+ public int hashCode()
+ {
+ if (!isHashValueSet)
+ {
+ hashValue = super.hashCode();
+ isHashValueSet = true;
+ }
+
+ return hashValue;
+ }
+
+ public byte[] getEncoded()
+ throws CRLException
+ {
+ try
+ {
+ return c.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return c.getUserCertificate().getValue();
+ }
+
+ public Date getRevocationDate()
+ {
+ return c.getRevocationDate().getDate();
+ }
+
+ public boolean hasExtensions()
+ {
+ return c.getExtensions() != null;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append(" userCertificate: ").append(this.getSerialNumber()).append(nl);
+ buf.append(" revocationDate: ").append(this.getRevocationDate()).append(nl);
+
+ Extensions extensions = c.getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+ if (e.hasMoreElements())
+ {
+ buf.append(" crlEntryExtensions:").append(nl);
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+ if (ext.getExtnValue() != null)
+ {
+ byte[] octs = ext.getExtnValue().getOctets();
+ ASN1InputStream dIn = new ASN1InputStream(octs);
+ buf.append(" critical(").append(ext.isCritical()).append(") ");
+ try
+ {
+ if (oid.equals(X509Extension.reasonCode))
+ {
+ buf.append(CRLReason.getInstance(ASN1Enumerated.getInstance(dIn.readObject()))).append(nl);
+ }
+ else if (oid.equals(X509Extension.certificateIssuer))
+ {
+ buf.append("Certificate issuer: ").append(GeneralNames.getInstance(dIn.readObject())).append(nl);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
+ }
+ }
+ catch (Exception ex)
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ }
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
new file mode 100644
index 00000000..f2b5f5d8
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
@@ -0,0 +1,556 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.util.ASN1Dump;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.CRLDistPoint;
+import org.spongycastle.asn1.x509.CRLNumber;
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.IssuingDistributionPoint;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.jce.X509Principal;
+import org.spongycastle.jce.provider.RFC3280CertPathUtilities;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.x509.extension.X509ExtensionUtil;
+
+/**
+ * The following extensions are listed in RFC 2459 as relevant to CRLs
+ *
+ * Authority Key Identifier
+ * Issuer Alternative Name
+ * CRL Number
+ * Delta CRL Indicator (critical)
+ * Issuing Distribution Point (critical)
+ */
+class X509CRLObject
+ extends X509CRL
+{
+ private CertificateList c;
+ private String sigAlgName;
+ private byte[] sigAlgParams;
+ private boolean isIndirect;
+
+ static boolean isIndirectCRL(X509CRL crl)
+ throws CRLException
+ {
+ try
+ {
+ byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId());
+ return idp != null
+ && IssuingDistributionPoint.getInstance(X509ExtensionUtil.fromExtensionValue(idp)).isIndirectCRL();
+ }
+ catch (Exception e)
+ {
+ throw new ExtCRLException(
+ "Exception reading IssuingDistributionPoint", e);
+ }
+ }
+
+ public X509CRLObject(
+ CertificateList c)
+ throws CRLException
+ {
+ this.c = c;
+
+ try
+ {
+ this.sigAlgName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+
+ if (c.getSignatureAlgorithm().getParameters() != null)
+ {
+ this.sigAlgParams = ((ASN1Encodable)c.getSignatureAlgorithm().getParameters()).toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ }
+ else
+ {
+ this.sigAlgParams = null;
+ }
+
+ this.isIndirect = isIndirectCRL(this);
+ }
+ catch (Exception e)
+ {
+ throw new CRLException("CRL contents invalid: " + e);
+ }
+ }
+
+ /**
+ * Will return true if any extensions are present and marked
+ * as critical as we currently dont handle any extensions!
+ */
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ Set extns = getCriticalExtensionOIDs();
+
+ if (extns == null)
+ {
+ return false;
+ }
+
+ extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT);
+ extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
+
+ return !extns.isEmpty();
+ }
+
+ private Set getExtensionOIDs(boolean critical)
+ {
+ if (this.getVersion() == 2)
+ {
+ Extensions extensions = c.getTBSCertList().getExtensions();
+
+ if (extensions != null)
+ {
+ Set set = new HashSet();
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (critical == ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+ }
+
+ return null;
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(true);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return getExtensionOIDs(false);
+ }
+
+ public byte[] getExtensionValue(String oid)
+ {
+ Extensions exts = c.getTBSCertList().getExtensions();
+
+ if (exts != null)
+ {
+ Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+
+ if (ext != null)
+ {
+ try
+ {
+ return ext.getExtnValue().getEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException("error parsing " + e.toString());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public byte[] getEncoded()
+ throws CRLException
+ {
+ try
+ {
+ return c.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ public void verify(PublicKey key)
+ throws CRLException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ verify(key, BouncyCastleProvider.PROVIDER_NAME);
+ }
+
+ public void verify(PublicKey key, String sigProvider)
+ throws CRLException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ if (!c.getSignatureAlgorithm().equals(c.getTBSCertList().getSignature()))
+ {
+ throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
+ }
+
+ Signature sig;
+
+ if (sigProvider != null)
+ {
+ sig = Signature.getInstance(getSigAlgName(), sigProvider);
+ }
+ else
+ {
+ sig = Signature.getInstance(getSigAlgName());
+ }
+
+ sig.initVerify(key);
+ sig.update(this.getTBSCertList());
+
+ if (!sig.verify(this.getSignature()))
+ {
+ throw new SignatureException("CRL does not verify with supplied public key.");
+ }
+ }
+
+ public int getVersion()
+ {
+ return c.getVersionNumber();
+ }
+
+ public Principal getIssuerDN()
+ {
+ return new X509Principal(X500Name.getInstance(c.getIssuer().toASN1Primitive()));
+ }
+
+ public Date getThisUpdate()
+ {
+ return c.getThisUpdate().getDate();
+ }
+
+ public Date getNextUpdate()
+ {
+ if (c.getNextUpdate() != null)
+ {
+ return c.getNextUpdate().getDate();
+ }
+
+ return null;
+ }
+
+ private Set loadCRLEntries()
+ {
+ Set entrySet = new HashSet();
+ Enumeration certs = c.getRevokedCertificateEnumeration();
+
+ X500Name previousCertificateIssuer = c.getIssuer();
+ while (certs.hasMoreElements())
+ {
+ TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
+ X509CRLEntryObject crlEntry = new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
+ entrySet.add(crlEntry);
+ if (isIndirect && entry.hasExtensions())
+ {
+ Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
+ }
+ }
+ }
+
+ return entrySet;
+ }
+
+ public X509CRLEntry getRevokedCertificate(BigInteger serialNumber)
+ {
+ Enumeration certs = c.getRevokedCertificateEnumeration();
+
+ X500Name previousCertificateIssuer = c.getIssuer();
+ while (certs.hasMoreElements())
+ {
+ TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)certs.nextElement();
+
+ if (serialNumber.equals(entry.getUserCertificate().getValue()))
+ {
+ return new X509CRLEntryObject(entry, isIndirect, previousCertificateIssuer);
+ }
+
+ if (isIndirect && entry.hasExtensions())
+ {
+ Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ previousCertificateIssuer = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Set getRevokedCertificates()
+ {
+ Set entrySet = loadCRLEntries();
+
+ if (!entrySet.isEmpty())
+ {
+ return Collections.unmodifiableSet(entrySet);
+ }
+
+ return null;
+ }
+
+ public byte[] getTBSCertList()
+ throws CRLException
+ {
+ try
+ {
+ return c.getTBSCertList().getEncoded("DER");
+ }
+ catch (IOException e)
+ {
+ throw new CRLException(e.toString());
+ }
+ }
+
+ public byte[] getSignature()
+ {
+ return c.getSignature().getBytes();
+ }
+
+ public String getSigAlgName()
+ {
+ return sigAlgName;
+ }
+
+ public String getSigAlgOID()
+ {
+ return c.getSignatureAlgorithm().getAlgorithm().getId();
+ }
+
+ public byte[] getSigAlgParams()
+ {
+ if (sigAlgParams != null)
+ {
+ byte[] tmp = new byte[sigAlgParams.length];
+
+ System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a string representation of this CRL.
+ *
+ * @return a string representation of this CRL.
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append(" Version: ").append(this.getVersion()).append(
+ nl);
+ buf.append(" IssuerDN: ").append(this.getIssuerDN())
+ .append(nl);
+ buf.append(" This update: ").append(this.getThisUpdate())
+ .append(nl);
+ buf.append(" Next update: ").append(this.getNextUpdate())
+ .append(nl);
+ buf.append(" Signature Algorithm: ").append(this.getSigAlgName())
+ .append(nl);
+
+ byte[] sig = this.getSignature();
+
+ buf.append(" Signature: ").append(
+ new String(Hex.encode(sig, 0, 20))).append(nl);
+ for (int i = 20; i < sig.length; i += 20)
+ {
+ if (i < sig.length - 20)
+ {
+ buf.append(" ").append(
+ new String(Hex.encode(sig, i, 20))).append(nl);
+ }
+ else
+ {
+ buf.append(" ").append(
+ new String(Hex.encode(sig, i, sig.length - i))).append(nl);
+ }
+ }
+
+ Extensions extensions = c.getTBSCertList().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ if (e.hasMoreElements())
+ {
+ buf.append(" Extensions: ").append(nl);
+ }
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (ext.getExtnValue() != null)
+ {
+ byte[] octs = ext.getExtnValue().getOctets();
+ ASN1InputStream dIn = new ASN1InputStream(octs);
+ buf.append(" critical(").append(
+ ext.isCritical()).append(") ");
+ try
+ {
+ if (oid.equals(Extension.cRLNumber))
+ {
+ buf.append(
+ new CRLNumber(ASN1Integer.getInstance(
+ dIn.readObject()).getPositiveValue()))
+ .append(nl);
+ }
+ else if (oid.equals(Extension.deltaCRLIndicator))
+ {
+ buf.append(
+ "Base CRL: "
+ + new CRLNumber(ASN1Integer.getInstance(
+ dIn.readObject()).getPositiveValue()))
+ .append(nl);
+ }
+ else if (oid
+ .equals(Extension.issuingDistributionPoint))
+ {
+ buf.append(
+ IssuingDistributionPoint.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid
+ .equals(Extension.cRLDistributionPoints))
+ {
+ buf.append(
+ CRLDistPoint.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(Extension.freshestCRL))
+ {
+ buf.append(
+ CRLDistPoint.getInstance(dIn.readObject())).append(nl);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append(
+ ASN1Dump.dumpAsString(dIn.readObject()))
+ .append(nl);
+ }
+ }
+ catch (Exception ex)
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ }
+ Set set = getRevokedCertificates();
+ if (set != null)
+ {
+ Iterator it = set.iterator();
+ while (it.hasNext())
+ {
+ buf.append(it.next());
+ buf.append(nl);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Checks whether the given certificate is on this CRL.
+ *
+ * @param cert the certificate to check for.
+ * @return true if the given certificate is on this CRL,
+ * false otherwise.
+ */
+ public boolean isRevoked(Certificate cert)
+ {
+ if (!cert.getType().equals("X.509"))
+ {
+ throw new RuntimeException("X.509 CRL used with non X.509 Cert");
+ }
+
+ TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
+
+ X500Name caName = c.getIssuer();
+
+ if (certs != null)
+ {
+ BigInteger serial = ((X509Certificate)cert).getSerialNumber();
+
+ for (int i = 0; i < certs.length; i++)
+ {
+ if (isIndirect && certs[i].hasExtensions())
+ {
+ Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ caName = X500Name.getInstance(GeneralNames.getInstance(currentCaName.getParsedValue()).getNames()[0].getName());
+ }
+ }
+
+ if (certs[i].getUserCertificate().getValue().equals(serial))
+ {
+ X500Name issuer;
+
+ try
+ {
+ issuer = org.spongycastle.asn1.x509.Certificate.getInstance(cert.getEncoded()).getIssuer();
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new RuntimeException("Cannot process certificate");
+ }
+
+ if (!caName.equals(issuer))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
new file mode 100644
index 00000000..aa83e65d
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java
@@ -0,0 +1,858 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ASN1String;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERIA5String;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.misc.MiscObjectIdentifiers;
+import org.spongycastle.asn1.misc.NetscapeCertType;
+import org.spongycastle.asn1.misc.NetscapeRevocationURL;
+import org.spongycastle.asn1.misc.VerisignCzagExtension;
+import org.spongycastle.asn1.util.ASN1Dump;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.style.RFC4519Style;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.BasicConstraints;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.KeyUsage;
+import org.spongycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.spongycastle.jce.X509Principal;
+import org.spongycastle.jce.provider.RFC3280CertPathUtilities;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.jce.interfaces.PKCS12BagAttributeCarrier;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Integers;
+import org.spongycastle.util.encoders.Hex;
+
+class X509CertificateObject
+ extends X509Certificate
+ implements PKCS12BagAttributeCarrier
+{
+ private org.spongycastle.asn1.x509.Certificate c;
+ private BasicConstraints basicConstraints;
+ private boolean[] keyUsage;
+ private boolean hashValueSet;
+ private int hashValue;
+
+ private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl();
+
+ public X509CertificateObject(
+ org.spongycastle.asn1.x509.Certificate c)
+ throws CertificateParsingException
+ {
+ this.c = c;
+
+ try
+ {
+ byte[] bytes = this.getExtensionBytes("2.5.29.19");
+
+ if (bytes != null)
+ {
+ basicConstraints = BasicConstraints.getInstance(ASN1Primitive.fromByteArray(bytes));
+ }
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
+ }
+
+ try
+ {
+ byte[] bytes = this.getExtensionBytes("2.5.29.15");
+ if (bytes != null)
+ {
+ DERBitString bits = DERBitString.getInstance(ASN1Primitive.fromByteArray(bytes));
+
+ bytes = bits.getBytes();
+ int length = (bytes.length * 8) - bits.getPadBits();
+
+ keyUsage = new boolean[(length < 9) ? 9 : length];
+
+ for (int i = 0; i != length; i++)
+ {
+ keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+ }
+ else
+ {
+ keyUsage = null;
+ }
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException("cannot construct KeyUsage: " + e);
+ }
+ }
+
+ public void checkValidity()
+ throws CertificateExpiredException, CertificateNotYetValidException
+ {
+ this.checkValidity(new Date());
+ }
+
+ public void checkValidity(
+ Date date)
+ throws CertificateExpiredException, CertificateNotYetValidException
+ {
+ if (date.getTime() > this.getNotAfter().getTime()) // for other VM compatibility
+ {
+ throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
+ }
+
+ if (date.getTime() < this.getNotBefore().getTime())
+ {
+ throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
+ }
+ }
+
+ public int getVersion()
+ {
+ return c.getVersionNumber();
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return c.getSerialNumber().getValue();
+ }
+
+ public Principal getIssuerDN()
+ {
+ try
+ {
+ return new X509Principal(X500Name.getInstance(c.getIssuer().getEncoded()));
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public Principal getSubjectDN()
+ {
+ return new X509Principal(X500Name.getInstance(c.getSubject().toASN1Primitive()));
+ }
+
+ public Date getNotBefore()
+ {
+ return c.getStartDate().getDate();
+ }
+
+ public Date getNotAfter()
+ {
+ return c.getEndDate().getDate();
+ }
+
+ public byte[] getTBSCertificate()
+ throws CertificateEncodingException
+ {
+ try
+ {
+ return c.getTBSCertificate().getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CertificateEncodingException(e.toString());
+ }
+ }
+
+ public byte[] getSignature()
+ {
+ return c.getSignature().getBytes();
+ }
+
+ /**
+ * return a more "meaningful" representation for the signature algorithm used in
+ * the certficate.
+ */
+ public String getSigAlgName()
+ {
+ Provider prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
+
+ if (prov != null)
+ {
+ String algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+
+ if (algName != null)
+ {
+ return algName;
+ }
+ }
+
+ Provider[] provs = Security.getProviders();
+
+ //
+ // search every provider looking for a real algorithm
+ //
+ for (int i = 0; i != provs.length; i++)
+ {
+ String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
+ if (algName != null)
+ {
+ return algName;
+ }
+ }
+
+ return this.getSigAlgOID();
+ }
+
+ /**
+ * return the object identifier for the signature.
+ */
+ public String getSigAlgOID()
+ {
+ return c.getSignatureAlgorithm().getAlgorithm().getId();
+ }
+
+ /**
+ * return the signature parameters, or null if there aren't any.
+ */
+ public byte[] getSigAlgParams()
+ {
+ if (c.getSignatureAlgorithm().getParameters() != null)
+ {
+ try
+ {
+ return c.getSignatureAlgorithm().getParameters().toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public boolean[] getIssuerUniqueID()
+ {
+ DERBitString id = c.getTBSCertificate().getIssuerUniqueId();
+
+ if (id != null)
+ {
+ byte[] bytes = id.getBytes();
+ boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];
+
+ for (int i = 0; i != boolId.length; i++)
+ {
+ boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+
+ return boolId;
+ }
+
+ return null;
+ }
+
+ public boolean[] getSubjectUniqueID()
+ {
+ DERBitString id = c.getTBSCertificate().getSubjectUniqueId();
+
+ if (id != null)
+ {
+ byte[] bytes = id.getBytes();
+ boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()];
+
+ for (int i = 0; i != boolId.length; i++)
+ {
+ boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+
+ return boolId;
+ }
+
+ return null;
+ }
+
+ public boolean[] getKeyUsage()
+ {
+ return keyUsage;
+ }
+
+ public List getExtendedKeyUsage()
+ throws CertificateParsingException
+ {
+ byte[] bytes = this.getExtensionBytes("2.5.29.37");
+
+ if (bytes != null)
+ {
+ try
+ {
+ ASN1InputStream dIn = new ASN1InputStream(bytes);
+ ASN1Sequence seq = (ASN1Sequence)dIn.readObject();
+ List list = new ArrayList();
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId());
+ }
+
+ return Collections.unmodifiableList(list);
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException("error processing extended key usage extension");
+ }
+ }
+
+ return null;
+ }
+
+ public int getBasicConstraints()
+ {
+ if (basicConstraints != null)
+ {
+ if (basicConstraints.isCA())
+ {
+ if (basicConstraints.getPathLenConstraint() == null)
+ {
+ return Integer.MAX_VALUE;
+ }
+ else
+ {
+ return basicConstraints.getPathLenConstraint().intValue();
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ return -1;
+ }
+
+ public Collection getSubjectAlternativeNames()
+ throws CertificateParsingException
+ {
+ return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId()));
+ }
+
+ public Collection getIssuerAlternativeNames()
+ throws CertificateParsingException
+ {
+ return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId()));
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ if (this.getVersion() == 3)
+ {
+ Set set = new HashSet();
+ Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+ }
+
+ return null;
+ }
+
+ private byte[] getExtensionBytes(String oid)
+ {
+ Extensions exts = c.getTBSCertificate().getExtensions();
+
+ if (exts != null)
+ {
+ Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+ if (ext != null)
+ {
+ return ext.getExtnValue().getOctets();
+ }
+ }
+
+ return null;
+ }
+
+ public byte[] getExtensionValue(String oid)
+ {
+ Extensions exts = c.getTBSCertificate().getExtensions();
+
+ if (exts != null)
+ {
+ Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
+
+ if (ext != null)
+ {
+ try
+ {
+ return ext.getExtnValue().getEncoded();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException("error parsing " + e.toString());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ if (this.getVersion() == 3)
+ {
+ Set set = new HashSet();
+ Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (!ext.isCritical())
+ {
+ set.add(oid.getId());
+ }
+ }
+
+ return set;
+ }
+ }
+
+ return null;
+ }
+
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ if (this.getVersion() == 3)
+ {
+ Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ String oidId = oid.getId();
+
+ if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE)
+ || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES)
+ || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS)
+ || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY)
+ || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS)
+ || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT)
+ || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR)
+ || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS)
+ || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS)
+ || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME)
+ || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS))
+ {
+ continue;
+ }
+
+ Extension ext = extensions.getExtension(oid);
+
+ if (ext.isCritical())
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public PublicKey getPublicKey()
+ {
+ try
+ {
+ return BouncyCastleProvider.getPublicKey(c.getSubjectPublicKeyInfo());
+ }
+ catch (IOException e)
+ {
+ return null; // should never happen...
+ }
+ }
+
+ public byte[] getEncoded()
+ throws CertificateEncodingException
+ {
+ try
+ {
+ return c.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ throw new CertificateEncodingException(e.toString());
+ }
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof Certificate))
+ {
+ return false;
+ }
+
+ Certificate other = (Certificate)o;
+
+ try
+ {
+ byte[] b1 = this.getEncoded();
+ byte[] b2 = other.getEncoded();
+
+ return Arrays.areEqual(b1, b2);
+ }
+ catch (CertificateEncodingException e)
+ {
+ return false;
+ }
+ }
+
+ public synchronized int hashCode()
+ {
+ if (!hashValueSet)
+ {
+ hashValue = calculateHashCode();
+ hashValueSet = true;
+ }
+
+ return hashValue;
+ }
+
+ private int calculateHashCode()
+ {
+ try
+ {
+ int hashCode = 0;
+ byte[] certData = this.getEncoded();
+ for (int i = 1; i < certData.length; i++)
+ {
+ hashCode += certData[i] * i;
+ }
+ return hashCode;
+ }
+ catch (CertificateEncodingException e)
+ {
+ return 0;
+ }
+ }
+
+ public void setBagAttribute(
+ ASN1ObjectIdentifier oid,
+ ASN1Encodable attribute)
+ {
+ attrCarrier.setBagAttribute(oid, attribute);
+ }
+
+ public ASN1Encodable getBagAttribute(
+ ASN1ObjectIdentifier oid)
+ {
+ return attrCarrier.getBagAttribute(oid);
+ }
+
+ public Enumeration getBagAttributeKeys()
+ {
+ return attrCarrier.getBagAttributeKeys();
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ String nl = System.getProperty("line.separator");
+
+ buf.append(" [0] Version: ").append(this.getVersion()).append(nl);
+ buf.append(" SerialNumber: ").append(this.getSerialNumber()).append(nl);
+ buf.append(" IssuerDN: ").append(this.getIssuerDN()).append(nl);
+ buf.append(" Start Date: ").append(this.getNotBefore()).append(nl);
+ buf.append(" Final Date: ").append(this.getNotAfter()).append(nl);
+ buf.append(" SubjectDN: ").append(this.getSubjectDN()).append(nl);
+ buf.append(" Public Key: ").append(this.getPublicKey()).append(nl);
+ buf.append(" Signature Algorithm: ").append(this.getSigAlgName()).append(nl);
+
+ byte[] sig = this.getSignature();
+
+ buf.append(" Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
+ for (int i = 20; i < sig.length; i += 20)
+ {
+ if (i < sig.length - 20)
+ {
+ buf.append(" ").append(new String(Hex.encode(sig, i, 20))).append(nl);
+ }
+ else
+ {
+ buf.append(" ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
+ }
+ }
+
+ Extensions extensions = c.getTBSCertificate().getExtensions();
+
+ if (extensions != null)
+ {
+ Enumeration e = extensions.oids();
+
+ if (e.hasMoreElements())
+ {
+ buf.append(" Extensions: \n");
+ }
+
+ while (e.hasMoreElements())
+ {
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement();
+ Extension ext = extensions.getExtension(oid);
+
+ if (ext.getExtnValue() != null)
+ {
+ byte[] octs = ext.getExtnValue().getOctets();
+ ASN1InputStream dIn = new ASN1InputStream(octs);
+ buf.append(" critical(").append(ext.isCritical()).append(") ");
+ try
+ {
+ if (oid.equals(Extension.basicConstraints))
+ {
+ buf.append(BasicConstraints.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(Extension.keyUsage))
+ {
+ buf.append(KeyUsage.getInstance(dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
+ {
+ buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
+ {
+ buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
+ }
+ else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
+ {
+ buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
+ }
+ else
+ {
+ buf.append(oid.getId());
+ buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
+ //buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ catch (Exception ex)
+ {
+ buf.append(oid.getId());
+ // buf.append(" value = ").append(new String(Hex.encode(ext.getExtnValue().getOctets()))).append(nl);
+ buf.append(" value = ").append("*****").append(nl);
+ }
+ }
+ else
+ {
+ buf.append(nl);
+ }
+ }
+ }
+
+ return buf.toString();
+ }
+
+ public final void verify(
+ PublicKey key)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ Signature signature;
+ String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+
+ try
+ {
+ signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME);
+ }
+ catch (Exception e)
+ {
+ signature = Signature.getInstance(sigName);
+ }
+
+ checkSignature(key, signature);
+ }
+
+ public final void verify(
+ PublicKey key,
+ String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException
+ {
+ String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
+ Signature signature = Signature.getInstance(sigName, sigProvider);
+
+ checkSignature(key, signature);
+ }
+
+ private void checkSignature(
+ PublicKey key,
+ Signature signature)
+ throws CertificateException, NoSuchAlgorithmException,
+ SignatureException, InvalidKeyException
+ {
+ if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature()))
+ {
+ throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+ }
+
+ ASN1Encodable params = c.getSignatureAlgorithm().getParameters();
+
+ // TODO This should go after the initVerify?
+ X509SignatureUtil.setSignatureParameters(signature, params);
+
+ signature.initVerify(key);
+
+ signature.update(this.getTBSCertificate());
+
+ if (!signature.verify(this.getSignature()))
+ {
+ throw new SignatureException("certificate does not verify with supplied key");
+ }
+ }
+
+ private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
+ {
+ if (!id1.getAlgorithm().equals(id2.getAlgorithm()))
+ {
+ return false;
+ }
+
+ if (id1.getParameters() == null)
+ {
+ if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (id2.getParameters() == null)
+ {
+ if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ return id1.getParameters().equals(id2.getParameters());
+ }
+
+ private static Collection getAlternativeNames(byte[] extVal)
+ throws CertificateParsingException
+ {
+ if (extVal == null)
+ {
+ return null;
+ }
+ try
+ {
+ Collection temp = new ArrayList();
+ Enumeration it = ASN1Sequence.getInstance(extVal).getObjects();
+ while (it.hasMoreElements())
+ {
+ GeneralName genName = GeneralName.getInstance(it.nextElement());
+ List list = new ArrayList();
+ list.add(Integers.valueOf(genName.getTagNo()));
+ switch (genName.getTagNo())
+ {
+ case GeneralName.ediPartyName:
+ case GeneralName.x400Address:
+ case GeneralName.otherName:
+ list.add(genName.getEncoded());
+ break;
+ case GeneralName.directoryName:
+ list.add(X500Name.getInstance(RFC4519Style.INSTANCE, genName.getName()).toString());
+ break;
+ case GeneralName.dNSName:
+ case GeneralName.rfc822Name:
+ case GeneralName.uniformResourceIdentifier:
+ list.add(((ASN1String)genName.getName()).getString());
+ break;
+ case GeneralName.registeredID:
+ list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId());
+ break;
+ case GeneralName.iPAddress:
+ byte[] addrBytes = DEROctetString.getInstance(genName.getName()).getOctets();
+ list.add(addrBytes);
+ break;
+ default:
+ throw new IOException("Bad tag number: " + genName.getTagNo());
+ }
+
+ temp.add(list);
+ }
+ if (temp.size() == 0)
+ {
+ return null;
+ }
+ return Collections.unmodifiableCollection(temp);
+ }
+ catch (Exception e)
+ {
+ throw new CertificateParsingException(e.getMessage());
+ }
+ }
+}
diff --git a/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
new file mode 100644
index 00000000..e74ced7f
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java
@@ -0,0 +1,125 @@
+package org.spongycastle.jcajce.provider.asymmetric.x509;
+
+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.3/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
new file mode 100644
index 00000000..9875bd14
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
@@ -0,0 +1,1636 @@
+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.getName());
+ }
+ 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 = SubjectPublicKeyInfo.getInstance(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.3/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
new file mode 100644
index 00000000..d188bf69
--- /dev/null
+++ b/prov/src/main/jdk1.3/org/spongycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
@@ -0,0 +1,1031 @@
+package org.spongycastle.jcajce.provider.symmetric.util;
+
+import java.lang.reflect.Method;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+import javax.crypto.spec.RC5ParameterSpec;
+
+import org.spongycastle.asn1.cms.GCMParameters;
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.OutputLengthException;
+import org.spongycastle.crypto.modes.AEADBlockCipher;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.modes.CCMBlockCipher;
+import org.spongycastle.crypto.modes.CFBBlockCipher;
+import org.spongycastle.crypto.modes.CTSBlockCipher;
+import org.spongycastle.crypto.modes.EAXBlockCipher;
+import org.spongycastle.crypto.modes.GCFBBlockCipher;
+import org.spongycastle.crypto.modes.GCMBlockCipher;
+import org.spongycastle.crypto.modes.GOFBBlockCipher;
+import org.spongycastle.crypto.modes.OCBBlockCipher;
+import org.spongycastle.crypto.modes.OFBBlockCipher;
+import org.spongycastle.crypto.modes.OpenPGPCFBBlockCipher;
+import org.spongycastle.crypto.modes.PGPCFBBlockCipher;
+import org.spongycastle.crypto.modes.SICBlockCipher;
+import org.spongycastle.crypto.paddings.BlockCipherPadding;
+import org.spongycastle.crypto.paddings.ISO10126d2Padding;
+import org.spongycastle.crypto.paddings.ISO7816d4Padding;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.paddings.TBCPadding;
+import org.spongycastle.crypto.paddings.X923Padding;
+import org.spongycastle.crypto.paddings.ZeroBytePadding;
+import org.spongycastle.crypto.params.AEADParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.params.ParametersWithSBox;
+import org.spongycastle.crypto.params.RC2Parameters;
+import org.spongycastle.crypto.params.RC5Parameters;
+import org.spongycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.spongycastle.jcajce.spec.RepeatedSecretKeySpec;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.util.Strings;
+
+public class BaseBlockCipher
+ extends BaseWrapCipher
+ implements PBE
+{
+ private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+
+ //
+ // specs we can handle.
+ //
+ private Class[] availableSpecs =
+ {
+ RC2ParameterSpec.class,
+ RC5ParameterSpec.class,
+ IvParameterSpec.class,
+ PBEParameterSpec.class,
+ GOST28147ParameterSpec.class,
+ gcmSpecClass
+ };
+
+ private BlockCipher baseEngine;
+ private BlockCipherProvider engineProvider;
+ private GenericBlockCipher cipher;
+ private ParametersWithIV ivParam;
+ private AEADParameters aeadParams;
+
+ private int ivLength = 0;
+
+ private boolean padded;
+
+ private PBEParameterSpec pbeSpec = null;
+ private String pbeAlgorithm = null;
+
+ private String modeName = null;
+
+ private static Class lookup(String className)
+ {
+ try
+ {
+ Class def = BaseBlockCipher.class.getClassLoader().loadClass(className);
+
+ return def;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ protected BaseBlockCipher(
+ BlockCipher engine)
+ {
+ baseEngine = engine;
+
+ cipher = new BufferedGenericBlockCipher(engine);
+ }
+
+ protected BaseBlockCipher(
+ BlockCipherProvider provider)
+ {
+ baseEngine = provider.get();
+ engineProvider = provider;
+
+ cipher = new BufferedGenericBlockCipher(provider.get());
+ }
+
+ protected BaseBlockCipher(
+ AEADBlockCipher engine)
+ {
+ baseEngine = engine.getUnderlyingCipher();
+ ivLength = baseEngine.getBlockSize();
+ cipher = new AEADGenericBlockCipher(engine);
+ }
+
+ protected BaseBlockCipher(
+ org.spongycastle.crypto.BlockCipher engine,
+ int ivLength)
+ {
+ baseEngine = engine;
+
+ this.cipher = new BufferedGenericBlockCipher(engine);
+ this.ivLength = ivLength / 8;
+ }
+
+ protected BaseBlockCipher(
+ BufferedBlockCipher engine,
+ int ivLength)
+ {
+ baseEngine = engine.getUnderlyingCipher();
+
+ this.cipher = new BufferedGenericBlockCipher(engine);
+ this.ivLength = ivLength / 8;
+ }
+
+ protected int engineGetBlockSize()
+ {
+ return baseEngine.getBlockSize();
+ }
+
+ protected byte[] engineGetIV()
+ {
+ return (ivParam != null) ? ivParam.getIV() : null;
+ }
+
+ protected int engineGetKeySize(
+ Key key)
+ {
+ return key.getEncoded().length * 8;
+ }
+
+ protected int engineGetOutputSize(
+ int inputLen)
+ {
+ return cipher.getOutputSize(inputLen);
+ }
+
+ protected AlgorithmParameters engineGetParameters()
+ {
+ if (engineParams == null)
+ {
+ if (pbeSpec != null)
+ {
+ try
+ {
+ engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
+ engineParams.init(pbeSpec);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ else if (ivParam != null)
+ {
+ String name = cipher.getUnderlyingCipher().getAlgorithmName();
+
+ if (name.indexOf('/') >= 0)
+ {
+ name = name.substring(0, name.indexOf('/'));
+ }
+
+ try
+ {
+ engineParams = AlgorithmParameters.getInstance(name, BouncyCastleProvider.PROVIDER_NAME);
+ engineParams.init(ivParam.getIV());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ else if (aeadParams != null)
+ {
+ try
+ {
+ engineParams = AlgorithmParameters.getInstance("GCM", BouncyCastleProvider.PROVIDER_NAME);
+ engineParams.init(new GCMParameters(aeadParams.getNonce(), aeadParams.getMacSize()).getEncoded());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ }
+
+ return engineParams;
+ }
+
+ protected void engineSetMode(
+ String mode)
+ throws NoSuchAlgorithmException
+ {
+ modeName = Strings.toUpperCase(mode);
+
+ if (modeName.equals("ECB"))
+ {
+ ivLength = 0;
+ cipher = new BufferedGenericBlockCipher(baseEngine);
+ }
+ else if (modeName.equals("CBC"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(
+ new CBCBlockCipher(baseEngine));
+ }
+ else if (modeName.startsWith("OFB"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ if (modeName.length() != 3)
+ {
+ int wordSize = Integer.parseInt(modeName.substring(3));
+
+ cipher = new BufferedGenericBlockCipher(
+ new OFBBlockCipher(baseEngine, wordSize));
+ }
+ else
+ {
+ cipher = new BufferedGenericBlockCipher(
+ new OFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+ }
+ }
+ else if (modeName.startsWith("CFB"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ if (modeName.length() != 3)
+ {
+ int wordSize = Integer.parseInt(modeName.substring(3));
+
+ cipher = new BufferedGenericBlockCipher(
+ new CFBBlockCipher(baseEngine, wordSize));
+ }
+ else
+ {
+ cipher = new BufferedGenericBlockCipher(
+ new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
+ }
+ }
+ else if (modeName.startsWith("PGP"))
+ {
+ boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV");
+
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(
+ new PGPCFBBlockCipher(baseEngine, inlineIV));
+ }
+ else if (modeName.equalsIgnoreCase("OpenPGPCFB"))
+ {
+ ivLength = 0;
+ cipher = new BufferedGenericBlockCipher(
+ new OpenPGPCFBBlockCipher(baseEngine));
+ }
+ else if (modeName.startsWith("SIC"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ if (ivLength < 16)
+ {
+ throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
+ }
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ new SICBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("CTR"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ new SICBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("GOFB"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ new GOFBBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("GCFB"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+ new GCFBBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("CTS"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
+ }
+ else if (modeName.startsWith("CCM"))
+ {
+ ivLength = 13; // CCM nonce 7..13 bytes
+ cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
+ }
+ else if (modeName.startsWith("OCB"))
+ {
+ if (engineProvider != null)
+ {
+ /*
+ * RFC 7253 4.2. Nonce is a string of no more than 120 bits
+ */
+ ivLength = 15;
+ cipher = new AEADGenericBlockCipher(new OCBBlockCipher(baseEngine, engineProvider.get()));
+ }
+ else
+ {
+ throw new NoSuchAlgorithmException("can't support mode " + mode);
+ }
+ }
+ else if (modeName.startsWith("EAX"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new AEADGenericBlockCipher(new EAXBlockCipher(baseEngine));
+ }
+ else if (modeName.startsWith("GCM"))
+ {
+ ivLength = baseEngine.getBlockSize();
+ cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
+ }
+ else
+ {
+ throw new NoSuchAlgorithmException("can't support mode " + mode);
+ }
+ }
+
+ protected void engineSetPadding(
+ String padding)
+ throws NoSuchPaddingException
+ {
+ String paddingName = Strings.toUpperCase(padding);
+
+ if (paddingName.equals("NOPADDING"))
+ {
+ if (cipher.wrapOnNoPadding())
+ {
+ cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
+ }
+ }
+ else if (paddingName.equals("WITHCTS"))
+ {
+ cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher()));
+ }
+ else
+ {
+ padded = true;
+
+ if (isAEADModeName(modeName))
+ {
+ throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes.");
+ }
+ else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher());
+ }
+ else if (paddingName.equals("ZEROBYTEPADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding());
+ }
+ else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding());
+ }
+ else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new X923Padding());
+ }
+ else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding());
+ }
+ else if (paddingName.equals("TBCPADDING"))
+ {
+ cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding());
+ }
+ else
+ {
+ throw new NoSuchPaddingException("Padding " + padding + " unknown.");
+ }
+ }
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ CipherParameters param;
+
+ this.pbeSpec = null;
+ this.pbeAlgorithm = null;
+ this.engineParams = null;
+ this.aeadParams = null;
+
+ //
+ // basic key check
+ //
+ if (!(key instanceof SecretKey))
+ {
+ throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption.");
+ }
+
+ //
+ // for RC5-64 we must have some default parameters
+ //
+ if (params == null && baseEngine.getAlgorithmName().startsWith("RC5-64"))
+ {
+ throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in.");
+ }
+
+ //
+ // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it).
+ //
+ if (key instanceof BCPBEKey)
+ {
+ BCPBEKey k = (BCPBEKey)key;
+
+ if (k.getOID() != null)
+ {
+ pbeAlgorithm = k.getOID().getId();
+ }
+ else
+ {
+ pbeAlgorithm = k.getAlgorithm();
+ }
+
+ if (k.getParam() != null)
+ {
+ param = k.getParam();
+ if (params instanceof IvParameterSpec)
+ {
+ IvParameterSpec iv = (IvParameterSpec)params;
+
+ param = new ParametersWithIV(param, iv.getIV());
+ }
+ else if (params instanceof GOST28147ParameterSpec)
+ {
+ // need to pick up IV and SBox.
+ GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
+
+ param = new ParametersWithSBox(param, gost28147Param.getSbox());
+
+ if (gost28147Param.getIV() != null && ivLength != 0)
+ {
+ param = new ParametersWithIV(param, gost28147Param.getIV());
+ }
+ }
+ }
+ else if (params instanceof PBEParameterSpec)
+ {
+ pbeSpec = (PBEParameterSpec)params;
+ param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName());
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
+ }
+
+ if (param instanceof ParametersWithIV)
+ {
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else if (params == null)
+ {
+ param = new KeyParameter(key.getEncoded());
+ }
+ else if (params instanceof IvParameterSpec)
+ {
+ if (ivLength != 0)
+ {
+ IvParameterSpec p = (IvParameterSpec)params;
+
+ if (p.getIV().length != ivLength && !isAEADModeName(modeName))
+ {
+ throw new InvalidAlgorithmParameterException("IV must be " + ivLength + " bytes long.");
+ }
+
+ if (key instanceof RepeatedSecretKeySpec)
+ {
+ param = new ParametersWithIV(null, p.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ else
+ {
+ param = new ParametersWithIV(new KeyParameter(key.getEncoded()), p.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else
+ {
+ if (modeName != null && modeName.equals("ECB"))
+ {
+ throw new InvalidAlgorithmParameterException("ECB mode does not use an IV");
+ }
+
+ param = new KeyParameter(key.getEncoded());
+ }
+ }
+ else if (params instanceof GOST28147ParameterSpec)
+ {
+ GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params;
+
+ param = new ParametersWithSBox(
+ new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
+
+ if (gost28147Param.getIV() != null && ivLength != 0)
+ {
+ param = new ParametersWithIV(param, gost28147Param.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else if (params instanceof RC2ParameterSpec)
+ {
+ RC2ParameterSpec rc2Param = (RC2ParameterSpec)params;
+
+ param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits());
+
+ if (rc2Param.getIV() != null && ivLength != 0)
+ {
+ param = new ParametersWithIV(param, rc2Param.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else if (params instanceof RC5ParameterSpec)
+ {
+ RC5ParameterSpec rc5Param = (RC5ParameterSpec)params;
+
+ param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds());
+ if (baseEngine.getAlgorithmName().startsWith("RC5"))
+ {
+ if (baseEngine.getAlgorithmName().equals("RC5-32"))
+ {
+ if (rc5Param.getWordSize() != 32)
+ {
+ throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 32 not " + rc5Param.getWordSize() + ".");
+ }
+ }
+ else if (baseEngine.getAlgorithmName().equals("RC5-64"))
+ {
+ if (rc5Param.getWordSize() != 64)
+ {
+ throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 64 not " + rc5Param.getWordSize() + ".");
+ }
+ }
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("RC5 parameters passed to a cipher that is not RC5.");
+ }
+ if ((rc5Param.getIV() != null) && (ivLength != 0))
+ {
+ param = new ParametersWithIV(param, rc5Param.getIV());
+ ivParam = (ParametersWithIV)param;
+ }
+ }
+ else if (gcmSpecClass != null && gcmSpecClass.isInstance(params))
+ {
+ if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher))
+ {
+ throw new InvalidAlgorithmParameterException("GCMParameterSpec can only be used with AEAD modes.");
+ }
+
+ try
+ {
+ Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
+ Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
+
+ if (key instanceof RepeatedSecretKeySpec)
+ {
+ param = aeadParams = new AEADParameters(null, ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0]));
+ }
+ else
+ {
+ param = aeadParams = new AEADParameters(new KeyParameter(key.getEncoded()), ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0]));
+ }
+ }
+ catch (Exception e)
+ {
+ throw new InvalidAlgorithmParameterException("Cannot process GCMParameterSpec.");
+ }
+ }
+ else
+ {
+ throw new InvalidAlgorithmParameterException("unknown parameter type.");
+ }
+
+ if ((ivLength != 0) && !(param instanceof ParametersWithIV) && !(param instanceof AEADParameters))
+ {
+ SecureRandom ivRandom = random;
+
+ if (ivRandom == null)
+ {
+ ivRandom = new SecureRandom();
+ }
+
+ if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
+ {
+ byte[] iv = new byte[ivLength];
+
+ ivRandom.nextBytes(iv);
+ param = new ParametersWithIV(param, iv);
+ ivParam = (ParametersWithIV)param;
+ }
+ else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0)
+ {
+ throw new InvalidAlgorithmParameterException("no IV set when one expected");
+ }
+ }
+
+ if (random != null && padded)
+ {
+ param = new ParametersWithRandom(param, random);
+ }
+
+ try
+ {
+ switch (opmode)
+ {
+ case Cipher.ENCRYPT_MODE:
+ case Cipher.WRAP_MODE:
+ cipher.init(true, param);
+ break;
+ case Cipher.DECRYPT_MODE:
+ case Cipher.UNWRAP_MODE:
+ cipher.init(false, param);
+ break;
+ default:
+ throw new InvalidParameterException("unknown opmode " + opmode + " passed");
+ }
+ }
+ catch (Exception e)
+ {
+ throw new InvalidKeyException(e.getMessage());
+ }
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ AlgorithmParameterSpec paramSpec = null;
+
+ if (params != null)
+ {
+ for (int i = 0; i != availableSpecs.length; i++)
+ {
+ if (availableSpecs[i] == null)
+ {
+ continue;
+ }
+
+ try
+ {
+ paramSpec = params.getParameterSpec(availableSpecs[i]);
+ break;
+ }
+ catch (Exception e)
+ {
+ // try again if possible
+ }
+ }
+
+ if (paramSpec == null)
+ {
+ throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
+ }
+ }
+
+ engineInit(opmode, key, paramSpec, random);
+
+ engineParams = params;
+ }
+
+ protected void engineInit(
+ int opmode,
+ Key key,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ try
+ {
+ engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw new InvalidKeyException(e.getMessage());
+ }
+ }
+
+ protected void engineUpdateAAD(byte[] input, int offset, int length)
+ {
+ cipher.updateAAD(input, offset, length);
+ }
+
+ protected byte[] engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ {
+ int length = cipher.getUpdateOutputSize(inputLen);
+
+ if (length > 0)
+ {
+ byte[] out = new byte[length];
+
+ int len = cipher.processBytes(input, inputOffset, inputLen, out, 0);
+
+ if (len == 0)
+ {
+ return null;
+ }
+ else if (len != out.length)
+ {
+ byte[] tmp = new byte[len];
+
+ System.arraycopy(out, 0, tmp, 0, len);
+
+ return tmp;
+ }
+
+ return out;
+ }
+
+ cipher.processBytes(input, inputOffset, inputLen, null, 0);
+
+ return null;
+ }
+
+ protected int engineUpdate(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ throws ShortBufferException
+ {
+ try
+ {
+ return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+ }
+ catch (DataLengthException e)
+ {
+ throw new ShortBufferException(e.getMessage());
+ }
+ }
+
+ protected byte[] engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException
+ {
+ int len = 0;
+ byte[] tmp = new byte[engineGetOutputSize(inputLen)];
+
+ if (inputLen != 0)
+ {
+ len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0);
+ }
+
+ try
+ {
+ len += cipher.doFinal(tmp, len);
+ }
+ catch (DataLengthException e)
+ {
+ throw new IllegalBlockSizeException(e.getMessage());
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+
+ if (len == tmp.length)
+ {
+ return tmp;
+ }
+
+ byte[] out = new byte[len];
+
+ System.arraycopy(tmp, 0, out, 0, len);
+
+ return out;
+ }
+
+ protected int engineDoFinal(
+ byte[] input,
+ int inputOffset,
+ int inputLen,
+ byte[] output,
+ int outputOffset)
+ throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
+ {
+ try
+ {
+ int len = 0;
+
+ if (inputLen != 0)
+ {
+ len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
+ }
+
+ return (len + cipher.doFinal(output, outputOffset + len));
+ }
+ catch (OutputLengthException e)
+ {
+ throw new ShortBufferException(e.getMessage());
+ }
+ catch (DataLengthException e)
+ {
+ throw new IllegalBlockSizeException(e.getMessage());
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new BadPaddingException(e.getMessage());
+ }
+ }
+
+ private boolean isAEADModeName(
+ String modeName)
+ {
+ return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName) || "OCB".equals(modeName);
+ }
+
+ /*
+ * The ciphers that inherit from us.
+ */
+
+ static private interface GenericBlockCipher
+ {
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ public boolean wrapOnNoPadding();
+
+ public String getAlgorithmName();
+
+ public org.spongycastle.crypto.BlockCipher getUnderlyingCipher();
+
+ public int getOutputSize(int len);
+
+ public int getUpdateOutputSize(int len);
+
+ public void updateAAD(byte[] input, int offset, int length);
+
+ public int processByte(byte in, byte[] out, int outOff)
+ throws DataLengthException;
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException;
+
+ public int doFinal(byte[] out, int outOff)
+ throws IllegalStateException, InvalidCipherTextException;
+ }
+
+ private static class BufferedGenericBlockCipher
+ implements GenericBlockCipher
+ {
+ private BufferedBlockCipher cipher;
+
+ BufferedGenericBlockCipher(BufferedBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ BufferedGenericBlockCipher(org.spongycastle.crypto.BlockCipher cipher)
+ {
+ this.cipher = new PaddedBufferedBlockCipher(cipher);
+ }
+
+ BufferedGenericBlockCipher(org.spongycastle.crypto.BlockCipher cipher, BlockCipherPadding padding)
+ {
+ this.cipher = new PaddedBufferedBlockCipher(cipher, padding);
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ cipher.init(forEncryption, params);
+ }
+
+ public boolean wrapOnNoPadding()
+ {
+ return !(cipher instanceof CTSBlockCipher);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName();
+ }
+
+ public org.spongycastle.crypto.BlockCipher getUnderlyingCipher()
+ {
+ return cipher.getUnderlyingCipher();
+ }
+
+ public int getOutputSize(int len)
+ {
+ return cipher.getOutputSize(len);
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ return cipher.getUpdateOutputSize(len);
+ }
+
+ public void updateAAD(byte[] input, int offset, int length)
+ {
+ throw new UnsupportedOperationException("AAD is not supported in the current mode.");
+ }
+
+ public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processByte(in, out, outOff);
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processBytes(in, inOff, len, out, outOff);
+ }
+
+ public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
+ {
+ return cipher.doFinal(out, outOff);
+ }
+ }
+
+ private static class AEADGenericBlockCipher
+ implements GenericBlockCipher
+ {
+ private AEADBlockCipher cipher;
+
+ AEADGenericBlockCipher(AEADBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ cipher.init(forEncryption, params);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName();
+ }
+
+ public boolean wrapOnNoPadding()
+ {
+ return false;
+ }
+
+ public org.spongycastle.crypto.BlockCipher getUnderlyingCipher()
+ {
+ return cipher.getUnderlyingCipher();
+ }
+
+ public int getOutputSize(int len)
+ {
+ return cipher.getOutputSize(len);
+ }
+
+ public int getUpdateOutputSize(int len)
+ {
+ return cipher.getUpdateOutputSize(len);
+ }
+
+ public void updateAAD(byte[] input, int offset, int length)
+ {
+ cipher.processAADBytes(input, offset, length);
+ }
+
+ public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processByte(in, out, outOff);
+ }
+
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
+ {
+ return cipher.processBytes(in, inOff, len, out, outOff);
+ }
+
+ public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
+ {
+ return cipher.doFinal(out, outOff);
+ }
+ }
+}