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 'pkix/src/main/java/org/spongycastle/pkcs')
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/MacDataGenerator.java49
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequest.java236
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequestBuilder.java156
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilder.java13
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilderProvider.java8
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPdu.java161
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPduBuilder.java179
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBag.java93
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagBuilder.java76
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagFactory.java58
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java76
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java54
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCSException.java27
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/PKCSIOException.java29
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequest.java42
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequestBuilder.java28
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilder.java54
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilderProvider.java40
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEInputDecryptorProviderBuilder.java66
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEOutputEncryptorBuilder.java77
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/bc/PKCS12PBEUtils.java153
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java115
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java38
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java45
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java15
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java122
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java108
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java177
-rw-r--r--pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java179
29 files changed, 2474 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/MacDataGenerator.java b/pkix/src/main/java/org/spongycastle/pkcs/MacDataGenerator.java
new file mode 100644
index 00000000..c6667d8b
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/MacDataGenerator.java
@@ -0,0 +1,49 @@
+package org.spongycastle.pkcs;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.pkcs.MacData;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.DigestInfo;
+import org.spongycastle.operator.MacCalculator;
+
+class MacDataGenerator
+{
+ private PKCS12MacCalculatorBuilder builder;
+
+ MacDataGenerator(PKCS12MacCalculatorBuilder builder)
+ {
+ this.builder = builder;
+ }
+
+ public MacData build(char[] password, byte[] data)
+ throws PKCSException
+ {
+ MacCalculator macCalculator;
+
+ try
+ {
+ macCalculator = builder.build(password);
+
+ OutputStream out = macCalculator.getOutputStream();
+
+ out.write(data);
+
+ out.close();
+ }
+ catch (Exception e)
+ {
+ throw new PKCSException("unable to process data: " + e.getMessage(), e);
+ }
+
+ AlgorithmIdentifier algId = macCalculator.getAlgorithmIdentifier();
+
+ DigestInfo dInfo = new DigestInfo(builder.getDigestAlgorithmIdentifier(), macCalculator.getMac());
+ PKCS12PBEParams params = PKCS12PBEParams.getInstance(algId.getParameters());
+
+ return new MacData(dInfo, params.getIV(), params.getIterations().intValue());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequest.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequest.java
new file mode 100644
index 00000000..c4ee749a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequest.java
@@ -0,0 +1,236 @@
+package org.spongycastle.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.pkcs.Attribute;
+import org.spongycastle.asn1.pkcs.CertificationRequest;
+import org.spongycastle.asn1.pkcs.CertificationRequestInfo;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for a PKCS#10 certification request.
+ */
+public class PKCS10CertificationRequest
+{
+ private static Attribute[] EMPTY_ARRAY = new Attribute[0];
+
+ private CertificationRequest certificationRequest;
+
+ private static CertificationRequest parseBytes(byte[] encoding)
+ throws IOException
+ {
+ try
+ {
+ return CertificationRequest.getInstance(ASN1Primitive.fromByteArray(encoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new PKCSIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new PKCSIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Create a PKCS10CertificationRequestHolder from an underlying ASN.1 structure.
+ *
+ * @param certificationRequest the underlying ASN.1 structure representing a request.
+ */
+ public PKCS10CertificationRequest(CertificationRequest certificationRequest)
+ {
+ this.certificationRequest = certificationRequest;
+ }
+
+ /**
+ * Create a PKCS10CertificationRequestHolder from the passed in bytes.
+ *
+ * @param encoded BER/DER encoding of the CertificationRequest structure.
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public PKCS10CertificationRequest(byte[] encoded)
+ throws IOException
+ {
+ this(parseBytes(encoded));
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for this request.
+ *
+ * @return a CertificateRequest object.
+ */
+ public CertificationRequest toASN1Structure()
+ {
+ return certificationRequest;
+ }
+
+ /**
+ * Return the subject on this request.
+ *
+ * @return the X500Name representing the request's subject.
+ */
+ public X500Name getSubject()
+ {
+ return X500Name.getInstance(certificationRequest.getCertificationRequestInfo().getSubject());
+ }
+
+ /**
+ * Return the details of the signature algorithm used to create this request.
+ *
+ * @return the AlgorithmIdentifier describing the signature algorithm used to create this request.
+ */
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return certificationRequest.getSignatureAlgorithm();
+ }
+
+ /**
+ * Return the bytes making up the signature associated with this request.
+ *
+ * @return the request signature bytes.
+ */
+ public byte[] getSignature()
+ {
+ return certificationRequest.getSignature().getBytes();
+ }
+
+ /**
+ * Return the SubjectPublicKeyInfo describing the public key this request is carrying.
+ *
+ * @return the public key ASN.1 structure contained in the request.
+ */
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return certificationRequest.getCertificationRequestInfo().getSubjectPublicKeyInfo();
+ }
+
+ /**
+ * Return the attributes, if any associated with this request.
+ *
+ * @return an array of Attribute, zero length if none present.
+ */
+ public Attribute[] getAttributes()
+ {
+ ASN1Set attrSet = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+ if (attrSet == null)
+ {
+ return EMPTY_ARRAY;
+ }
+
+ Attribute[] attrs = new Attribute[attrSet.size()];
+
+ for (int i = 0; i != attrSet.size(); i++)
+ {
+ attrs[i] = Attribute.getInstance(attrSet.getObjectAt(i));
+ }
+
+ return attrs;
+ }
+
+ /**
+ * Return an array of attributes matching the passed in type OID.
+ *
+ * @param type the type of the attribute being looked for.
+ * @return an array of Attribute of the requested type, zero length if none present.
+ */
+ public Attribute[] getAttributes(ASN1ObjectIdentifier type)
+ {
+ ASN1Set attrSet = certificationRequest.getCertificationRequestInfo().getAttributes();
+
+ if (attrSet == null)
+ {
+ return EMPTY_ARRAY;
+ }
+
+ List list = new ArrayList();
+
+ for (int i = 0; i != attrSet.size(); i++)
+ {
+ Attribute attr = Attribute.getInstance(attrSet.getObjectAt(i));
+ if (attr.getAttrType().equals(type))
+ {
+ list.add(attr);
+ }
+ }
+
+ if (list.size() == 0)
+ {
+ return EMPTY_ARRAY;
+ }
+
+ return (Attribute[])list.toArray(new Attribute[list.size()]);
+ }
+
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return certificationRequest.getEncoded();
+ }
+
+ /**
+ * Validate the signature on the PKCS10 certification request in this holder.
+ *
+ * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+ * @return true if the signature is valid, false otherwise.
+ * @throws PKCSException if the signature cannot be processed or is inappropriate.
+ */
+ public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+ throws PKCSException
+ {
+ CertificationRequestInfo requestInfo = certificationRequest.getCertificationRequestInfo();
+
+ ContentVerifier verifier;
+
+ try
+ {
+ verifier = verifierProvider.get(certificationRequest.getSignatureAlgorithm());
+
+ OutputStream sOut = verifier.getOutputStream();
+
+ sOut.write(requestInfo.getEncoded(ASN1Encoding.DER));
+
+ sOut.close();
+ }
+ catch (Exception e)
+ {
+ throw new PKCSException("unable to process signature: " + e.getMessage(), e);
+ }
+
+ return verifier.verify(certificationRequest.getSignature().getBytes());
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof PKCS10CertificationRequest))
+ {
+ return false;
+ }
+
+ PKCS10CertificationRequest other = (PKCS10CertificationRequest)o;
+
+ return this.toASN1Structure().equals(other.toASN1Structure());
+ }
+
+ public int hashCode()
+ {
+ return this.toASN1Structure().hashCode();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequestBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequestBuilder.java
new file mode 100644
index 00000000..d10c6fdd
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS10CertificationRequestBuilder.java
@@ -0,0 +1,156 @@
+package org.spongycastle.pkcs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.pkcs.Attribute;
+import org.spongycastle.asn1.pkcs.CertificationRequest;
+import org.spongycastle.asn1.pkcs.CertificationRequestInfo;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.ContentSigner;
+
+/**
+ * A class for creating PKCS#10 Certification requests.
+ * <pre>
+ * CertificationRequest ::= SEQUENCE {
+ * certificationRequestInfo CertificationRequestInfo,
+ * signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},
+ * signature BIT STRING
+ * }
+ *
+ * CertificationRequestInfo ::= SEQUENCE {
+ * version INTEGER { v1(0) } (v1,...),
+ * subject Name,
+ * subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+ * attributes [0] Attributes{{ CRIAttributes }}
+ * }
+ *
+ * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
+ *
+ * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
+ * type ATTRIBUTE.&id({IOSet}),
+ * values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
+ * }
+ * </pre>
+ */
+public class PKCS10CertificationRequestBuilder
+{
+ private SubjectPublicKeyInfo publicKeyInfo;
+ private X500Name subject;
+ private List attributes = new ArrayList();
+ private boolean leaveOffEmpty = false;
+
+ /**
+ * Basic constructor.
+ *
+ * @param subject the X.500 Name defining the certificate subject this request is for.
+ * @param publicKeyInfo the info structure for the public key to be associated with this subject.
+ */
+ public PKCS10CertificationRequestBuilder(X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ this.subject = subject;
+ this.publicKeyInfo = publicKeyInfo;
+ }
+
+ /**
+ * Add an attribute to the certification request we are building.
+ *
+ * @param attrType the OID giving the type of the attribute.
+ * @param attrValue the ASN.1 structure that forms the value of the attribute.
+ * @return this builder object.
+ */
+ public PKCS10CertificationRequestBuilder addAttribute(ASN1ObjectIdentifier attrType, ASN1Encodable attrValue)
+ {
+ attributes.add(new Attribute(attrType, new DERSet(attrValue)));
+
+ return this;
+ }
+
+ /**
+ * Add an attribute with multiple values to the certification request we are building.
+ *
+ * @param attrType the OID giving the type of the attribute.
+ * @param attrValues an array of ASN.1 structures that form the value of the attribute.
+ * @return this builder object.
+ */
+ public PKCS10CertificationRequestBuilder addAttribute(ASN1ObjectIdentifier attrType, ASN1Encodable[] attrValues)
+ {
+ attributes.add(new Attribute(attrType, new DERSet(attrValues)));
+
+ return this;
+ }
+
+ /**
+ * The attributes field in PKCS10 should encoded to an empty tagged set if there are
+ * no attributes. Some CAs will reject requests with the attribute field present.
+ *
+ * @param leaveOffEmpty true if empty attributes should be left out of the encoding false otherwise.
+ * @return this builder object.
+ */
+ public PKCS10CertificationRequestBuilder setLeaveOffEmptyAttributes(boolean leaveOffEmpty)
+ {
+ this.leaveOffEmpty = leaveOffEmpty;
+
+ return this;
+ }
+
+ /**
+ * Generate an PKCS#10 request based on the past in signer.
+ *
+ * @param signer the content signer to be used to generate the signature validating the certificate.
+ * @return a holder containing the resulting PKCS#10 certification request.
+ */
+ public PKCS10CertificationRequest build(
+ ContentSigner signer)
+ {
+ CertificationRequestInfo info;
+
+ if (attributes.isEmpty())
+ {
+ if (leaveOffEmpty)
+ {
+ info = new CertificationRequestInfo(subject, publicKeyInfo, null);
+ }
+ else
+ {
+ info = new CertificationRequestInfo(subject, publicKeyInfo, new DERSet());
+ }
+ }
+ else
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (Iterator it = attributes.iterator(); it.hasNext();)
+ {
+ v.add(Attribute.getInstance(it.next()));
+ }
+
+ info = new CertificationRequestInfo(subject, publicKeyInfo, new DERSet(v));
+ }
+
+ try
+ {
+ OutputStream sOut = signer.getOutputStream();
+
+ sOut.write(info.getEncoded(ASN1Encoding.DER));
+
+ sOut.close();
+
+ return new PKCS10CertificationRequest(new CertificationRequest(info, signer.getAlgorithmIdentifier(), new DERBitString(signer.getSignature())));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("cannot produce certification request signature");
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilder.java
new file mode 100644
index 00000000..be82e990
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilder.java
@@ -0,0 +1,13 @@
+package org.spongycastle.pkcs;
+
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.operator.OperatorCreationException;
+
+public interface PKCS12MacCalculatorBuilder
+{
+ MacCalculator build(char[] password)
+ throws OperatorCreationException;
+
+ AlgorithmIdentifier getDigestAlgorithmIdentifier();
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilderProvider.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilderProvider.java
new file mode 100644
index 00000000..52c9d1cd
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12MacCalculatorBuilderProvider.java
@@ -0,0 +1,8 @@
+package org.spongycastle.pkcs;
+
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface PKCS12MacCalculatorBuilderProvider
+{
+ PKCS12MacCalculatorBuilder get(AlgorithmIdentifier algorithmIdentifier);
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPdu.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPdu.java
new file mode 100644
index 00000000..6a229e48
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPdu.java
@@ -0,0 +1,161 @@
+package org.spongycastle.pkcs;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.pkcs.ContentInfo;
+import org.spongycastle.asn1.pkcs.MacData;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.pkcs.Pfx;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.CertIOException;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A holding class for the PKCS12 Pfx structure.
+ */
+public class PKCS12PfxPdu
+{
+ private Pfx pfx;
+
+ private static Pfx parseBytes(byte[] pfxEncoding)
+ throws IOException
+ {
+ try
+ {
+ return Pfx.getInstance(ASN1Primitive.fromByteArray(pfxEncoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ public PKCS12PfxPdu(Pfx pfx)
+ {
+ this.pfx = pfx;
+ }
+
+ public PKCS12PfxPdu(byte[] pfx)
+ throws IOException
+ {
+ this(parseBytes(pfx));
+ }
+
+ /**
+ * Return the content infos in the AuthenticatedSafe contained in this Pfx.
+ *
+ * @return an array of ContentInfo.
+ */
+ public ContentInfo[] getContentInfos()
+ {
+ ASN1Sequence seq = ASN1Sequence.getInstance(ASN1OctetString.getInstance(this.pfx.getAuthSafe().getContent()).getOctets());
+ ContentInfo[] content = new ContentInfo[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ content[i] = ContentInfo.getInstance(seq.getObjectAt(i));
+ }
+
+ return content;
+ }
+
+ /**
+ * Return whether or not there is MAC attached to this file.
+ *
+ * @return true if there is, false otherwise.
+ */
+ public boolean hasMac()
+ {
+ return pfx.getMacData() != null;
+ }
+
+ /**
+ * Return the algorithm identifier describing the MAC algorithm
+ *
+ * @return the AlgorithmIdentifier representing the MAC algorithm, null if none present.
+ */
+ public AlgorithmIdentifier getMacAlgorithmID()
+ {
+ MacData md = pfx.getMacData();
+
+ if (md != null)
+ {
+ return md.getMac().getAlgorithmId();
+ }
+
+ return null;
+ }
+
+ /**
+ * Verify the MacData attached to the PFX is consistent with what is expected.
+ *
+ * @param macCalcProviderBuilder provider builder for the calculator for the MAC
+ * @param password password to use
+ * @return true if mac data is valid, false otherwise.
+ * @throws PKCSException if there is a problem evaluating the MAC.
+ * @throws IllegalStateException if no MAC is actually present
+ */
+ public boolean isMacValid(PKCS12MacCalculatorBuilderProvider macCalcProviderBuilder, char[] password)
+ throws PKCSException
+ {
+ if (hasMac())
+ {
+ MacData pfxmData = pfx.getMacData();
+ MacDataGenerator mdGen = new MacDataGenerator(macCalcProviderBuilder.get(new AlgorithmIdentifier(pfxmData.getMac().getAlgorithmId().getAlgorithm(), new PKCS12PBEParams(pfxmData.getSalt(), pfxmData.getIterationCount().intValue()))));
+
+ try
+ {
+ MacData mData = mdGen.build(
+ password,
+ ASN1OctetString.getInstance(pfx.getAuthSafe().getContent()).getOctets());
+
+ return Arrays.constantTimeAreEqual(mData.getEncoded(), pfx.getMacData().getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new PKCSException("unable to process AuthSafe: " + e.getMessage());
+ }
+ }
+
+ throw new IllegalStateException("no MAC present on PFX");
+ }
+
+ /**
+ * Return the underlying ASN.1 object.
+ *
+ * @return a Pfx object.
+ */
+ public Pfx toASN1Structure()
+ {
+ return pfx;
+ }
+
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return toASN1Structure().getEncoded();
+ }
+
+ /**
+ * Return a Pfx with the outer wrapper encoded as asked for. For example, Pfx is a usually
+ * a BER encoded object, to get one with DefiniteLength encoding use:
+ * <pre>
+ * getEncoded(ASN1Encoding.DL)
+ * </pre>
+ * @param encoding encoding style (ASN1Encoding.DER, ASN1Encoding.DL, ASN1Encoding.BER)
+ * @return a byte array containing the encoded object.
+ * @throws IOException
+ */
+ public byte[] getEncoded(String encoding)
+ throws IOException
+ {
+ return toASN1Structure().getEncoded(encoding);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPduBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPduBuilder.java
new file mode 100644
index 00000000..a9cb0b5b
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12PfxPduBuilder.java
@@ -0,0 +1,179 @@
+package org.spongycastle.pkcs;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.DLSequence;
+import org.spongycastle.asn1.pkcs.AuthenticatedSafe;
+import org.spongycastle.asn1.pkcs.ContentInfo;
+import org.spongycastle.asn1.pkcs.MacData;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.Pfx;
+import org.spongycastle.cms.CMSEncryptedDataGenerator;
+import org.spongycastle.cms.CMSException;
+import org.spongycastle.cms.CMSProcessableByteArray;
+import org.spongycastle.operator.OutputEncryptor;
+
+/**
+ * A builder for the PKCS#12 Pfx key and certificate store.
+ * <p>
+ * For example: you can build a basic key store for the user owning privKey as follows:
+ * </p>
+ * <pre>
+ * X509Certificate[] chain = ....
+ * PublicKey pubKey = ....
+ * PrivateKey privKey = ....
+ * JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+ *
+ * PKCS12SafeBagBuilder taCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[2]);
+ *
+ * taCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Bouncy Primary Certificate"));
+ *
+ * PKCS12SafeBagBuilder caCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[1]);
+ *
+ * caCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Bouncy Intermediate Certificate"));
+ *
+ * PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[0]);
+ *
+ * eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Eric's Key"));
+ * eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, extUtils.createSubjectKeyIdentifier(pubKey));
+ *
+ * PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(privKey, new BcPKCS12PBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, new CBCBlockCipher(new DESedeEngine())).build(passwd));
+ *
+ * keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Eric's Key"));
+ * keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, extUtils.createSubjectKeyIdentifier(pubKey));
+ *
+ * //
+ * // construct the actual key store
+ * //
+ * PKCS12PfxPduBuilder pfxPduBuilder = new PKCS12PfxPduBuilder();
+ *
+ * PKCS12SafeBag[] certs = new PKCS12SafeBag[3];
+ *
+ * certs[0] = eeCertBagBuilder.build();
+ * certs[1] = caCertBagBuilder.build();
+ * certs[2] = taCertBagBuilder.build();
+ *
+ * pfxPduBuilder.addEncryptedData(new BcPKCS12PBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, new CBCBlockCipher(new RC2Engine())).build(passwd), certs);
+ *
+ * pfxPduBuilder.addData(keyBagBuilder.build());
+ *
+ * PKCS12PfxPdu pfx = pfxPduBuilder.build(new BcPKCS12MacCalculatorBuilder(), passwd);
+ * </pre>
+ *
+ */
+public class PKCS12PfxPduBuilder
+{
+ private ASN1EncodableVector dataVector = new ASN1EncodableVector();
+
+ /**
+ * Add a SafeBag that is to be included as is.
+ *
+ * @param data the SafeBag to add.
+ * @return this builder.
+ * @throws IOException
+ */
+ public PKCS12PfxPduBuilder addData(PKCS12SafeBag data)
+ throws IOException
+ {
+ dataVector.add(new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(new DLSequence(data.toASN1Structure()).getEncoded())));
+
+ return this;
+ }
+
+ /**
+ * Add a SafeBag that is to be wrapped in a EncryptedData object.
+ *
+ * @param dataEncryptor the encryptor to use for encoding the data.
+ * @param data the SafeBag to include.
+ * @return this builder.
+ * @throws IOException if a issue occurs processing the data.
+ */
+ public PKCS12PfxPduBuilder addEncryptedData(OutputEncryptor dataEncryptor, PKCS12SafeBag data)
+ throws IOException
+ {
+ return addEncryptedData(dataEncryptor, new DERSequence(data.toASN1Structure()));
+ }
+
+ /**
+ * Add a set of SafeBags that are to be wrapped in a EncryptedData object.
+ *
+ * @param dataEncryptor the encryptor to use for encoding the data.
+ * @param data the SafeBags to include.
+ * @return this builder.
+ * @throws IOException if a issue occurs processing the data.
+ */
+ public PKCS12PfxPduBuilder addEncryptedData(OutputEncryptor dataEncryptor, PKCS12SafeBag[] data)
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != data.length; i++)
+ {
+ v.add(data[i].toASN1Structure());
+ }
+
+ return addEncryptedData(dataEncryptor, new DLSequence(v));
+ }
+
+ private PKCS12PfxPduBuilder addEncryptedData(OutputEncryptor dataEncryptor, ASN1Sequence data)
+ throws IOException
+ {
+ CMSEncryptedDataGenerator envGen = new CMSEncryptedDataGenerator();
+
+ try
+ {
+ dataVector.add(envGen.generate(new CMSProcessableByteArray(data.getEncoded()), dataEncryptor).toASN1Structure());
+ }
+ catch (CMSException e)
+ {
+ throw new PKCSIOException(e.getMessage(), e.getCause());
+ }
+
+ return this;
+ }
+
+ /**
+ * Build the Pfx structure, protecting it with a MAC calculated against the passed in password.
+ *
+ * @param macCalcBuilder a builder for a PKCS12 mac calculator.
+ * @param password the password to use.
+ * @return a Pfx object.
+ * @throws PKCSException on a encoding or processing error.
+ */
+ public PKCS12PfxPdu build(PKCS12MacCalculatorBuilder macCalcBuilder, char[] password)
+ throws PKCSException
+ {
+ AuthenticatedSafe auth = AuthenticatedSafe.getInstance(new DLSequence(dataVector));
+ byte[] encAuth;
+
+ try
+ {
+ encAuth = auth.getEncoded();
+ }
+ catch (IOException e)
+ {
+ throw new PKCSException("unable to encode AuthenticatedSafe: " + e.getMessage(), e);
+ }
+
+ ContentInfo mainInfo = new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(encAuth));
+ MacData mData = null;
+
+ if (macCalcBuilder != null)
+ {
+ MacDataGenerator mdGen = new MacDataGenerator(macCalcBuilder);
+
+ mData = mdGen.build(password, encAuth);
+ }
+
+ //
+ // output the Pfx
+ //
+ Pfx pfx = new Pfx(mainInfo, mData);
+
+ return new PKCS12PfxPdu(pfx);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBag.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBag.java
new file mode 100644
index 00000000..0ce0887e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBag.java
@@ -0,0 +1,93 @@
+package org.spongycastle.pkcs;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.pkcs.Attribute;
+import org.spongycastle.asn1.pkcs.CRLBag;
+import org.spongycastle.asn1.pkcs.CertBag;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.SafeBag;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.cert.X509CRLHolder;
+import org.spongycastle.cert.X509CertificateHolder;
+
+public class PKCS12SafeBag
+{
+ public static final ASN1ObjectIdentifier friendlyNameAttribute = PKCSObjectIdentifiers.pkcs_9_at_friendlyName;
+ public static final ASN1ObjectIdentifier localKeyIdAttribute = PKCSObjectIdentifiers.pkcs_9_at_localKeyId;
+
+ private SafeBag safeBag;
+
+ public PKCS12SafeBag(SafeBag safeBag)
+ {
+ this.safeBag = safeBag;
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for this safe bag.
+ *
+ * @return a SafeBag
+ */
+ public SafeBag toASN1Structure()
+ {
+ return safeBag;
+ }
+
+ /**
+ * Return the BagId giving the type of content in the bag.
+ *
+ * @return the bagId
+ */
+ public ASN1ObjectIdentifier getType()
+ {
+ return safeBag.getBagId();
+ }
+
+ public Attribute[] getAttributes()
+ {
+ ASN1Set attrs = safeBag.getBagAttributes();
+
+ if (attrs == null)
+ {
+ return null;
+ }
+
+ Attribute[] attributes = new Attribute[attrs.size()];
+ for (int i = 0; i != attrs.size(); i++)
+ {
+ attributes[i] = Attribute.getInstance(attrs.getObjectAt(i));
+ }
+
+ return attributes;
+ }
+
+ public Object getBagValue()
+ {
+ if (getType().equals(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag))
+ {
+ return new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(safeBag.getBagValue()));
+ }
+ if (getType().equals(PKCSObjectIdentifiers.certBag))
+ {
+ CertBag certBag = CertBag.getInstance(safeBag.getBagValue());
+
+ return new X509CertificateHolder(Certificate.getInstance(ASN1OctetString.getInstance(certBag.getCertValue()).getOctets()));
+ }
+ if (getType().equals(PKCSObjectIdentifiers.keyBag))
+ {
+ return PrivateKeyInfo.getInstance(safeBag.getBagValue());
+ }
+ if (getType().equals(PKCSObjectIdentifiers.crlBag))
+ {
+ CRLBag crlBag = CRLBag.getInstance(safeBag.getBagValue());
+
+ return new X509CRLHolder(CertificateList.getInstance(ASN1OctetString.getInstance(crlBag.getCRLValue()).getOctets()));
+ }
+
+ return safeBag.getBagValue();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagBuilder.java
new file mode 100644
index 00000000..d60bd5a5
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagBuilder.java
@@ -0,0 +1,76 @@
+package org.spongycastle.pkcs;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.pkcs.Attribute;
+import org.spongycastle.asn1.pkcs.CertBag;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.SafeBag;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.cert.X509CRLHolder;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.OutputEncryptor;
+
+public class PKCS12SafeBagBuilder
+{
+ private ASN1ObjectIdentifier bagType;
+ private ASN1Encodable bagValue;
+ private ASN1EncodableVector bagAttrs = new ASN1EncodableVector();
+
+ public PKCS12SafeBagBuilder(PrivateKeyInfo privateKeyInfo, OutputEncryptor encryptor)
+ {
+ this.bagType = PKCSObjectIdentifiers.pkcs8ShroudedKeyBag;
+ this.bagValue = new PKCS8EncryptedPrivateKeyInfoBuilder(privateKeyInfo).build(encryptor).toASN1Structure();
+ }
+
+ public PKCS12SafeBagBuilder(PrivateKeyInfo privateKeyInfo)
+ {
+ this.bagType = PKCSObjectIdentifiers.keyBag;
+ this.bagValue = privateKeyInfo;
+ }
+
+ public PKCS12SafeBagBuilder(X509CertificateHolder certificate)
+ throws IOException
+ {
+ this(certificate.toASN1Structure());
+ }
+
+ public PKCS12SafeBagBuilder(X509CRLHolder crl)
+ throws IOException
+ {
+ this(crl.toASN1Structure());
+ }
+
+ public PKCS12SafeBagBuilder(Certificate certificate)
+ throws IOException
+ {
+ this.bagType = PKCSObjectIdentifiers.certBag;
+ this.bagValue = new CertBag(PKCSObjectIdentifiers.x509Certificate, new DEROctetString(certificate.getEncoded()));
+ }
+
+ public PKCS12SafeBagBuilder(CertificateList crl)
+ throws IOException
+ {
+ this.bagType = PKCSObjectIdentifiers.crlBag;
+ this.bagValue = new CertBag(PKCSObjectIdentifiers.x509Crl, new DEROctetString(crl.getEncoded()));
+ }
+
+ public PKCS12SafeBagBuilder addBagAttribute(ASN1ObjectIdentifier attrType, ASN1Encodable attrValue)
+ {
+ bagAttrs.add(new Attribute(attrType, new DERSet(attrValue)));
+
+ return this;
+ }
+
+ public PKCS12SafeBag build()
+ {
+ return new PKCS12SafeBag(new SafeBag(bagType, bagValue, new DERSet(bagAttrs)));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagFactory.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagFactory.java
new file mode 100644
index 00000000..36f53723
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS12SafeBagFactory.java
@@ -0,0 +1,58 @@
+package org.spongycastle.pkcs;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.pkcs.ContentInfo;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.SafeBag;
+import org.spongycastle.cms.CMSEncryptedData;
+import org.spongycastle.cms.CMSException;
+import org.spongycastle.operator.InputDecryptorProvider;
+
+public class PKCS12SafeBagFactory
+{
+ private ASN1Sequence safeBagSeq;
+
+ public PKCS12SafeBagFactory(ContentInfo info)
+ {
+ if (info.getContentType().equals(PKCSObjectIdentifiers.encryptedData))
+ {
+ throw new IllegalArgumentException("encryptedData requires constructor with decryptor.");
+ }
+
+ this.safeBagSeq = ASN1Sequence.getInstance(ASN1OctetString.getInstance(info.getContent()).getOctets());
+ }
+
+ public PKCS12SafeBagFactory(ContentInfo info, InputDecryptorProvider inputDecryptorProvider)
+ throws PKCSException
+ {
+ if (info.getContentType().equals(PKCSObjectIdentifiers.encryptedData))
+ {
+ CMSEncryptedData encData = new CMSEncryptedData(org.spongycastle.asn1.cms.ContentInfo.getInstance(info));
+
+ try
+ {
+ this.safeBagSeq = ASN1Sequence.getInstance(encData.getContent(inputDecryptorProvider));
+ }
+ catch (CMSException e)
+ {
+ throw new PKCSException("unable to extract data: " + e.getMessage(), e);
+ }
+ return;
+ }
+
+ throw new IllegalArgumentException("encryptedData requires constructor with decryptor.");
+ }
+
+ public PKCS12SafeBag[] getSafeBags()
+ {
+ PKCS12SafeBag[] safeBags = new PKCS12SafeBag[safeBagSeq.size()];
+
+ for (int i = 0; i != safeBagSeq.size(); i++)
+ {
+ safeBags[i] = new PKCS12SafeBag(SafeBag.getInstance(safeBagSeq.getObjectAt(i)));
+ }
+
+ return safeBags;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
new file mode 100644
index 00000000..1f41fb67
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
@@ -0,0 +1,76 @@
+package org.spongycastle.pkcs;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.cert.CertIOException;
+import org.spongycastle.operator.InputDecryptor;
+import org.spongycastle.operator.InputDecryptorProvider;
+import org.spongycastle.util.io.Streams;
+
+/**
+ * Holding class for a PKCS#8 EncryptedPrivateKeyInfo structure.
+ */
+public class PKCS8EncryptedPrivateKeyInfo
+{
+ private EncryptedPrivateKeyInfo encryptedPrivateKeyInfo;
+
+ private static EncryptedPrivateKeyInfo parseBytes(byte[] pkcs8Encoding)
+ throws IOException
+ {
+ try
+ {
+ return EncryptedPrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(pkcs8Encoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ public PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo)
+ {
+ this.encryptedPrivateKeyInfo = encryptedPrivateKeyInfo;
+ }
+
+ public PKCS8EncryptedPrivateKeyInfo(byte[] encryptedPrivateKeyInfo)
+ throws IOException
+ {
+ this(parseBytes(encryptedPrivateKeyInfo));
+ }
+
+ public EncryptedPrivateKeyInfo toASN1Structure()
+ {
+ return encryptedPrivateKeyInfo;
+ }
+
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return encryptedPrivateKeyInfo.getEncoded();
+ }
+
+ public PrivateKeyInfo decryptPrivateKeyInfo(InputDecryptorProvider inputDecryptorProvider)
+ throws PKCSException
+ {
+ try
+ {
+ InputDecryptor decrytor = inputDecryptorProvider.get(encryptedPrivateKeyInfo.getEncryptionAlgorithm());
+
+ ByteArrayInputStream encIn = new ByteArrayInputStream(encryptedPrivateKeyInfo.getEncryptedData());
+
+ return PrivateKeyInfo.getInstance(Streams.readAll(decrytor.getInputStream(encIn)));
+ }
+ catch (Exception e)
+ {
+ throw new PKCSException("unable to read encrypted data: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
new file mode 100644
index 00000000..3897c40a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
@@ -0,0 +1,54 @@
+package org.spongycastle.pkcs;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.operator.OutputEncryptor;
+
+/**
+ * A class for creating EncryptedPrivateKeyInfo structures.
+ * <pre>
+ * EncryptedPrivateKeyInfo ::= SEQUENCE {
+ * encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}},
+ * encryptedData EncryptedData
+ * }
+ *
+ * EncryptedData ::= OCTET STRING
+ *
+ * KeyEncryptionAlgorithms ALGORITHM-IDENTIFIER ::= {
+ * ... -- For local profiles
+ * }
+ * </pre>
+ */
+public class PKCS8EncryptedPrivateKeyInfoBuilder
+{
+ private PrivateKeyInfo privateKeyInfo;
+
+ public PKCS8EncryptedPrivateKeyInfoBuilder(PrivateKeyInfo privateKeyInfo)
+ {
+ this.privateKeyInfo = privateKeyInfo;
+ }
+
+ public PKCS8EncryptedPrivateKeyInfo build(
+ OutputEncryptor encryptor)
+ {
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ OutputStream cOut = encryptor.getOutputStream(bOut);
+
+ cOut.write(privateKeyInfo.getEncoded());
+
+ cOut.close();
+
+ return new PKCS8EncryptedPrivateKeyInfo(new EncryptedPrivateKeyInfo(encryptor.getAlgorithmIdentifier(), bOut.toByteArray()));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("cannot encode privateKeyInfo");
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCSException.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCSException.java
new file mode 100644
index 00000000..201dde93
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCSException.java
@@ -0,0 +1,27 @@
+package org.spongycastle.pkcs;
+
+/**
+ * General checked Exception thrown in the cert package and its sub-packages.
+ */
+public class PKCSException
+ extends Exception
+{
+ private Throwable cause;
+
+ public PKCSException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public PKCSException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/PKCSIOException.java b/pkix/src/main/java/org/spongycastle/pkcs/PKCSIOException.java
new file mode 100644
index 00000000..0352829b
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/PKCSIOException.java
@@ -0,0 +1,29 @@
+package org.spongycastle.pkcs;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the cert package and its sub-packages.
+ */
+public class PKCSIOException
+ extends IOException
+{
+ private Throwable cause;
+
+ public PKCSIOException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public PKCSIOException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequest.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequest.java
new file mode 100644
index 00000000..6ac22464
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequest.java
@@ -0,0 +1,42 @@
+package org.spongycastle.pkcs.bc;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.pkcs.CertificationRequest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.util.PublicKeyFactory;
+import org.spongycastle.pkcs.PKCS10CertificationRequest;
+import org.spongycastle.pkcs.PKCSException;
+
+public class BcPKCS10CertificationRequest
+ extends PKCS10CertificationRequest
+{
+ public BcPKCS10CertificationRequest(CertificationRequest certificationRequest)
+ {
+ super(certificationRequest);
+ }
+
+ public BcPKCS10CertificationRequest(byte[] encoding)
+ throws IOException
+ {
+ super(encoding);
+ }
+
+ public BcPKCS10CertificationRequest(PKCS10CertificationRequest requestHolder)
+ {
+ super(requestHolder.toASN1Structure());
+ }
+
+ public AsymmetricKeyParameter getPublicKey()
+ throws PKCSException
+ {
+ try
+ {
+ return PublicKeyFactory.createKey(this.getSubjectPublicKeyInfo());
+ }
+ catch (IOException e)
+ {
+ throw new PKCSException("error extracting key encoding: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequestBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequestBuilder.java
new file mode 100644
index 00000000..1590a663
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS10CertificationRequestBuilder.java
@@ -0,0 +1,28 @@
+package org.spongycastle.pkcs.bc;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.spongycastle.pkcs.PKCS10CertificationRequestBuilder;
+
+/**
+ * Extension of the PKCS#10 builder to support AsymmetricKey objects.
+ */
+public class BcPKCS10CertificationRequestBuilder
+ extends PKCS10CertificationRequestBuilder
+{
+ /**
+ * Create a PKCS#10 builder for the passed in subject and JCA public key.
+ *
+ * @param subject an X500Name containing the subject associated with the request we are building.
+ * @param publicKey a JCA public key that is to be associated with the request we are building.
+ * @throws IOException if there is a problem encoding the public key.
+ */
+ public BcPKCS10CertificationRequestBuilder(X500Name subject, AsymmetricKeyParameter publicKey)
+ throws IOException
+ {
+ super(subject, SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilder.java
new file mode 100644
index 00000000..456d5b08
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilder.java
@@ -0,0 +1,54 @@
+package org.spongycastle.pkcs.bc;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.pkcs.PKCS12MacCalculatorBuilder;
+
+public class BcPKCS12MacCalculatorBuilder
+ implements PKCS12MacCalculatorBuilder
+{
+ private ExtendedDigest digest;
+ private AlgorithmIdentifier algorithmIdentifier;
+
+ private SecureRandom random;
+ private int saltLength;
+ private int iterationCount = 1024;
+
+ public BcPKCS12MacCalculatorBuilder()
+ {
+ this(new SHA1Digest(), new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE));
+ }
+
+ public BcPKCS12MacCalculatorBuilder(ExtendedDigest digest, AlgorithmIdentifier algorithmIdentifier)
+ {
+ this.digest = digest;
+ this.algorithmIdentifier = algorithmIdentifier;
+ this.saltLength = digest.getDigestSize();
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithmIdentifier()
+ {
+ return algorithmIdentifier;
+ }
+
+ public MacCalculator build(final char[] password)
+ {
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ byte[] salt = new byte[saltLength];
+
+ random.nextBytes(salt);
+
+ return PKCS12PBEUtils.createMacCalculator(algorithmIdentifier.getAlgorithm(), digest, new PKCS12PBEParams(salt, iterationCount), password);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilderProvider.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilderProvider.java
new file mode 100644
index 00000000..d5533753
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12MacCalculatorBuilderProvider.java
@@ -0,0 +1,40 @@
+package org.spongycastle.pkcs.bc;
+
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.bc.BcDigestProvider;
+import org.spongycastle.pkcs.PKCS12MacCalculatorBuilder;
+import org.spongycastle.pkcs.PKCS12MacCalculatorBuilderProvider;
+
+public class BcPKCS12MacCalculatorBuilderProvider
+ implements PKCS12MacCalculatorBuilderProvider
+{
+ private BcDigestProvider digestProvider;
+
+ public BcPKCS12MacCalculatorBuilderProvider(BcDigestProvider digestProvider)
+ {
+ this.digestProvider = digestProvider;
+ }
+
+ public PKCS12MacCalculatorBuilder get(final AlgorithmIdentifier algorithmIdentifier)
+ {
+ return new PKCS12MacCalculatorBuilder()
+ {
+ public MacCalculator build(final char[] password)
+ throws OperatorCreationException
+ {
+ PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algorithmIdentifier.getParameters());
+
+ return PKCS12PBEUtils.createMacCalculator(algorithmIdentifier.getAlgorithm(), digestProvider.get(algorithmIdentifier), pbeParams, password);
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(algorithmIdentifier.getAlgorithm(), DERNull.INSTANCE);
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEInputDecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEInputDecryptorProviderBuilder.java
new file mode 100644
index 00000000..a4618c04
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEInputDecryptorProviderBuilder.java
@@ -0,0 +1,66 @@
+package org.spongycastle.pkcs.bc;
+
+import java.io.InputStream;
+
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.spongycastle.crypto.io.CipherInputStream;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.InputDecryptor;
+import org.spongycastle.operator.InputDecryptorProvider;
+
+public class BcPKCS12PBEInputDecryptorProviderBuilder
+{
+ private ExtendedDigest digest;
+
+ public BcPKCS12PBEInputDecryptorProviderBuilder()
+ {
+ this(new SHA1Digest());
+ }
+
+ public BcPKCS12PBEInputDecryptorProviderBuilder(ExtendedDigest digest)
+ {
+ this.digest = digest;
+ }
+
+ public InputDecryptorProvider build(final char[] password)
+ {
+ return new InputDecryptorProvider()
+ {
+ public InputDecryptor get(final AlgorithmIdentifier algorithmIdentifier)
+ {
+ final PaddedBufferedBlockCipher engine = PKCS12PBEUtils.getEngine(algorithmIdentifier.getAlgorithm());
+
+ PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algorithmIdentifier.getParameters());
+
+ CipherParameters params = PKCS12PBEUtils.createCipherParameters(algorithmIdentifier.getAlgorithm(), digest, engine.getBlockSize(), pbeParams, password);
+
+ engine.init(false, params);
+
+ return new InputDecryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return algorithmIdentifier;
+ }
+
+ public InputStream getInputStream(InputStream input)
+ {
+ return new CipherInputStream(input, engine);
+ }
+
+ public GenericKey getKey()
+ {
+ return new GenericKey(PKCS12ParametersGenerator.PKCS12PasswordToBytes(password));
+ }
+ };
+ }
+ };
+
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEOutputEncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEOutputEncryptorBuilder.java
new file mode 100644
index 00000000..d8af97c3
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/BcPKCS12PBEOutputEncryptorBuilder.java
@@ -0,0 +1,77 @@
+package org.spongycastle.pkcs.bc;
+
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.spongycastle.crypto.io.CipherOutputStream;
+import org.spongycastle.crypto.paddings.PKCS7Padding;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.OutputEncryptor;
+
+public class BcPKCS12PBEOutputEncryptorBuilder
+{
+ private ExtendedDigest digest;
+
+ private BufferedBlockCipher engine;
+ private ASN1ObjectIdentifier algorithm;
+ private SecureRandom random;
+
+ public BcPKCS12PBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm, BlockCipher engine)
+ {
+ this(algorithm, engine, new SHA1Digest());
+ }
+
+ public BcPKCS12PBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm, BlockCipher engine, ExtendedDigest pbeDigest)
+ {
+ this.algorithm = algorithm;
+ this.engine = new PaddedBufferedBlockCipher(engine, new PKCS7Padding());
+ this.digest = pbeDigest;
+ }
+
+ public OutputEncryptor build(final char[] password)
+ {
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ final byte[] salt = new byte[20];
+ final int iterationCount = 1024;
+
+ random.nextBytes(salt);
+
+ final PKCS12PBEParams pbeParams = new PKCS12PBEParams(salt, iterationCount);
+
+ CipherParameters params = PKCS12PBEUtils.createCipherParameters(algorithm, digest, engine.getBlockSize(), pbeParams, password);
+
+ engine.init(true, params);
+
+ return new OutputEncryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(algorithm, pbeParams);
+ }
+
+ public OutputStream getOutputStream(OutputStream out)
+ {
+ return new CipherOutputStream(out, engine);
+ }
+
+ public GenericKey getKey()
+ {
+ return new GenericKey(new AlgorithmIdentifier(algorithm, pbeParams), PKCS12ParametersGenerator.PKCS12PasswordToBytes(password));
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/bc/PKCS12PBEUtils.java b/pkix/src/main/java/org/spongycastle/pkcs/bc/PKCS12PBEUtils.java
new file mode 100644
index 00000000..52f07a54
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/bc/PKCS12PBEUtils.java
@@ -0,0 +1,153 @@
+package org.spongycastle.pkcs.bc;
+
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.RC2Engine;
+import org.spongycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.spongycastle.crypto.io.MacOutputStream;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.modes.CBCBlockCipher;
+import org.spongycastle.crypto.paddings.PKCS7Padding;
+import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.spongycastle.crypto.params.DESedeParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.util.Integers;
+
+class PKCS12PBEUtils
+{
+ private static Map keySizes = new HashMap();
+ private static Set noIvAlgs = new HashSet();
+ private static Set desAlgs = new HashSet();
+
+ static
+ {
+ keySizes.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4, Integers.valueOf(128));
+ keySizes.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4, Integers.valueOf(40));
+ keySizes.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192));
+ keySizes.put(PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, Integers.valueOf(128));
+ keySizes.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128));
+ keySizes.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40));
+
+ noIvAlgs.add(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4);
+ noIvAlgs.add(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4);
+
+ desAlgs.add(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC);
+ desAlgs.add(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC);
+ }
+
+ static int getKeySize(ASN1ObjectIdentifier algorithm)
+ {
+ return ((Integer)keySizes.get(algorithm)).intValue();
+ }
+
+ static boolean hasNoIv(ASN1ObjectIdentifier algorithm)
+ {
+ return noIvAlgs.contains(algorithm);
+ }
+
+ static boolean isDesAlg(ASN1ObjectIdentifier algorithm)
+ {
+ return desAlgs.contains(algorithm);
+ }
+
+ static PaddedBufferedBlockCipher getEngine(ASN1ObjectIdentifier algorithm)
+ {
+ BlockCipher engine;
+
+ if (algorithm.equals(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC)
+ || algorithm.equals(PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC))
+ {
+ engine = new DESedeEngine();
+ }
+ else if (algorithm.equals(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC)
+ || algorithm.equals(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC))
+ {
+ engine = new RC2Engine();
+ }
+ else
+ {
+ throw new IllegalStateException("unknown algorithm");
+ }
+
+ return new PaddedBufferedBlockCipher(new CBCBlockCipher(engine), new PKCS7Padding());
+ }
+
+ static MacCalculator createMacCalculator(final ASN1ObjectIdentifier digestAlgorithm, ExtendedDigest digest, final PKCS12PBEParams pbeParams, final char[] password)
+ {
+ PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(digest);
+
+ pGen.init(PKCS12ParametersGenerator.PKCS12PasswordToBytes(password), pbeParams.getIV(), pbeParams.getIterations().intValue());
+
+ final KeyParameter keyParam = (KeyParameter)pGen.generateDerivedMacParameters(digest.getDigestSize() * 8);
+
+ final HMac hMac = new HMac(digest);
+
+ hMac.init(keyParam);
+
+ return new MacCalculator()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(digestAlgorithm, pbeParams);
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return new MacOutputStream(hMac);
+ }
+
+ public byte[] getMac()
+ {
+ byte[] res = new byte[hMac.getMacSize()];
+
+ hMac.doFinal(res, 0);
+
+ return res;
+ }
+
+ public GenericKey getKey()
+ {
+ return new GenericKey(getAlgorithmIdentifier(), PKCS12ParametersGenerator.PKCS12PasswordToBytes(password));
+ }
+ };
+ }
+
+ static CipherParameters createCipherParameters(ASN1ObjectIdentifier algorithm, ExtendedDigest digest, int blockSize, PKCS12PBEParams pbeParams, char[] password)
+ {
+ PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(digest);
+
+ pGen.init(PKCS12ParametersGenerator.PKCS12PasswordToBytes(password), pbeParams.getIV(), pbeParams.getIterations().intValue());
+
+ CipherParameters params;
+
+ if (PKCS12PBEUtils.hasNoIv(algorithm))
+ {
+ params = pGen.generateDerivedParameters(PKCS12PBEUtils.getKeySize(algorithm));
+ }
+ else
+ {
+ params = pGen.generateDerivedParameters(PKCS12PBEUtils.getKeySize(algorithm), blockSize * 8);
+
+ if (PKCS12PBEUtils.isDesAlg(algorithm))
+ {
+ DESedeParameters.setOddParity(((KeyParameter)((ParametersWithIV)params).getParameters()).getKey());
+ }
+ }
+ return params;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java
new file mode 100644
index 00000000..8f0e20cf
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequest.java
@@ -0,0 +1,115 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Hashtable;
+
+import org.spongycastle.asn1.pkcs.CertificationRequest;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.pkcs.PKCS10CertificationRequest;
+
+public class JcaPKCS10CertificationRequest
+ extends PKCS10CertificationRequest
+{
+ private static Hashtable keyAlgorithms = new Hashtable();
+
+ static
+ {
+ //
+ // key types
+ //
+ keyAlgorithms.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+ keyAlgorithms.put(X9ObjectIdentifiers.id_dsa, "DSA");
+ }
+
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ public JcaPKCS10CertificationRequest(CertificationRequest certificationRequest)
+ {
+ super(certificationRequest);
+ }
+
+ public JcaPKCS10CertificationRequest(byte[] encoding)
+ throws IOException
+ {
+ super(encoding);
+ }
+
+ public JcaPKCS10CertificationRequest(PKCS10CertificationRequest requestHolder)
+ {
+ super(requestHolder.toASN1Structure());
+ }
+
+ public JcaPKCS10CertificationRequest setProvider(String providerName)
+ {
+ helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public JcaPKCS10CertificationRequest setProvider(Provider provider)
+ {
+ helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public PublicKey getPublicKey()
+ throws InvalidKeyException, NoSuchAlgorithmException
+ {
+ try
+ {
+ SubjectPublicKeyInfo keyInfo = this.getSubjectPublicKeyInfo();
+ X509EncodedKeySpec xspec = new X509EncodedKeySpec(keyInfo.getEncoded());
+ KeyFactory kFact;
+
+ try
+ {
+ kFact = helper.createKeyFactory(keyInfo.getAlgorithm().getAlgorithm().getId());
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ //
+ // try an alternate
+ //
+ if (keyAlgorithms.get(keyInfo.getAlgorithm().getAlgorithm()) != null)
+ {
+ String keyAlgorithm = (String)keyAlgorithms.get(keyInfo.getAlgorithm().getAlgorithm());
+
+ kFact = helper.createKeyFactory(keyAlgorithm);
+ }
+ else
+ {
+ throw e;
+ }
+ }
+
+ return kFact.generatePublic(xspec);
+ }
+ catch (InvalidKeySpecException e)
+ {
+ throw new InvalidKeyException("error decoding public key");
+ }
+ catch (IOException e)
+ {
+ throw new InvalidKeyException("error extracting key encoding");
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new NoSuchAlgorithmException("cannot find provider: " + e.getMessage());
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java
new file mode 100644
index 00000000..0efa5fa2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS10CertificationRequestBuilder.java
@@ -0,0 +1,38 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.security.PublicKey;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.pkcs.PKCS10CertificationRequestBuilder;
+
+/**
+ * Extension of the PKCS#10 builder to support PublicKey and X500Principal objects.
+ */
+public class JcaPKCS10CertificationRequestBuilder
+ extends PKCS10CertificationRequestBuilder
+{
+ /**
+ * Create a PKCS#10 builder for the passed in subject and JCA public key.
+ *
+ * @param subject an X500Name containing the subject associated with the request we are building.
+ * @param publicKey a JCA public key that is to be associated with the request we are building.
+ */
+ public JcaPKCS10CertificationRequestBuilder(X500Name subject, PublicKey publicKey)
+ {
+ super(subject, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Create a PKCS#10 builder for the passed in subject and JCA public key.
+ *
+ * @param subject an X500Principal containing the subject associated with the request we are building.
+ * @param publicKey a JCA public key that is to be associated with the request we are building.
+ */
+ public JcaPKCS10CertificationRequestBuilder(X500Principal subject, PublicKey publicKey)
+ {
+ super(X500Name.getInstance(subject.getEncoded()), SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java
new file mode 100644
index 00000000..f8c06f7c
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS12SafeBagBuilder.java
@@ -0,0 +1,45 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.pkcs.PKCS12SafeBagBuilder;
+import org.spongycastle.pkcs.PKCSIOException;
+
+public class JcaPKCS12SafeBagBuilder
+ extends PKCS12SafeBagBuilder
+{
+ public JcaPKCS12SafeBagBuilder(X509Certificate certificate)
+ throws IOException
+ {
+ super(convertCert(certificate));
+ }
+
+ private static Certificate convertCert(X509Certificate certificate)
+ throws IOException
+ {
+ try
+ {
+ return Certificate.getInstance(certificate.getEncoded());
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new PKCSIOException("cannot encode certificate: " + e.getMessage(), e);
+ }
+ }
+
+ public JcaPKCS12SafeBagBuilder(PrivateKey privateKey, OutputEncryptor encryptor)
+ {
+ super(PrivateKeyInfo.getInstance(privateKey.getEncoded()), encryptor);
+ }
+
+ public JcaPKCS12SafeBagBuilder(PrivateKey privateKey)
+ {
+ super(PrivateKeyInfo.getInstance(privateKey.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java
new file mode 100644
index 00000000..f8a5856c
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcaPKCS8EncryptedPrivateKeyInfoBuilder.java
@@ -0,0 +1,15 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.security.PrivateKey;
+
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBuilder;
+
+public class JcaPKCS8EncryptedPrivateKeyInfoBuilder
+ extends PKCS8EncryptedPrivateKeyInfoBuilder
+{
+ public JcaPKCS8EncryptedPrivateKeyInfoBuilder(PrivateKey privateKey)
+ {
+ super(PrivateKeyInfo.getInstance(privateKey.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java
new file mode 100644
index 00000000..25b8da06
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilder.java
@@ -0,0 +1,122 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.io.OutputStream;
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.ExtendedDigest;
+import org.spongycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.spongycastle.jcajce.io.MacOutputStream;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.pkcs.PKCS12MacCalculatorBuilder;
+
+public class JcePKCS12MacCalculatorBuilder
+ implements PKCS12MacCalculatorBuilder
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+ private ExtendedDigest digest;
+ private ASN1ObjectIdentifier algorithm;
+
+ private SecureRandom random;
+ private int saltLength;
+ private int iterationCount = 1024;
+
+ public JcePKCS12MacCalculatorBuilder()
+ {
+ this(OIWObjectIdentifiers.idSHA1);
+ }
+
+ public JcePKCS12MacCalculatorBuilder(ASN1ObjectIdentifier hashAlgorithm)
+ {
+ this.algorithm = hashAlgorithm;
+ }
+
+ public JcePKCS12MacCalculatorBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcePKCS12MacCalculatorBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(algorithm, DERNull.INSTANCE);
+ }
+
+ public MacCalculator build(final char[] password)
+ throws OperatorCreationException
+ {
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ try
+ {
+ final Mac mac = helper.createMac(algorithm.getId());
+
+ saltLength = mac.getMacLength();
+ final byte[] salt = new byte[saltLength];
+
+ random.nextBytes(salt);
+
+ SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId());
+ PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount);
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ SecretKey key = keyFact.generateSecret(pbeSpec);
+
+ mac.init(key, defParams);
+
+ return new MacCalculator()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(algorithm, new PKCS12PBEParams(salt, iterationCount));
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return new MacOutputStream(mac);
+ }
+
+ public byte[] getMac()
+ {
+ return mac.doFinal();
+ }
+
+ public GenericKey getKey()
+ {
+ return new GenericKey(getAlgorithmIdentifier(), PKCS12ParametersGenerator.PKCS12PasswordToBytes(password));
+ }
+ };
+ }
+ catch (Exception e)
+ {
+ throw new OperatorCreationException("unable to create MAC calculator: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java
new file mode 100644
index 00000000..6c9c3023
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCS12MacCalculatorBuilderProvider.java
@@ -0,0 +1,108 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.io.OutputStream;
+import java.security.Provider;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.spongycastle.jcajce.io.MacOutputStream;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.pkcs.PKCS12MacCalculatorBuilder;
+import org.spongycastle.pkcs.PKCS12MacCalculatorBuilderProvider;
+
+public class JcePKCS12MacCalculatorBuilderProvider
+ implements PKCS12MacCalculatorBuilderProvider
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ public JcePKCS12MacCalculatorBuilderProvider()
+ {
+ }
+
+ public JcePKCS12MacCalculatorBuilderProvider setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcePKCS12MacCalculatorBuilderProvider setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public PKCS12MacCalculatorBuilder get(final AlgorithmIdentifier algorithmIdentifier)
+ {
+ return new PKCS12MacCalculatorBuilder()
+ {
+ public MacCalculator build(final char[] password)
+ throws OperatorCreationException
+ {
+ final PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algorithmIdentifier.getParameters());
+
+ try
+ {
+ final ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm();
+
+ final Mac mac = helper.createMac(algorithm.getId());
+
+ SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId());
+ PBEParameterSpec defParams = new PBEParameterSpec(pbeParams.getIV(), pbeParams.getIterations().intValue());
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ SecretKey key = keyFact.generateSecret(pbeSpec);
+
+ mac.init(key, defParams);
+
+ return new MacCalculator()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(algorithm, pbeParams);
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return new MacOutputStream(mac);
+ }
+
+ public byte[] getMac()
+ {
+ return mac.doFinal();
+ }
+
+ public GenericKey getKey()
+ {
+ return new GenericKey(getAlgorithmIdentifier(), PKCS12ParametersGenerator.PKCS12PasswordToBytes(password));
+ }
+ };
+ }
+ catch (Exception e)
+ {
+ throw new OperatorCreationException("unable to create MAC calculator: " + e.getMessage(), e);
+ }
+ }
+
+ public AlgorithmIdentifier getDigestAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(algorithmIdentifier.getAlgorithm(), DERNull.INSTANCE);
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
new file mode 100644
index 00000000..5f5413ca
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
@@ -0,0 +1,177 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.io.InputStream;
+import java.security.Provider;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+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.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.cryptopro.GOST28147Parameters;
+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.x509.AlgorithmIdentifier;
+import org.spongycastle.jcajce.provider.symmetric.util.BCPBEKey;
+import org.spongycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.spongycastle.jcajce.spec.PBKDF2KeySpec;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.DefaultSecretKeySizeProvider;
+import org.spongycastle.operator.InputDecryptor;
+import org.spongycastle.operator.InputDecryptorProvider;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.SecretKeySizeProvider;
+
+public class JcePKCSPBEInputDecryptorProviderBuilder
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+ private boolean wrongPKCS12Zero = false;
+ private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE;
+
+ public JcePKCSPBEInputDecryptorProviderBuilder()
+ {
+ }
+
+ public JcePKCSPBEInputDecryptorProviderBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcePKCSPBEInputDecryptorProviderBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public JcePKCSPBEInputDecryptorProviderBuilder setTryWrongPKCS12Zero(boolean tryWrong)
+ {
+ this.wrongPKCS12Zero = tryWrong;
+
+ return this;
+ }
+
+ /**
+ * Set the lookup provider of AlgorithmIdentifier returning key_size_in_bits used to
+ * handle PKCS5 decryption.
+ *
+ * @param keySizeProvider a provider of integer secret key sizes.
+ *
+ * @return the current builder.
+ */
+ public JcePKCSPBEInputDecryptorProviderBuilder setKeySizeProvider(SecretKeySizeProvider keySizeProvider)
+ {
+ this.keySizeProvider = keySizeProvider;
+
+ return this;
+ }
+
+ public InputDecryptorProvider build(final char[] password)
+ {
+ return new InputDecryptorProvider()
+ {
+ private Cipher cipher;
+ private SecretKey key;
+ private AlgorithmIdentifier encryptionAlg;
+
+ public InputDecryptor get(final AlgorithmIdentifier algorithmIdentifier)
+ throws OperatorCreationException
+ {
+ ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm();
+
+ try
+ {
+ if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds))
+ {
+ PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algorithmIdentifier.getParameters());
+
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+
+ SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId());
+
+ PBEParameterSpec defParams = new PBEParameterSpec(
+ pbeParams.getIV(),
+ pbeParams.getIterations().intValue());
+
+ key = keyFact.generateSecret(pbeSpec);
+
+ if (key instanceof BCPBEKey)
+ {
+ ((BCPBEKey)key).setTryWrongPKCS12Zero(wrongPKCS12Zero);
+ }
+
+ cipher = helper.createCipher(algorithm.getId());
+
+ cipher.init(Cipher.DECRYPT_MODE, key, defParams);
+
+ encryptionAlg = algorithmIdentifier;
+ }
+ else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
+ {
+ PBES2Parameters alg = PBES2Parameters.getInstance(algorithmIdentifier.getParameters());
+ PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
+ AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+ SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId());
+
+ if (func.isDefaultPrf())
+ {
+ key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+ }
+ else
+ {
+ key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+ }
+
+ cipher = helper.createCipher(alg.getEncryptionScheme().getAlgorithm().getId());
+
+ encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+ ASN1Encodable encParams = alg.getEncryptionScheme().getParameters();
+ if (encParams instanceof ASN1OctetString)
+ {
+ cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets()));
+ }
+ else
+ {
+ // TODO: at the moment it's just GOST, but...
+ GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams);
+
+ cipher.init(Cipher.DECRYPT_MODE, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV()));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new OperatorCreationException("unable to create InputDecryptor: " + e.getMessage(), e);
+ }
+
+ return new InputDecryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return encryptionAlg;
+ }
+
+ public InputStream getInputStream(InputStream input)
+ {
+ return new CipherInputStream(input, cipher);
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
new file mode 100644
index 00000000..9ab806ef
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
@@ -0,0 +1,179 @@
+package org.spongycastle.pkcs.jcajce;
+
+import java.io.OutputStream;
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.bc.BCObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.EncryptionScheme;
+import org.spongycastle.asn1.pkcs.KeyDerivationFunc;
+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.x509.AlgorithmIdentifier;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.DefaultSecretKeySizeProvider;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.operator.SecretKeySizeProvider;
+
+public class JcePKCSPBEOutputEncryptorBuilder
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+ private ASN1ObjectIdentifier algorithm;
+ private ASN1ObjectIdentifier keyEncAlgorithm;
+ private SecureRandom random;
+ private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE;
+
+ public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm)
+ {
+ if (isPKCS12(algorithm))
+ {
+ this.algorithm = algorithm;
+ this.keyEncAlgorithm = algorithm;
+ }
+ else
+ {
+ this.algorithm = PKCSObjectIdentifiers.id_PBES2;
+ this.keyEncAlgorithm = algorithm;
+ }
+ }
+
+ public JcePKCSPBEOutputEncryptorBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcePKCSPBEOutputEncryptorBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ /**
+ * Set the lookup provider of AlgorithmIdentifier returning key_size_in_bits used to
+ * handle PKCS5 decryption.
+ *
+ * @param keySizeProvider a provider of integer secret key sizes.
+ *
+ * @return the current builder.
+ */
+ public JcePKCSPBEOutputEncryptorBuilder setKeySizeProvider(SecretKeySizeProvider keySizeProvider)
+ {
+ this.keySizeProvider = keySizeProvider;
+
+ return this;
+ }
+
+ public OutputEncryptor build(final char[] password)
+ throws OperatorCreationException
+ {
+ final Cipher cipher;
+ SecretKey key;
+
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ final AlgorithmIdentifier encryptionAlg;
+ final byte[] salt = new byte[20];
+ final int iterationCount = 1024;
+
+ random.nextBytes(salt);
+
+ try
+ {
+ if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds))
+ {
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+
+ SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId());
+
+ PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount);
+
+ key = keyFact.generateSecret(pbeSpec);
+
+ cipher = helper.createCipher(algorithm.getId());
+
+ cipher.init(Cipher.ENCRYPT_MODE, key, defParams);
+
+ encryptionAlg = new AlgorithmIdentifier(algorithm, new PKCS12PBEParams(salt, iterationCount));
+ }
+ else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
+ {
+ SecretKeyFactory keyFact = helper.createSecretKeyFactory(PKCSObjectIdentifiers.id_PBKDF2.getId());
+
+ key = keyFact.generateSecret(new PBEKeySpec(password, salt, iterationCount, keySizeProvider.getKeySize(new AlgorithmIdentifier(keyEncAlgorithm))));
+
+ cipher = helper.createCipher(keyEncAlgorithm.getId());
+
+ cipher.init(Cipher.ENCRYPT_MODE, key, random);
+
+ PBES2Parameters algParams = new PBES2Parameters(
+ new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount)),
+ new EncryptionScheme(keyEncAlgorithm, ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded())));
+
+ encryptionAlg = new AlgorithmIdentifier(algorithm, algParams);
+ }
+ else
+ {
+ throw new OperatorCreationException("unrecognised algorithm");
+ }
+
+ return new OutputEncryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return encryptionAlg;
+ }
+
+ public OutputStream getOutputStream(OutputStream out)
+ {
+ return new CipherOutputStream(out, cipher);
+ }
+
+ public GenericKey getKey()
+ {
+ if (isPKCS12(encryptionAlg.getAlgorithm()))
+ {
+ return new GenericKey(encryptionAlg, PBEParametersGenerator.PKCS5PasswordToBytes(password));
+ }
+ else
+ {
+ return new GenericKey(encryptionAlg, PBEParametersGenerator.PKCS12PasswordToBytes(password));
+ }
+ }
+ };
+ }
+ catch (Exception e)
+ {
+ throw new OperatorCreationException("unable to create OutputEncryptor: " + e.getMessage(), e);
+ }
+ }
+
+ private boolean isPKCS12(ASN1ObjectIdentifier algorithm)
+ {
+ return algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)
+ || algorithm.on(BCObjectIdentifiers.bc_pbe_sha1_pkcs12)
+ || algorithm.on(BCObjectIdentifiers.bc_pbe_sha256_pkcs12);
+ }
+}