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/cert')
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/AttributeCertificateHolder.java357
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/AttributeCertificateIssuer.java147
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/CertException.java27
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/CertIOException.java29
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/CertRuntimeException.java19
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/CertUtils.java244
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509AttributeCertificateHolder.java366
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509CRLEntryHolder.java144
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509CRLHolder.java317
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509CertificateHolder.java327
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509ContentVerifierProviderBuilder.java14
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509ExtensionUtils.java132
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509v1CertificateBuilder.java101
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509v2AttributeCertificateBuilder.java162
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509v2CRLBuilder.java266
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/X509v3CertificateBuilder.java195
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/bc/BcX509ExtensionUtils.java91
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/bc/BcX509v1CertificateBuilder.java33
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/bc/BcX509v3CertificateBuilder.java51
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/CMPException.java24
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/CMPRuntimeException.java19
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/CMPUtil.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContent.java41
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContentBuilder.java78
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/CertificateStatus.java60
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/GeneralPKIMessage.java82
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessage.java198
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessageBuilder.java306
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetails.java36
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetailsBuilder.java59
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/AuthenticatorControl.java57
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/CRMFException.java19
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/CRMFRuntimeException.java19
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/CRMFUtil.java42
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessage.java309
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessageBuilder.java279
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/Control.java24
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueBuilder.java133
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValuePadder.java24
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueParser.java103
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControl.java104
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControlBuilder.java78
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/PKMACBuilder.java199
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueGenerator.java41
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueVerifier.java43
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValuesCalculator.java15
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/ProofOfPossessionSigningKeyBuilder.java75
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/RegTokenControl.java57
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/ValueDecryptorGenerator.java10
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/bc/BcFixedLengthMGF1Padder.java121
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/CRMFHelper.java450
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessage.java84
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessageBuilder.java57
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaPKIArchiveControlBuilder.java29
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceAsymmetricValueDecryptorGenerator.java120
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceCRMFEncryptorBuilder.java136
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcePKMACValuesCalculator.java69
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/CertHelper.java17
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/DefaultCertHelper.java14
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttrCertStore.java62
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttributeCertificateIssuer.java32
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCRLStore.java63
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStore.java64
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStoreBuilder.java148
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX500NameUtil.java29
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509AttributeCertificateHolder.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLConverter.java103
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLHolder.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateConverter.java116
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateHolder.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java50
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ExtensionUtils.java145
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v1CertificateBuilder.java48
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v2CRLBuilder.java23
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v3CertificateBuilder.java119
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/NamedCertHelper.java22
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/jcajce/ProviderCertHelper.java22
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPResp.java212
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPRespBuilder.java264
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateID.java156
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateStatus.java6
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPException.java27
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReq.java259
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReqBuilder.java199
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPResp.java141
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPRespBuilder.java59
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPUtils.java64
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/Req.java25
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/RespData.java52
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/RespID.java89
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/RevokedStatus.java55
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/SingleResp.java102
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/UnknownStatus.java12
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java18
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaCertificateID.java20
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaRespID.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPath.java80
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPathUtils.java21
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPathValidation.java11
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationContext.java61
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationException.java24
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResult.java66
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResultBuilder.java14
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/BasicConstraintsValidation.java103
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/CRLValidation.java78
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidation.java146
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java35
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/KeyUsageValidation.java63
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/ParentCertIssuedValidation.java127
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/path/validations/ValidationUtils.java11
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/MSOutlookKeyIdCalculator.java422
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelector.java268
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java194
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/X509CertificateHolderSelector.java152
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaSelectorConverter.java35
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertSelectorConverter.java57
-rw-r--r--pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertificateHolderSelector.java72
118 files changed, 11655 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/cert/AttributeCertificateHolder.java b/pkix/src/main/java/org/spongycastle/cert/AttributeCertificateHolder.java
new file mode 100644
index 00000000..610cdbe7
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/AttributeCertificateHolder.java
@@ -0,0 +1,357 @@
+package org.spongycastle.cert;
+
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.Holder;
+import org.spongycastle.asn1.x509.IssuerSerial;
+import org.spongycastle.asn1.x509.ObjectDigestInfo;
+import org.spongycastle.operator.DigestCalculator;
+import org.spongycastle.operator.DigestCalculatorProvider;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Selector;
+
+/**
+ * The Holder object.
+ *
+ * <pre>
+ * Holder ::= SEQUENCE {
+ * baseCertificateID [0] IssuerSerial OPTIONAL,
+ * -- the issuer and serial number of
+ * -- the holder's Public Key Certificate
+ * entityName [1] GeneralNames OPTIONAL,
+ * -- the name of the claimant or role
+ * objectDigestInfo [2] ObjectDigestInfo OPTIONAL
+ * -- used to directly authenticate the holder,
+ * -- for example, an executable
+ * }
+ * </pre>
+ * <p>
+ * <b>Note:</b> If objectDigestInfo comparisons are to be carried out the static
+ * method setDigestCalculatorProvider <b>must</b> be called once to configure the class
+ * to do the necessary calculations.
+ * </p>
+ */
+public class AttributeCertificateHolder
+ implements Selector
+{
+ private static DigestCalculatorProvider digestCalculatorProvider;
+
+ final Holder holder;
+
+ AttributeCertificateHolder(ASN1Sequence seq)
+ {
+ holder = Holder.getInstance(seq);
+ }
+
+ public AttributeCertificateHolder(X500Name issuerName,
+ BigInteger serialNumber)
+ {
+ holder = new Holder(new IssuerSerial(
+ new GeneralNames(new GeneralName(issuerName)),
+ new ASN1Integer(serialNumber)));
+ }
+
+ public AttributeCertificateHolder(X509CertificateHolder cert)
+ {
+ holder = new Holder(new IssuerSerial(generateGeneralNames(cert.getIssuer()),
+ new ASN1Integer(cert.getSerialNumber())));
+ }
+
+ public AttributeCertificateHolder(X500Name principal)
+ {
+ holder = new Holder(generateGeneralNames(principal));
+ }
+
+ /**
+ * Constructs a holder for v2 attribute certificates with a hash value for
+ * some type of object.
+ * <p>
+ * <code>digestedObjectType</code> can be one of the following:
+ * <ul>
+ * <li>0 - publicKey - A hash of the public key of the holder must be
+ * passed.
+ * <li>1 - publicKeyCert - A hash of the public key certificate of the
+ * holder must be passed.
+ * <li>2 - otherObjectDigest - A hash of some other object type must be
+ * passed. <code>otherObjectTypeID</code> must not be empty.
+ * </ul>
+ * <p>
+ * This cannot be used if a v1 attribute certificate is used.
+ *
+ * @param digestedObjectType The digest object type.
+ * @param digestAlgorithm The algorithm identifier for the hash.
+ * @param otherObjectTypeID The object type ID if
+ * <code>digestedObjectType</code> is
+ * <code>otherObjectDigest</code>.
+ * @param objectDigest The hash value.
+ */
+ public AttributeCertificateHolder(int digestedObjectType,
+ ASN1ObjectIdentifier digestAlgorithm, ASN1ObjectIdentifier otherObjectTypeID, byte[] objectDigest)
+ {
+ holder = new Holder(new ObjectDigestInfo(digestedObjectType,
+ otherObjectTypeID, new AlgorithmIdentifier(digestAlgorithm), Arrays
+ .clone(objectDigest)));
+ }
+
+ /**
+ * Returns the digest object type if an object digest info is used.
+ * <p>
+ * <ul>
+ * <li>0 - publicKey - A hash of the public key of the holder must be
+ * passed.
+ * <li>1 - publicKeyCert - A hash of the public key certificate of the
+ * holder must be passed.
+ * <li>2 - otherObjectDigest - A hash of some other object type must be
+ * passed. <code>otherObjectTypeID</code> must not be empty.
+ * </ul>
+ *
+ * @return The digest object type or -1 if no object digest info is set.
+ */
+ public int getDigestedObjectType()
+ {
+ if (holder.getObjectDigestInfo() != null)
+ {
+ return holder.getObjectDigestInfo().getDigestedObjectType()
+ .getValue().intValue();
+ }
+ return -1;
+ }
+
+ /**
+ * Returns algorithm identifier for the digest used if ObjectDigestInfo is present.
+ *
+ * @return digest AlgorithmIdentifier or <code>null</code> if ObjectDigestInfo is absent.
+ */
+ public AlgorithmIdentifier getDigestAlgorithm()
+ {
+ if (holder.getObjectDigestInfo() != null)
+ {
+ return holder.getObjectDigestInfo().getDigestAlgorithm();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the hash if an object digest info is used.
+ *
+ * @return The hash or <code>null</code> if ObjectDigestInfo is absent.
+ */
+ public byte[] getObjectDigest()
+ {
+ if (holder.getObjectDigestInfo() != null)
+ {
+ return holder.getObjectDigestInfo().getObjectDigest().getBytes();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the digest algorithm ID if an object digest info is used.
+ *
+ * @return The digest algorithm ID or <code>null</code> if no object
+ * digest info is set.
+ */
+ public ASN1ObjectIdentifier getOtherObjectTypeID()
+ {
+ if (holder.getObjectDigestInfo() != null)
+ {
+ new ASN1ObjectIdentifier(holder.getObjectDigestInfo().getOtherObjectTypeID().getId());
+ }
+ return null;
+ }
+
+ private GeneralNames generateGeneralNames(X500Name principal)
+ {
+ return new GeneralNames(new GeneralName(principal));
+ }
+
+ private boolean matchesDN(X500Name subject, GeneralNames targets)
+ {
+ GeneralName[] names = targets.getNames();
+
+ for (int i = 0; i != names.length; i++)
+ {
+ GeneralName gn = names[i];
+
+ if (gn.getTagNo() == GeneralName.directoryName)
+ {
+ if (X500Name.getInstance(gn.getName()).equals(subject))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private X500Name[] getPrincipals(GeneralName[] names)
+ {
+ List l = new ArrayList(names.length);
+
+ for (int i = 0; i != names.length; i++)
+ {
+ if (names[i].getTagNo() == GeneralName.directoryName)
+ {
+ l.add(X500Name.getInstance(names[i].getName()));
+ }
+ }
+
+ return (X500Name[])l.toArray(new X500Name[l.size()]);
+ }
+
+ /**
+ * Return any principal objects inside the attribute certificate holder
+ * entity names field.
+ *
+ * @return an array of Principal objects (usually X500Principal), null if no
+ * entity names field is set.
+ */
+ public X500Name[] getEntityNames()
+ {
+ if (holder.getEntityName() != null)
+ {
+ return getPrincipals(holder.getEntityName().getNames());
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the principals associated with the issuer attached to this holder
+ *
+ * @return an array of principals, null if no BaseCertificateID is set.
+ */
+ public X500Name[] getIssuer()
+ {
+ if (holder.getBaseCertificateID() != null)
+ {
+ return getPrincipals(holder.getBaseCertificateID().getIssuer().getNames());
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the serial number associated with the issuer attached to this
+ * holder.
+ *
+ * @return the certificate serial number, null if no BaseCertificateID is
+ * set.
+ */
+ public BigInteger getSerialNumber()
+ {
+ if (holder.getBaseCertificateID() != null)
+ {
+ return holder.getBaseCertificateID().getSerial().getValue();
+ }
+
+ return null;
+ }
+
+ public Object clone()
+ {
+ return new AttributeCertificateHolder((ASN1Sequence)holder.toASN1Primitive());
+ }
+
+ public boolean match(Object obj)
+ {
+ if (!(obj instanceof X509CertificateHolder))
+ {
+ return false;
+ }
+
+ X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
+
+ if (holder.getBaseCertificateID() != null)
+ {
+ return holder.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
+ && matchesDN(x509Cert.getIssuer(), holder.getBaseCertificateID().getIssuer());
+ }
+
+ if (holder.getEntityName() != null)
+ {
+ if (matchesDN(x509Cert.getSubject(),
+ holder.getEntityName()))
+ {
+ return true;
+ }
+ }
+
+ if (holder.getObjectDigestInfo() != null)
+ {
+ try
+ {
+ DigestCalculator digCalc = digestCalculatorProvider.get(holder.getObjectDigestInfo().getDigestAlgorithm());
+ OutputStream digOut = digCalc.getOutputStream();
+
+ switch (getDigestedObjectType())
+ {
+ case ObjectDigestInfo.publicKey:
+ // TODO: DSA Dss-parms
+ digOut.write(x509Cert.getSubjectPublicKeyInfo().getEncoded());
+ break;
+ case ObjectDigestInfo.publicKeyCert:
+ digOut.write(x509Cert.getEncoded());
+ break;
+ }
+
+ digOut.close();
+
+ if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest()))
+ {
+ return false;
+ }
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof AttributeCertificateHolder))
+ {
+ return false;
+ }
+
+ AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
+
+ return this.holder.equals(other.holder);
+ }
+
+ public int hashCode()
+ {
+ return this.holder.hashCode();
+ }
+
+ /**
+ * Set a digest calculator provider to be used if matches are attempted using
+ * ObjectDigestInfo,
+ *
+ * @param digCalcProvider a provider of digest calculators.
+ */
+ public static void setDigestCalculatorProvider(DigestCalculatorProvider digCalcProvider)
+ {
+ digestCalculatorProvider = digCalcProvider;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/AttributeCertificateIssuer.java b/pkix/src/main/java/org/spongycastle/cert/AttributeCertificateIssuer.java
new file mode 100644
index 00000000..e659e592
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/AttributeCertificateIssuer.java
@@ -0,0 +1,147 @@
+package org.spongycastle.cert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AttCertIssuer;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.V2Form;
+import org.spongycastle.util.Selector;
+
+/**
+ * Carrying class for an attribute certificate issuer.
+ */
+public class AttributeCertificateIssuer
+ implements Selector
+{
+ final ASN1Encodable form;
+
+ /**
+ * Set the issuer directly with the ASN.1 structure.
+ *
+ * @param issuer The issuer
+ */
+ public AttributeCertificateIssuer(AttCertIssuer issuer)
+ {
+ form = issuer.getIssuer();
+ }
+
+ public AttributeCertificateIssuer(X500Name principal)
+ {
+ form = new V2Form(new GeneralNames(new GeneralName(principal)));
+ }
+
+ public X500Name[] getNames()
+ {
+ GeneralNames name;
+
+ if (form instanceof V2Form)
+ {
+ name = ((V2Form)form).getIssuerName();
+ }
+ else
+ {
+ name = (GeneralNames)form;
+ }
+
+ GeneralName[] names = name.getNames();
+
+ List l = new ArrayList(names.length);
+
+ for (int i = 0; i != names.length; i++)
+ {
+ if (names[i].getTagNo() == GeneralName.directoryName)
+ {
+ l.add(X500Name.getInstance(names[i].getName()));
+ }
+ }
+
+ return (X500Name[])l.toArray(new X500Name[l.size()]);
+ }
+
+ private boolean matchesDN(X500Name subject, GeneralNames targets)
+ {
+ GeneralName[] names = targets.getNames();
+
+ for (int i = 0; i != names.length; i++)
+ {
+ GeneralName gn = names[i];
+
+ if (gn.getTagNo() == GeneralName.directoryName)
+ {
+ if (X500Name.getInstance(gn.getName()).equals(subject))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public Object clone()
+ {
+ return new AttributeCertificateIssuer(AttCertIssuer.getInstance(form));
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ if (!(obj instanceof AttributeCertificateIssuer))
+ {
+ return false;
+ }
+
+ AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj;
+
+ return this.form.equals(other.form);
+ }
+
+ public int hashCode()
+ {
+ return this.form.hashCode();
+ }
+
+ public boolean match(Object obj)
+ {
+ if (!(obj instanceof X509CertificateHolder))
+ {
+ return false;
+ }
+
+ X509CertificateHolder x509Cert = (X509CertificateHolder)obj;
+
+ if (form instanceof V2Form)
+ {
+ V2Form issuer = (V2Form)form;
+ if (issuer.getBaseCertificateID() != null)
+ {
+ return issuer.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber())
+ && matchesDN(x509Cert.getIssuer(), issuer.getBaseCertificateID().getIssuer());
+ }
+
+ GeneralNames name = issuer.getIssuerName();
+ if (matchesDN(x509Cert.getSubject(), name))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ GeneralNames name = (GeneralNames)form;
+ if (matchesDN(x509Cert.getSubject(), name))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/CertException.java b/pkix/src/main/java/org/spongycastle/cert/CertException.java
new file mode 100644
index 00000000..b394ec89
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/CertException.java
@@ -0,0 +1,27 @@
+package org.spongycastle.cert;
+
+/**
+ * General checked Exception thrown in the cert package and its sub-packages.
+ */
+public class CertException
+ extends Exception
+{
+ private Throwable cause;
+
+ public CertException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public CertException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/CertIOException.java b/pkix/src/main/java/org/spongycastle/cert/CertIOException.java
new file mode 100644
index 00000000..f21641d6
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/CertIOException.java
@@ -0,0 +1,29 @@
+package org.spongycastle.cert;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the cert package and its sub-packages.
+ */
+public class CertIOException
+ extends IOException
+{
+ private Throwable cause;
+
+ public CertIOException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public CertIOException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/CertRuntimeException.java b/pkix/src/main/java/org/spongycastle/cert/CertRuntimeException.java
new file mode 100644
index 00000000..a797083e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/CertRuntimeException.java
@@ -0,0 +1,19 @@
+package org.spongycastle.cert;
+
+public class CertRuntimeException
+ extends RuntimeException
+{
+ private Throwable cause;
+
+ public CertRuntimeException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/CertUtils.java b/pkix/src/main/java/org/spongycastle/cert/CertUtils.java
new file mode 100644
index 00000000..d03f7843
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/CertUtils.java
@@ -0,0 +1,244 @@
+package org.spongycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.AttributeCertificate;
+import org.spongycastle.asn1.x509.AttributeCertificateInfo;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.asn1.x509.TBSCertificate;
+import org.spongycastle.operator.ContentSigner;
+
+class CertUtils
+{
+ private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
+ private static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
+
+ static X509CertificateHolder generateFullCert(ContentSigner signer, TBSCertificate tbsCert)
+ {
+ try
+ {
+ return new X509CertificateHolder(generateStructure(tbsCert, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCert)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("cannot produce certificate signature");
+ }
+ }
+
+ static X509AttributeCertificateHolder generateFullAttrCert(ContentSigner signer, AttributeCertificateInfo attrInfo)
+ {
+ try
+ {
+ return new X509AttributeCertificateHolder(generateAttrStructure(attrInfo, signer.getAlgorithmIdentifier(), generateSig(signer, attrInfo)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("cannot produce attribute certificate signature");
+ }
+ }
+
+ static X509CRLHolder generateFullCRL(ContentSigner signer, TBSCertList tbsCertList)
+ {
+ try
+ {
+ return new X509CRLHolder(generateCRLStructure(tbsCertList, signer.getAlgorithmIdentifier(), generateSig(signer, tbsCertList)));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("cannot produce certificate signature");
+ }
+ }
+
+ private static byte[] generateSig(ContentSigner signer, ASN1Encodable tbsObj)
+ throws IOException
+ {
+ OutputStream sOut = signer.getOutputStream();
+ DEROutputStream dOut = new DEROutputStream(sOut);
+
+ dOut.writeObject(tbsObj);
+
+ sOut.close();
+
+ return signer.getSignature();
+ }
+
+ private static Certificate generateStructure(TBSCertificate tbsCert, AlgorithmIdentifier sigAlgId, byte[] signature)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsCert);
+ v.add(sigAlgId);
+ v.add(new DERBitString(signature));
+
+ return Certificate.getInstance(new DERSequence(v));
+ }
+
+ private static AttributeCertificate generateAttrStructure(AttributeCertificateInfo attrInfo, AlgorithmIdentifier sigAlgId, byte[] signature)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(attrInfo);
+ v.add(sigAlgId);
+ v.add(new DERBitString(signature));
+
+ return AttributeCertificate.getInstance(new DERSequence(v));
+ }
+
+ private static CertificateList generateCRLStructure(TBSCertList tbsCertList, AlgorithmIdentifier sigAlgId, byte[] signature)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsCertList);
+ v.add(sigAlgId);
+ v.add(new DERBitString(signature));
+
+ return CertificateList.getInstance(new DERSequence(v));
+ }
+
+ static Set getCriticalExtensionOIDs(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return EMPTY_SET;
+ }
+
+ return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs())));
+ }
+
+ static Set getNonCriticalExtensionOIDs(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return EMPTY_SET;
+ }
+
+ // TODO: should probably produce a set that imposes correct ordering
+ return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
+ }
+
+ static List getExtensionOIDs(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return EMPTY_LIST;
+ }
+
+ return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs()));
+ }
+
+ static void addExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value)
+ throws CertIOException
+ {
+ try
+ {
+ extGenerator.addExtension(oid, isCritical, value);
+ }
+ catch (IOException e)
+ {
+ throw new CertIOException("cannot encode extension: " + e.getMessage(), e);
+ }
+ }
+
+ static DERBitString booleanToBitString(boolean[] id)
+ {
+ byte[] bytes = new byte[(id.length + 7) / 8];
+
+ for (int i = 0; i != id.length; i++)
+ {
+ bytes[i / 8] |= (id[i]) ? (1 << ((7 - (i % 8)))) : 0;
+ }
+
+ int pad = id.length % 8;
+
+ if (pad == 0)
+ {
+ return new DERBitString(bytes);
+ }
+ else
+ {
+ return new DERBitString(bytes, 8 - pad);
+ }
+ }
+
+ static boolean[] bitStringToBoolean(DERBitString bitString)
+ {
+ if (bitString != null)
+ {
+ byte[] bytes = bitString.getBytes();
+ boolean[] boolId = new boolean[bytes.length * 8 - bitString.getPadBits()];
+
+ for (int i = 0; i != boolId.length; i++)
+ {
+ boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+ }
+
+ return boolId;
+ }
+
+ return null;
+ }
+
+ static Date recoverDate(ASN1GeneralizedTime time)
+ {
+ try
+ {
+ return time.getDate();
+ }
+ catch (ParseException e)
+ {
+ throw new IllegalStateException("unable to recover date: " + e.getMessage());
+ }
+ }
+
+ static 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());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509AttributeCertificateHolder.java b/pkix/src/main/java/org/spongycastle/cert/X509AttributeCertificateHolder.java
new file mode 100644
index 00000000..76c0cdf9
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509AttributeCertificateHolder.java
@@ -0,0 +1,366 @@
+package org.spongycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.AttCertValidityPeriod;
+import org.spongycastle.asn1.x509.Attribute;
+import org.spongycastle.asn1.x509.AttributeCertificate;
+import org.spongycastle.asn1.x509.AttributeCertificateInfo;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for an X.509 AttributeCertificate structure.
+ */
+public class X509AttributeCertificateHolder
+{
+ private static Attribute[] EMPTY_ARRAY = new Attribute[0];
+
+ private AttributeCertificate attrCert;
+ private Extensions extensions;
+
+ private static AttributeCertificate parseBytes(byte[] certEncoding)
+ throws IOException
+ {
+ try
+ {
+ return AttributeCertificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Create a X509AttributeCertificateHolder from the passed in bytes.
+ *
+ * @param certEncoding BER/DER encoding of the certificate.
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public X509AttributeCertificateHolder(byte[] certEncoding)
+ throws IOException
+ {
+ this(parseBytes(certEncoding));
+ }
+
+ /**
+ * Create a X509AttributeCertificateHolder from the passed in ASN.1 structure.
+ *
+ * @param attrCert an ASN.1 AttributeCertificate structure.
+ */
+ public X509AttributeCertificateHolder(AttributeCertificate attrCert)
+ {
+ this.attrCert = attrCert;
+ this.extensions = attrCert.getAcinfo().getExtensions();
+ }
+
+ /**
+ * Return the ASN.1 encoding of this holder's attribute certificate.
+ *
+ * @return a DER encoded byte array.
+ * @throws IOException if an encoding cannot be generated.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return attrCert.getEncoded();
+ }
+
+ public int getVersion()
+ {
+ return attrCert.getAcinfo().getVersion().getValue().intValue() + 1;
+ }
+
+ /**
+ * Return the serial number of this attribute certificate.
+ *
+ * @return the serial number.
+ */
+ public BigInteger getSerialNumber()
+ {
+ return attrCert.getAcinfo().getSerialNumber().getValue();
+ }
+
+ /**
+ * Return the holder details for this attribute certificate.
+ *
+ * @return this attribute certificate's holder structure.
+ */
+ public AttributeCertificateHolder getHolder()
+ {
+ return new AttributeCertificateHolder((ASN1Sequence)attrCert.getAcinfo().getHolder().toASN1Primitive());
+ }
+
+ /**
+ * Return the issuer details for this attribute certificate.
+ *
+ * @return this attribute certificate's issuer structure,
+ */
+ public AttributeCertificateIssuer getIssuer()
+ {
+ return new AttributeCertificateIssuer(attrCert.getAcinfo().getIssuer());
+ }
+
+ /**
+ * Return the date before which this attribute certificate is not valid.
+ *
+ * @return the start date for the attribute certificate's validity period.
+ */
+ public Date getNotBefore()
+ {
+ return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotBeforeTime());
+ }
+
+ /**
+ * Return the date after which this attribute certificate is not valid.
+ *
+ * @return the final date for the attribute certificate's validity period.
+ */
+ public Date getNotAfter()
+ {
+ return CertUtils.recoverDate(attrCert.getAcinfo().getAttrCertValidityPeriod().getNotAfterTime());
+ }
+
+ /**
+ * Return the attributes, if any associated with this request.
+ *
+ * @return an array of Attribute, zero length if none present.
+ */
+ public Attribute[] getAttributes()
+ {
+ ASN1Sequence seq = attrCert.getAcinfo().getAttributes();
+ Attribute[] attrs = new Attribute[seq.size()];
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ attrs[i] = Attribute.getInstance(seq.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)
+ {
+ ASN1Sequence seq = attrCert.getAcinfo().getAttributes();
+ List list = new ArrayList();
+
+ for (int i = 0; i != seq.size(); i++)
+ {
+ Attribute attr = Attribute.getInstance(seq.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()]);
+ }
+
+ /**
+ * Return whether or not the holder's attribute certificate contains extensions.
+ *
+ * @return true if extension are present, false otherwise.
+ */
+ public boolean hasExtensions()
+ {
+ return extensions != null;
+ }
+
+ /**
+ * Look up the extension associated with the passed in OID.
+ *
+ * @param oid the OID of the extension of interest.
+ *
+ * @return the extension if present, null otherwise.
+ */
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the extensions block associated with this certificate if there is one.
+ *
+ * @return the extensions block, null otherwise.
+ */
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+ * extensions contained in this holder's attribute certificate.
+ *
+ * @return a list of extension OIDs.
+ */
+ public List getExtensionOIDs()
+ {
+ return CertUtils.getExtensionOIDs(extensions);
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * critical extensions contained in this holder's attribute certificate.
+ *
+ * @return a set of critical extension OIDs.
+ */
+ public Set getCriticalExtensionOIDs()
+ {
+ return CertUtils.getCriticalExtensionOIDs(extensions);
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * non-critical extensions contained in this holder's attribute certificate.
+ *
+ * @return a set of non-critical extension OIDs.
+ */
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return CertUtils.getNonCriticalExtensionOIDs(extensions);
+ }
+
+ public boolean[] getIssuerUniqueID()
+ {
+ return CertUtils.bitStringToBoolean(attrCert.getAcinfo().getIssuerUniqueID());
+ }
+
+ /**
+ * Return the details of the signature algorithm used to create this attribute certificate.
+ *
+ * @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
+ */
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return attrCert.getSignatureAlgorithm();
+ }
+
+ /**
+ * Return the bytes making up the signature associated with this attribute certificate.
+ *
+ * @return the attribute certificate signature bytes.
+ */
+ public byte[] getSignature()
+ {
+ return attrCert.getSignatureValue().getBytes();
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for the attribute certificate in this holder.
+ *
+ * @return a AttributeCertificate object.
+ */
+ public AttributeCertificate toASN1Structure()
+ {
+ return attrCert;
+ }
+
+ /**
+ * Return whether or not this attribute certificate is valid on a particular date.
+ *
+ * @param date the date of interest.
+ * @return true if the attribute certificate is valid, false otherwise.
+ */
+ public boolean isValidOn(Date date)
+ {
+ AttCertValidityPeriod certValidityPeriod = attrCert.getAcinfo().getAttrCertValidityPeriod();
+
+ return !date.before(CertUtils.recoverDate(certValidityPeriod.getNotBeforeTime())) && !date.after(CertUtils.recoverDate(certValidityPeriod.getNotAfterTime()));
+ }
+
+ /**
+ * Validate the signature on the attribute certificate 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 CertException if the signature cannot be processed or is inappropriate.
+ */
+ public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+ throws CertException
+ {
+ AttributeCertificateInfo acinfo = attrCert.getAcinfo();
+
+ if (!CertUtils.isAlgIdEqual(acinfo.getSignature(), attrCert.getSignatureAlgorithm()))
+ {
+ throw new CertException("signature invalid - algorithm identifier mismatch");
+ }
+
+ ContentVerifier verifier;
+
+ try
+ {
+ verifier = verifierProvider.get((acinfo.getSignature()));
+
+ OutputStream sOut = verifier.getOutputStream();
+ DEROutputStream dOut = new DEROutputStream(sOut);
+
+ dOut.writeObject(acinfo);
+
+ sOut.close();
+ }
+ catch (Exception e)
+ {
+ throw new CertException("unable to process signature: " + e.getMessage(), e);
+ }
+
+ return verifier.verify(attrCert.getSignatureValue().getBytes());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof X509AttributeCertificateHolder))
+ {
+ return false;
+ }
+
+ X509AttributeCertificateHolder other = (X509AttributeCertificateHolder)o;
+
+ return this.attrCert.equals(other.attrCert);
+ }
+
+ public int hashCode()
+ {
+ return this.attrCert.hashCode();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509CRLEntryHolder.java b/pkix/src/main/java/org/spongycastle/cert/X509CRLEntryHolder.java
new file mode 100644
index 00000000..da542c0e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509CRLEntryHolder.java
@@ -0,0 +1,144 @@
+package org.spongycastle.cert;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.TBSCertList;
+
+/**
+ * Holding class for an X.509 CRL Entry structure.
+ */
+public class X509CRLEntryHolder
+{
+ private TBSCertList.CRLEntry entry;
+ private GeneralNames ca;
+
+ X509CRLEntryHolder(TBSCertList.CRLEntry entry, boolean isIndirect, GeneralNames previousCA)
+ {
+ this.entry = entry;
+ this.ca = previousCA;
+
+ if (isIndirect && entry.hasExtensions())
+ {
+ Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ ca = GeneralNames.getInstance(currentCaName.getParsedValue());
+ }
+ }
+ }
+
+ /**
+ * Return the serial number of the certificate associated with this CRLEntry.
+ *
+ * @return the revoked certificate's serial number.
+ */
+ public BigInteger getSerialNumber()
+ {
+ return entry.getUserCertificate().getValue();
+ }
+
+ /**
+ * Return the date on which the certificate associated with this CRLEntry was revoked.
+ *
+ * @return the revocation date for the revoked certificate.
+ */
+ public Date getRevocationDate()
+ {
+ return entry.getRevocationDate().getDate();
+ }
+
+ /**
+ * Return whether or not the holder's CRL entry contains extensions.
+ *
+ * @return true if extension are present, false otherwise.
+ */
+ public boolean hasExtensions()
+ {
+ return entry.hasExtensions();
+ }
+
+ /**
+ * Return the available names for the certificate issuer for the certificate referred to by this CRL entry.
+ * <p>
+ * Note: this will be the issuer of the CRL unless it has been specified that the CRL is indirect
+ * in the IssuingDistributionPoint extension and either a previous entry, or the current one,
+ * has specified a different CA via the certificateIssuer extension.
+ * </p>
+ *
+ * @return the revoked certificate's issuer.
+ */
+ public GeneralNames getCertificateIssuer()
+ {
+ return this.ca;
+ }
+
+ /**
+ * Look up the extension associated with the passed in OID.
+ *
+ * @param oid the OID of the extension of interest.
+ *
+ * @return the extension if present, null otherwise.
+ */
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ Extensions extensions = entry.getExtensions();
+
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the extensions block associated with this CRL entry if there is one.
+ *
+ * @return the extensions block, null otherwise.
+ */
+ public Extensions getExtensions()
+ {
+ return entry.getExtensions();
+ }
+
+ /**
+ * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+ * extensions contained in this holder's CRL entry.
+ *
+ * @return a list of extension OIDs.
+ */
+ public List getExtensionOIDs()
+ {
+ return CertUtils.getExtensionOIDs(entry.getExtensions());
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * critical extensions contained in this holder's CRL entry.
+ *
+ * @return a set of critical extension OIDs.
+ */
+ public Set getCriticalExtensionOIDs()
+ {
+ return CertUtils.getCriticalExtensionOIDs(entry.getExtensions());
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * non-critical extensions contained in this holder's CRL entry.
+ *
+ * @return a set of non-critical extension OIDs.
+ */
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return CertUtils.getNonCriticalExtensionOIDs(entry.getExtensions());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509CRLHolder.java b/pkix/src/main/java/org/spongycastle/cert/X509CRLHolder.java
new file mode 100644
index 00000000..e94c2c1b
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509CRLHolder.java
@@ -0,0 +1,317 @@
+package org.spongycastle.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.CertificateList;
+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.IssuingDistributionPoint;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for an X.509 CRL structure.
+ */
+public class X509CRLHolder
+{
+ private CertificateList x509CRL;
+ private boolean isIndirect;
+ private Extensions extensions;
+ private GeneralNames issuerName;
+
+ private static CertificateList parseStream(InputStream stream)
+ throws IOException
+ {
+ try
+ {
+ return CertificateList.getInstance(new ASN1InputStream(stream, true).readObject());
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ private static boolean isIndirectCRL(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return false;
+ }
+
+ Extension ext = extensions.getExtension(Extension.issuingDistributionPoint);
+
+ return ext != null && IssuingDistributionPoint.getInstance(ext.getParsedValue()).isIndirectCRL();
+ }
+
+ /**
+ * Create a X509CRLHolder from the passed in bytes.
+ *
+ * @param crlEncoding BER/DER encoding of the CRL
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public X509CRLHolder(byte[] crlEncoding)
+ throws IOException
+ {
+ this(parseStream(new ByteArrayInputStream(crlEncoding)));
+ }
+
+ /**
+ * Create a X509CRLHolder from the passed in InputStream.
+ *
+ * @param crlStream BER/DER encoded InputStream of the CRL
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public X509CRLHolder(InputStream crlStream)
+ throws IOException
+ {
+ this(parseStream(crlStream));
+ }
+
+ /**
+ * Create a X509CRLHolder from the passed in ASN.1 structure.
+ *
+ * @param x509CRL an ASN.1 CertificateList structure.
+ */
+ public X509CRLHolder(CertificateList x509CRL)
+ {
+ this.x509CRL = x509CRL;
+ this.extensions = x509CRL.getTBSCertList().getExtensions();
+ this.isIndirect = isIndirectCRL(extensions);
+ this.issuerName = new GeneralNames(new GeneralName(x509CRL.getIssuer()));
+ }
+
+ /**
+ * Return the ASN.1 encoding of this holder's CRL.
+ *
+ * @return a DER encoded byte array.
+ * @throws IOException if an encoding cannot be generated.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return x509CRL.getEncoded();
+ }
+
+ /**
+ * Return the issuer of this holder's CRL.
+ *
+ * @return the CRL issuer.
+ */
+ public X500Name getIssuer()
+ {
+ return X500Name.getInstance(x509CRL.getIssuer());
+ }
+
+ public X509CRLEntryHolder getRevokedCertificate(BigInteger serialNumber)
+ {
+ GeneralNames currentCA = issuerName;
+ for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+ {
+ TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
+
+ if (entry.getUserCertificate().getValue().equals(serialNumber))
+ {
+ return new X509CRLEntryHolder(entry, isIndirect, currentCA);
+ }
+
+ if (isIndirect && entry.hasExtensions())
+ {
+ Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
+
+ if (currentCaName != null)
+ {
+ currentCA = GeneralNames.getInstance(currentCaName.getParsedValue());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return a collection of X509CRLEntryHolder objects, giving the details of the
+ * revoked certificates that appear on this CRL.
+ *
+ * @return the revoked certificates as a collection of X509CRLEntryHolder objects.
+ */
+ public Collection getRevokedCertificates()
+ {
+ TBSCertList.CRLEntry[] entries = x509CRL.getRevokedCertificates();
+ List l = new ArrayList(entries.length);
+ GeneralNames currentCA = issuerName;
+
+ for (Enumeration en = x509CRL.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+ {
+ TBSCertList.CRLEntry entry = (TBSCertList.CRLEntry)en.nextElement();
+ X509CRLEntryHolder crlEntry = new X509CRLEntryHolder(entry, isIndirect, currentCA);
+
+ l.add(crlEntry);
+
+ currentCA = crlEntry.getCertificateIssuer();
+ }
+
+ return l;
+ }
+
+ /**
+ * Return whether or not the holder's CRL contains extensions.
+ *
+ * @return true if extension are present, false otherwise.
+ */
+ public boolean hasExtensions()
+ {
+ return extensions != null;
+ }
+
+ /**
+ * Look up the extension associated with the passed in OID.
+ *
+ * @param oid the OID of the extension of interest.
+ *
+ * @return the extension if present, null otherwise.
+ */
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the extensions block associated with this CRL if there is one.
+ *
+ * @return the extensions block, null otherwise.
+ */
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+ * extensions contained in this holder's CRL.
+ *
+ * @return a list of extension OIDs.
+ */
+ public List getExtensionOIDs()
+ {
+ return CertUtils.getExtensionOIDs(extensions);
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * critical extensions contained in this holder's CRL.
+ *
+ * @return a set of critical extension OIDs.
+ */
+ public Set getCriticalExtensionOIDs()
+ {
+ return CertUtils.getCriticalExtensionOIDs(extensions);
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * non-critical extensions contained in this holder's CRL.
+ *
+ * @return a set of non-critical extension OIDs.
+ */
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return CertUtils.getNonCriticalExtensionOIDs(extensions);
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for the CRL in this holder.
+ *
+ * @return a CertificateList object.
+ */
+ public CertificateList toASN1Structure()
+ {
+ return x509CRL;
+ }
+
+ /**
+ * Validate the signature on the CRL.
+ *
+ * @param verifierProvider a ContentVerifierProvider that can generate a verifier for the signature.
+ * @return true if the signature is valid, false otherwise.
+ * @throws CertException if the signature cannot be processed or is inappropriate.
+ */
+ public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+ throws CertException
+ {
+ TBSCertList tbsCRL = x509CRL.getTBSCertList();
+
+ if (!CertUtils.isAlgIdEqual(tbsCRL.getSignature(), x509CRL.getSignatureAlgorithm()))
+ {
+ throw new CertException("signature invalid - algorithm identifier mismatch");
+ }
+
+ ContentVerifier verifier;
+
+ try
+ {
+ verifier = verifierProvider.get((tbsCRL.getSignature()));
+
+ OutputStream sOut = verifier.getOutputStream();
+ DEROutputStream dOut = new DEROutputStream(sOut);
+
+ dOut.writeObject(tbsCRL);
+
+ sOut.close();
+ }
+ catch (Exception e)
+ {
+ throw new CertException("unable to process signature: " + e.getMessage(), e);
+ }
+
+ return verifier.verify(x509CRL.getSignature().getBytes());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof X509CRLHolder))
+ {
+ return false;
+ }
+
+ X509CRLHolder other = (X509CRLHolder)o;
+
+ return this.x509CRL.equals(other.x509CRL);
+ }
+
+ public int hashCode()
+ {
+ return this.x509CRL.hashCode();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509CertificateHolder.java b/pkix/src/main/java/org/spongycastle/cert/X509CertificateHolder.java
new file mode 100644
index 00000000..fd53b92c
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509CertificateHolder.java
@@ -0,0 +1,327 @@
+package org.spongycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.TBSCertificate;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+
+/**
+ * Holding class for an X.509 Certificate structure.
+ */
+public class X509CertificateHolder
+{
+ private Certificate x509Certificate;
+ private Extensions extensions;
+
+ private static Certificate parseBytes(byte[] certEncoding)
+ throws IOException
+ {
+ try
+ {
+ return Certificate.getInstance(ASN1Primitive.fromByteArray(certEncoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Create a X509CertificateHolder from the passed in bytes.
+ *
+ * @param certEncoding BER/DER encoding of the certificate.
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public X509CertificateHolder(byte[] certEncoding)
+ throws IOException
+ {
+ this(parseBytes(certEncoding));
+ }
+
+ /**
+ * Create a X509CertificateHolder from the passed in ASN.1 structure.
+ *
+ * @param x509Certificate an ASN.1 Certificate structure.
+ */
+ public X509CertificateHolder(Certificate x509Certificate)
+ {
+ this.x509Certificate = x509Certificate;
+ this.extensions = x509Certificate.getTBSCertificate().getExtensions();
+ }
+
+ public int getVersionNumber()
+ {
+ return x509Certificate.getVersionNumber();
+ }
+
+ /**
+ * @deprecated use getVersionNumber
+ */
+ public int getVersion()
+ {
+ return x509Certificate.getVersionNumber();
+ }
+
+ /**
+ * Return whether or not the holder's certificate contains extensions.
+ *
+ * @return true if extension are present, false otherwise.
+ */
+ public boolean hasExtensions()
+ {
+ return extensions != null;
+ }
+
+ /**
+ * Look up the extension associated with the passed in OID.
+ *
+ * @param oid the OID of the extension of interest.
+ *
+ * @return the extension if present, null otherwise.
+ */
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the extensions block associated with this certificate if there is one.
+ *
+ * @return the extensions block, null otherwise.
+ */
+ public Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * Returns a list of ASN1ObjectIdentifier objects representing the OIDs of the
+ * extensions contained in this holder's certificate.
+ *
+ * @return a list of extension OIDs.
+ */
+ public List getExtensionOIDs()
+ {
+ return CertUtils.getExtensionOIDs(extensions);
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * critical extensions contained in this holder's certificate.
+ *
+ * @return a set of critical extension OIDs.
+ */
+ public Set getCriticalExtensionOIDs()
+ {
+ return CertUtils.getCriticalExtensionOIDs(extensions);
+ }
+
+ /**
+ * Returns a set of ASN1ObjectIdentifier objects representing the OIDs of the
+ * non-critical extensions contained in this holder's certificate.
+ *
+ * @return a set of non-critical extension OIDs.
+ */
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return CertUtils.getNonCriticalExtensionOIDs(extensions);
+ }
+
+ /**
+ * Return the serial number of this attribute certificate.
+ *
+ * @return the serial number.
+ */
+ public BigInteger getSerialNumber()
+ {
+ return x509Certificate.getSerialNumber().getValue();
+ }
+
+ /**
+ * Return the issuer of this certificate.
+ *
+ * @return the certificate issuer.
+ */
+ public X500Name getIssuer()
+ {
+ return X500Name.getInstance(x509Certificate.getIssuer());
+ }
+
+ /**
+ * Return the subject this certificate is for.
+ *
+ * @return the subject for the certificate.
+ */
+ public X500Name getSubject()
+ {
+ return X500Name.getInstance(x509Certificate.getSubject());
+ }
+
+ /**
+ * Return the date before which this certificate is not valid.
+ *
+ * @return the start time for the certificate's validity period.
+ */
+ public Date getNotBefore()
+ {
+ return x509Certificate.getStartDate().getDate();
+ }
+
+ /**
+ * Return the date after which this certificate is not valid.
+ *
+ * @return the final time for the certificate's validity period.
+ */
+ public Date getNotAfter()
+ {
+ return x509Certificate.getEndDate().getDate();
+ }
+
+ /**
+ * Return the SubjectPublicKeyInfo describing the public key this certificate is carrying.
+ *
+ * @return the public key ASN.1 structure contained in the certificate.
+ */
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return x509Certificate.getSubjectPublicKeyInfo();
+ }
+
+ /**
+ * Return the underlying ASN.1 structure for the certificate in this holder.
+ *
+ * @return a X509CertificateStructure object.
+ */
+ public Certificate toASN1Structure()
+ {
+ return x509Certificate;
+ }
+
+ /**
+ * Return the details of the signature algorithm used to create this attribute certificate.
+ *
+ * @return the AlgorithmIdentifier describing the signature algorithm used to create this attribute certificate.
+ */
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return x509Certificate.getSignatureAlgorithm();
+ }
+
+ /**
+ * Return the bytes making up the signature associated with this attribute certificate.
+ *
+ * @return the attribute certificate signature bytes.
+ */
+ public byte[] getSignature()
+ {
+ return x509Certificate.getSignature().getBytes();
+ }
+
+ /**
+ * Return whether or not this certificate is valid on a particular date.
+ *
+ * @param date the date of interest.
+ * @return true if the certificate is valid, false otherwise.
+ */
+ public boolean isValidOn(Date date)
+ {
+ return !date.before(x509Certificate.getStartDate().getDate()) && !date.after(x509Certificate.getEndDate().getDate());
+ }
+
+ /**
+ * Validate the signature on the certificate 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 CertException if the signature cannot be processed or is inappropriate.
+ */
+ public boolean isSignatureValid(ContentVerifierProvider verifierProvider)
+ throws CertException
+ {
+ TBSCertificate tbsCert = x509Certificate.getTBSCertificate();
+
+ if (!CertUtils.isAlgIdEqual(tbsCert.getSignature(), x509Certificate.getSignatureAlgorithm()))
+ {
+ throw new CertException("signature invalid - algorithm identifier mismatch");
+ }
+
+ ContentVerifier verifier;
+
+ try
+ {
+ verifier = verifierProvider.get((tbsCert.getSignature()));
+
+ OutputStream sOut = verifier.getOutputStream();
+ DEROutputStream dOut = new DEROutputStream(sOut);
+
+ dOut.writeObject(tbsCert);
+
+ sOut.close();
+ }
+ catch (Exception e)
+ {
+ throw new CertException("unable to process signature: " + e.getMessage(), e);
+ }
+
+ return verifier.verify(x509Certificate.getSignature().getBytes());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof X509CertificateHolder))
+ {
+ return false;
+ }
+
+ X509CertificateHolder other = (X509CertificateHolder)o;
+
+ return this.x509Certificate.equals(other.x509Certificate);
+ }
+
+ public int hashCode()
+ {
+ return this.x509Certificate.hashCode();
+ }
+
+ /**
+ * Return the ASN.1 encoding of this holder's certificate.
+ *
+ * @return a DER encoded byte array.
+ * @throws IOException if an encoding cannot be generated.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return x509Certificate.getEncoded();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509ContentVerifierProviderBuilder.java b/pkix/src/main/java/org/spongycastle/cert/X509ContentVerifierProviderBuilder.java
new file mode 100644
index 00000000..d8429912
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509ContentVerifierProviderBuilder.java
@@ -0,0 +1,14 @@
+package org.spongycastle.cert;
+
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.ContentVerifierProvider;
+import org.spongycastle.operator.OperatorCreationException;
+
+public interface X509ContentVerifierProviderBuilder
+{
+ ContentVerifierProvider build(SubjectPublicKeyInfo validatingKeyInfo)
+ throws OperatorCreationException;
+
+ ContentVerifierProvider build(X509CertificateHolder validatingKeyInfo)
+ throws OperatorCreationException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509ExtensionUtils.java b/pkix/src/main/java/org/spongycastle/cert/X509ExtensionUtils.java
new file mode 100644
index 00000000..96e603f8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509ExtensionUtils.java
@@ -0,0 +1,132 @@
+package org.spongycastle.cert;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.DigestCalculator;
+
+/**
+ * General utility class for creating calculated extensions using the standard methods.
+ * <p>
+ * <b>Note:</b> This class is not thread safe!
+ * </p>
+ */
+public class X509ExtensionUtils
+{
+ private DigestCalculator calculator;
+
+ public X509ExtensionUtils(DigestCalculator calculator)
+ {
+ this.calculator = calculator;
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(
+ X509CertificateHolder certHolder)
+ {
+ if (certHolder.getVersionNumber() != 3)
+ {
+ GeneralName genName = new GeneralName(certHolder.getIssuer());
+ SubjectPublicKeyInfo info = certHolder.getSubjectPublicKeyInfo();
+
+ return new AuthorityKeyIdentifier(
+ calculateIdentifier(info), new GeneralNames(genName), certHolder.getSerialNumber());
+ }
+ else
+ {
+ GeneralName genName = new GeneralName(certHolder.getIssuer());
+ Extension ext = certHolder.getExtension(Extension.subjectKeyIdentifier);
+
+ if (ext != null)
+ {
+ ASN1OctetString str = ASN1OctetString.getInstance(ext.getParsedValue());
+
+ return new AuthorityKeyIdentifier(
+ str.getOctets(), new GeneralNames(genName), certHolder.getSerialNumber());
+ }
+ else
+ {
+ SubjectPublicKeyInfo info = certHolder.getSubjectPublicKeyInfo();
+
+ return new AuthorityKeyIdentifier(
+ calculateIdentifier(info), new GeneralNames(genName), certHolder.getSerialNumber());
+ }
+ }
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(SubjectPublicKeyInfo publicKeyInfo)
+ {
+ return new AuthorityKeyIdentifier(calculateIdentifier(publicKeyInfo));
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(SubjectPublicKeyInfo publicKeyInfo, GeneralNames generalNames, BigInteger serial)
+ {
+ return new AuthorityKeyIdentifier(calculateIdentifier(publicKeyInfo), generalNames, serial);
+ }
+
+ /**
+ * Return a RFC 3280 type 1 key identifier. As in:
+ * <pre>
+ * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+ * value of the BIT STRING subjectPublicKey (excluding the tag,
+ * length, and number of unused bits).
+ * </pre>
+ * @param publicKeyInfo the key info object containing the subjectPublicKey field.
+ * @return the key identifier.
+ */
+ public SubjectKeyIdentifier createSubjectKeyIdentifier(
+ SubjectPublicKeyInfo publicKeyInfo)
+ {
+ return new SubjectKeyIdentifier(calculateIdentifier(publicKeyInfo));
+ }
+
+ /**
+ * Return a RFC 3280 type 2 key identifier. As in:
+ * <pre>
+ * (2) The keyIdentifier is composed of a four bit type field with
+ * the value 0100 followed by the least significant 60 bits of the
+ * SHA-1 hash of the value of the BIT STRING subjectPublicKey.
+ * </pre>
+ * @param publicKeyInfo the key info object containing the subjectPublicKey field.
+ * @return the key identifier.
+ */
+ public SubjectKeyIdentifier createTruncatedSubjectKeyIdentifier(SubjectPublicKeyInfo publicKeyInfo)
+ {
+ byte[] digest = calculateIdentifier(publicKeyInfo);
+ byte[] id = new byte[8];
+
+ System.arraycopy(digest, digest.length - 8, id, 0, id.length);
+
+ id[0] &= 0x0f;
+ id[0] |= 0x40;
+
+ return new SubjectKeyIdentifier(id);
+ }
+
+ private byte[] calculateIdentifier(SubjectPublicKeyInfo publicKeyInfo)
+ {
+ byte[] bytes = publicKeyInfo.getPublicKeyData().getBytes();
+
+ OutputStream cOut = calculator.getOutputStream();
+
+ try
+ {
+ cOut.write(bytes);
+
+ cOut.close();
+ }
+ catch (IOException e)
+ { // it's hard to imagine this happening, but yes it does!
+ throw new CertRuntimeException("unable to calculate identifier: " + e.getMessage(), e);
+ }
+
+ return calculator.getDigest();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509v1CertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/X509v1CertificateBuilder.java
new file mode 100644
index 00000000..083e1505
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509v1CertificateBuilder.java
@@ -0,0 +1,101 @@
+package org.spongycastle.cert;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Locale;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.asn1.x509.V1TBSCertificateGenerator;
+import org.spongycastle.asn1.x509.V3TBSCertificateGenerator;
+import org.spongycastle.operator.ContentSigner;
+
+
+/**
+ * class to produce an X.509 Version 1 certificate.
+ */
+public class X509v1CertificateBuilder
+{
+ private V1TBSCertificateGenerator tbsGen;
+
+ /**
+ * Create a builder for a version 1 certificate.
+ *
+ * @param issuer the certificate issuer
+ * @param serial the certificate serial number
+ * @param notBefore the date before which the certificate is not valid
+ * @param notAfter the date after which the certificate is not valid
+ * @param subject the certificate subject
+ * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
+ */
+ public X509v1CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ this(issuer, serial, new Time(notBefore), new Time(notAfter), subject, publicKeyInfo);
+ }
+
+ /**
+ * Create a builder for a version 1 certificate. You may need to use this constructor if the default locale
+ * doesn't use a Gregorian calender so that the Time produced is compatible with other ASN.1 implementations.
+ *
+ * @param issuer the certificate issuer
+ * @param serial the certificate serial number
+ * @param notBefore the date before which the certificate is not valid
+ * @param notAfter the date after which the certificate is not valid
+ * @param dateLocale locale to be used for date interpretation.
+ * @param subject the certificate subject
+ * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
+ */
+ public X509v1CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, Locale dateLocale, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ this(issuer, serial, new Time(notBefore, dateLocale), new Time(notAfter, dateLocale), subject, publicKeyInfo);
+ }
+
+ /**
+ * Create a builder for a version 1 certificate.
+ *
+ * @param issuer the certificate issuer
+ * @param serial the certificate serial number
+ * @param notBefore the Time before which the certificate is not valid
+ * @param notAfter the Time after which the certificate is not valid
+ * @param subject the certificate subject
+ * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
+ */
+ public X509v1CertificateBuilder(X500Name issuer, BigInteger serial, Time notBefore, Time notAfter, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ if (issuer == null)
+ {
+ throw new IllegalArgumentException("issuer must not be null");
+ }
+
+ if (publicKeyInfo == null)
+ {
+ throw new IllegalArgumentException("publicKeyInfo must not be null");
+ }
+
+ tbsGen = new V1TBSCertificateGenerator();
+ tbsGen.setSerialNumber(new ASN1Integer(serial));
+ tbsGen.setIssuer(issuer);
+ tbsGen.setStartDate(notBefore);
+ tbsGen.setEndDate(notAfter);
+ tbsGen.setSubject(subject);
+ tbsGen.setSubjectPublicKeyInfo(publicKeyInfo);
+ }
+
+ /**
+ * Generate an X509 certificate, based on the current issuer and subject
+ * using the passed in signer.
+ *
+ * @param signer the content signer to be used to generate the signature validating the certificate.
+ * @return a holder containing the resulting signed certificate.
+ */
+ public X509CertificateHolder build(
+ ContentSigner signer)
+ {
+ tbsGen.setSignature(signer.getAlgorithmIdentifier());
+
+ return CertUtils.generateFullCert(signer, tbsGen.generateTBSCertificate());
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509v2AttributeCertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/X509v2AttributeCertificateBuilder.java
new file mode 100644
index 00000000..ffdd1567
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509v2AttributeCertificateBuilder.java
@@ -0,0 +1,162 @@
+package org.spongycastle.cert;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Locale;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.x509.AttCertIssuer;
+import org.spongycastle.asn1.x509.Attribute;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.V2AttributeCertificateInfoGenerator;
+import org.spongycastle.operator.ContentSigner;
+
+/**
+ * class to produce an X.509 Version 2 AttributeCertificate.
+ */
+public class X509v2AttributeCertificateBuilder
+{
+ private V2AttributeCertificateInfoGenerator acInfoGen;
+ private ExtensionsGenerator extGenerator;
+
+ /**
+ * Base constructor.
+ *
+ * @param holder holder certificate details
+ * @param issuer issuer of this attribute certificate.
+ * @param serialNumber serial number of this attribute certificate.
+ * @param notBefore the date before which the certificate is not valid.
+ * @param notAfter the date after which the certificate is not valid.
+ */
+ public X509v2AttributeCertificateBuilder(AttributeCertificateHolder holder, AttributeCertificateIssuer issuer, BigInteger serialNumber, Date notBefore, Date notAfter)
+ {
+ acInfoGen = new V2AttributeCertificateInfoGenerator();
+ extGenerator = new ExtensionsGenerator();
+
+ acInfoGen.setHolder(holder.holder);
+ acInfoGen.setIssuer(AttCertIssuer.getInstance(issuer.form));
+ acInfoGen.setSerialNumber(new ASN1Integer(serialNumber));
+ acInfoGen.setStartDate(new ASN1GeneralizedTime(notBefore));
+ acInfoGen.setEndDate(new ASN1GeneralizedTime(notAfter));
+ }
+
+ /**
+ * Base constructor with locale for interpreting dates. You may need to use this constructor if the default locale
+ * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
+ *
+ * @param holder holder certificate details
+ * @param issuer issuer of this attribute certificate.
+ * @param serialNumber serial number of this attribute certificate.
+ * @param notBefore the date before which the certificate is not valid.
+ * @param notAfter the date after which the certificate is not valid.
+ * @param dateLocale locale to be used for date interpretation.
+ */
+ public X509v2AttributeCertificateBuilder(AttributeCertificateHolder holder, AttributeCertificateIssuer issuer, BigInteger serialNumber, Date notBefore, Date notAfter, Locale dateLocale)
+ {
+ acInfoGen = new V2AttributeCertificateInfoGenerator();
+ extGenerator = new ExtensionsGenerator();
+
+ acInfoGen.setHolder(holder.holder);
+ acInfoGen.setIssuer(AttCertIssuer.getInstance(issuer.form));
+ acInfoGen.setSerialNumber(new ASN1Integer(serialNumber));
+ acInfoGen.setStartDate(new ASN1GeneralizedTime(notBefore, dateLocale));
+ acInfoGen.setEndDate(new ASN1GeneralizedTime(notAfter, dateLocale));
+ }
+
+ /**
+ * 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 X509v2AttributeCertificateBuilder addAttribute(ASN1ObjectIdentifier attrType, ASN1Encodable attrValue)
+ {
+ acInfoGen.addAttribute(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 X509v2AttributeCertificateBuilder addAttribute(ASN1ObjectIdentifier attrType, ASN1Encodable[] attrValues)
+ {
+ acInfoGen.addAttribute(new Attribute(attrType, new DERSet(attrValues)));
+
+ return this;
+ }
+
+ public void setIssuerUniqueId(
+ boolean[] iui)
+ {
+ acInfoGen.setIssuerUniqueID(CertUtils.booleanToBitString(iui));
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the extension is critical, false otherwise.
+ * @param value the ASN.1 structure that forms the extension's value.
+ * @return this builder object.
+ */
+ public X509v2AttributeCertificateBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ ASN1Encodable value)
+ throws CertIOException
+ {
+ CertUtils.addExtension(extGenerator, oid, isCritical, value);
+
+ return this;
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3) using a byte encoding of the
+ * extension value.
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the extension is critical, false otherwise.
+ * @param encodedValue a byte array representing the encoding of the extension value.
+ * @return this builder object.
+ */
+ public X509v2AttributeCertificateBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ byte[] encodedValue)
+ throws CertIOException
+ {
+ extGenerator.addExtension(oid, isCritical, encodedValue);
+
+ return this;
+ }
+
+ /**
+ * Generate an X509 certificate, based on the current issuer and subject
+ * using the passed in signer.
+ *
+ * @param signer the content signer to be used to generate the signature validating the certificate.
+ * @return a holder containing the resulting signed certificate.
+ */
+ public X509AttributeCertificateHolder build(
+ ContentSigner signer)
+ {
+ acInfoGen.setSignature(signer.getAlgorithmIdentifier());
+
+ if (!extGenerator.isEmpty())
+ {
+ acInfoGen.setExtensions(extGenerator.generate());
+ }
+
+ return CertUtils.generateFullAttrCert(signer, acInfoGen.generateAttributeCertificateInfo());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509v2CRLBuilder.java b/pkix/src/main/java/org/spongycastle/cert/X509v2CRLBuilder.java
new file mode 100644
index 00000000..69cb2406
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509v2CRLBuilder.java
@@ -0,0 +1,266 @@
+package org.spongycastle.cert;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.TBSCertList;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.asn1.x509.V2TBSCertListGenerator;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.operator.ContentSigner;
+
+/**
+ * class to produce an X.509 Version 2 CRL.
+ */
+public class X509v2CRLBuilder
+{
+ private V2TBSCertListGenerator tbsGen;
+ private ExtensionsGenerator extGenerator;
+
+ /**
+ * Basic constructor.
+ *
+ * @param issuer the issuer this CRL is associated with.
+ * @param thisUpdate the date of this update.
+ */
+ public X509v2CRLBuilder(
+ X500Name issuer,
+ Date thisUpdate)
+ {
+ tbsGen = new V2TBSCertListGenerator();
+ extGenerator = new ExtensionsGenerator();
+
+ tbsGen.setIssuer(issuer);
+ tbsGen.setThisUpdate(new Time(thisUpdate));
+ }
+
+ /**
+ * Basic constructor with Locale. You may need to use this constructor if the default locale
+ * doesn't use a Gregorian calender so that the Time produced is compatible with other ASN.1 implementations.
+ *
+ * @param issuer the issuer this CRL is associated with.
+ * @param thisUpdate the date of this update.
+ * @param dateLocale locale to be used for date interpretation.
+ */
+ public X509v2CRLBuilder(
+ X500Name issuer,
+ Date thisUpdate,
+ Locale dateLocale)
+ {
+ tbsGen = new V2TBSCertListGenerator();
+ extGenerator = new ExtensionsGenerator();
+
+ tbsGen.setIssuer(issuer);
+ tbsGen.setThisUpdate(new Time(thisUpdate, dateLocale));
+ }
+
+ /**
+ * Basic constructor.
+ *
+ * @param issuer the issuer this CRL is associated with.
+ * @param thisUpdate the Time of this update.
+ */
+ public X509v2CRLBuilder(
+ X500Name issuer,
+ Time thisUpdate)
+ {
+ tbsGen = new V2TBSCertListGenerator();
+ extGenerator = new ExtensionsGenerator();
+
+ tbsGen.setIssuer(issuer);
+ tbsGen.setThisUpdate(thisUpdate);
+ }
+
+ /**
+ * Set the date by which the next CRL will become available.
+ *
+ * @param date date of next CRL update.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder setNextUpdate(
+ Date date)
+ {
+ return this.setNextUpdate(new Time(date));
+ }
+
+ /**
+ * Set the date by which the next CRL will become available.
+ *
+ * @param date date of next CRL update.
+ * @param dateLocale locale to be used for date interpretation.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder setNextUpdate(
+ Date date,
+ Locale dateLocale)
+ {
+ return this.setNextUpdate(new Time(date, dateLocale));
+ }
+
+ /**
+ * Set the date by which the next CRL will become available.
+ *
+ * @param date date of next CRL update.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder setNextUpdate(
+ Time date)
+ {
+ tbsGen.setNextUpdate(date);
+
+ return this;
+ }
+
+ /**
+ * Add a CRL entry with the just reasonCode extension.
+ *
+ * @param userCertificateSerial serial number of revoked certificate.
+ * @param revocationDate date of certificate revocation.
+ * @param reason the reason code, as indicated in CRLReason, i.e CRLReason.keyCompromise, or 0 if not to be used.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, int reason)
+ {
+ tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), reason);
+
+ return this;
+ }
+
+ /**
+ * Add a CRL entry with an invalidityDate extension as well as a reasonCode extension. This is used
+ * where the date of revocation might be after issues with the certificate may have occurred.
+ *
+ * @param userCertificateSerial serial number of revoked certificate.
+ * @param revocationDate date of certificate revocation.
+ * @param reason the reason code, as indicated in CRLReason, i.e CRLReason.keyCompromise, or 0 if not to be used.
+ * @param invalidityDate the date on which the private key for the certificate became compromised or the certificate otherwise became invalid.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, int reason, Date invalidityDate)
+ {
+ tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), reason, new ASN1GeneralizedTime(invalidityDate));
+
+ return this;
+ }
+
+ /**
+ * Add a CRL entry with extensions.
+ *
+ * @param userCertificateSerial serial number of revoked certificate.
+ * @param revocationDate date of certificate revocation.
+ * @param extensions extension set to be associated with this CRLEntry.
+ * @return the current builder.
+ * @deprecated use method taking Extensions
+ */
+ public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, X509Extensions extensions)
+ {
+ tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), Extensions.getInstance(extensions));
+
+ return this;
+ }
+
+ /**
+ * Add a CRL entry with extensions.
+ *
+ * @param userCertificateSerial serial number of revoked certificate.
+ * @param revocationDate date of certificate revocation.
+ * @param extensions extension set to be associated with this CRLEntry.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, Extensions extensions)
+ {
+ tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), extensions);
+
+ return this;
+ }
+
+ /**
+ * Add the CRLEntry objects contained in a previous CRL.
+ *
+ * @param other the X509CRLHolder to source the other entries from.
+ * @return the current builder.
+ */
+ public X509v2CRLBuilder addCRL(X509CRLHolder other)
+ {
+ TBSCertList revocations = other.toASN1Structure().getTBSCertList();
+
+ if (revocations != null)
+ {
+ for (Enumeration en = revocations.getRevokedCertificateEnumeration(); en.hasMoreElements();)
+ {
+ tbsGen.addCRLEntry(ASN1Sequence.getInstance(((ASN1Encodable)en.nextElement()).toASN1Primitive()));
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3)
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the extension is critical, false otherwise.
+ * @param value the ASN.1 structure that forms the extension's value.
+ * @return this builder object.
+ */
+ public X509v2CRLBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ ASN1Encodable value)
+ throws CertIOException
+ {
+ CertUtils.addExtension(extGenerator, oid, isCritical, value);
+
+ return this;
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3) using a byte encoding of the
+ * extension value.
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the extension is critical, false otherwise.
+ * @param encodedValue a byte array representing the encoding of the extension value.
+ * @return this builder object.
+ */
+ public X509v2CRLBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ byte[] encodedValue)
+ throws CertIOException
+ {
+ extGenerator.addExtension(oid, isCritical, encodedValue);
+
+ return this;
+ }
+
+ /**
+ * Generate an X.509 CRL, based on the current issuer and subject
+ * using the passed in signer.
+ *
+ * @param signer the content signer to be used to generate the signature validating the certificate.
+ * @return a holder containing the resulting signed certificate.
+ */
+ public X509CRLHolder build(
+ ContentSigner signer)
+ {
+ tbsGen.setSignature(signer.getAlgorithmIdentifier());
+
+ if (!extGenerator.isEmpty())
+ {
+ tbsGen.setExtensions(extGenerator.generate());
+ }
+
+ return CertUtils.generateFullCRL(signer, tbsGen.generateTBSCertList());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/X509v3CertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/X509v3CertificateBuilder.java
new file mode 100644
index 00000000..0b55b2aa
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/X509v3CertificateBuilder.java
@@ -0,0 +1,195 @@
+package org.spongycastle.cert;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Locale;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.asn1.x509.V3TBSCertificateGenerator;
+import org.spongycastle.operator.ContentSigner;
+
+
+/**
+ * class to produce an X.509 Version 3 certificate.
+ */
+public class X509v3CertificateBuilder
+{
+ private V3TBSCertificateGenerator tbsGen;
+ private ExtensionsGenerator extGenerator;
+
+ /**
+ * Create a builder for a version 3 certificate.
+ *
+ * @param issuer the certificate issuer
+ * @param serial the certificate serial number
+ * @param notBefore the date before which the certificate is not valid
+ * @param notAfter the date after which the certificate is not valid
+ * @param subject the certificate subject
+ * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
+ */
+ public X509v3CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ this(issuer, serial, new Time(notBefore), new Time(notAfter), subject, publicKeyInfo);
+ }
+
+ /**
+ * Create a builder for a version 3 certificate. You may need to use this constructor if the default locale
+ * doesn't use a Gregorian calender so that the Time produced is compatible with other ASN.1 implementations.
+ *
+ * @param issuer the certificate issuer
+ * @param serial the certificate serial number
+ * @param notBefore the date before which the certificate is not valid
+ * @param notAfter the date after which the certificate is not valid
+ * @param dateLocale locale to be used for date interpretation.
+ * @param subject the certificate subject
+ * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
+ */
+ public X509v3CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, Locale dateLocale, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ this(issuer, serial, new Time(notBefore, dateLocale), new Time(notAfter, dateLocale), subject, publicKeyInfo);
+ }
+
+ /**
+ * Create a builder for a version 3 certificate.
+ *
+ * @param issuer the certificate issuer
+ * @param serial the certificate serial number
+ * @param notBefore the Time before which the certificate is not valid
+ * @param notAfter the Time after which the certificate is not valid
+ * @param subject the certificate subject
+ * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
+ */
+ public X509v3CertificateBuilder(X500Name issuer, BigInteger serial, Time notBefore, Time notAfter, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
+ {
+ tbsGen = new V3TBSCertificateGenerator();
+ tbsGen.setSerialNumber(new ASN1Integer(serial));
+ tbsGen.setIssuer(issuer);
+ tbsGen.setStartDate(notBefore);
+ tbsGen.setEndDate(notAfter);
+ tbsGen.setSubject(subject);
+ tbsGen.setSubjectPublicKeyInfo(publicKeyInfo);
+
+ extGenerator = new ExtensionsGenerator();
+ }
+
+ /**
+ * Set the subjectUniqueID - note: it is very rare that it is correct to do this.
+ *
+ * @param uniqueID a boolean array representing the bits making up the subjectUniqueID.
+ * @return this builder object.
+ */
+ public X509v3CertificateBuilder setSubjectUniqueID(boolean[] uniqueID)
+ {
+ tbsGen.setSubjectUniqueID(CertUtils.booleanToBitString(uniqueID));
+
+ return this;
+ }
+
+ /**
+ * Set the issuerUniqueID - note: it is very rare that it is correct to do this.
+ *
+ * @param uniqueID a boolean array representing the bits making up the issuerUniqueID.
+ * @return this builder object.
+ */
+ public X509v3CertificateBuilder setIssuerUniqueID(boolean[] uniqueID)
+ {
+ tbsGen.setIssuerUniqueID(CertUtils.booleanToBitString(uniqueID));
+
+ return this;
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3)
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the extension is critical, false otherwise.
+ * @param value the ASN.1 structure that forms the extension's value.
+ * @return this builder object.
+ */
+ public X509v3CertificateBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ ASN1Encodable value)
+ throws CertIOException
+ {
+ CertUtils.addExtension(extGenerator, oid, isCritical, value);
+
+ return this;
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3) using a byte encoding of the
+ * extension value.
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the extension is critical, false otherwise.
+ * @param encodedValue a byte array representing the encoding of the extension value.
+ * @return this builder object.
+ */
+ public X509v3CertificateBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ byte[] encodedValue)
+ throws CertIOException
+ {
+ extGenerator.addExtension(oid, isCritical, encodedValue);
+
+ return this;
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3)
+ * copying the extension value from another certificate.
+ *
+ * @param oid the OID defining the extension type.
+ * @param isCritical true if the copied extension is to be marked as critical, false otherwise.
+ * @param certHolder the holder for the certificate that the extension is to be copied from.
+ * @return this builder object.
+ */
+ public X509v3CertificateBuilder copyAndAddExtension(
+ ASN1ObjectIdentifier oid,
+ boolean isCritical,
+ X509CertificateHolder certHolder)
+ {
+ Certificate cert = certHolder.toASN1Structure();
+
+ Extension extension = cert.getTBSCertificate().getExtensions().getExtension(oid);
+
+ if (extension == null)
+ {
+ throw new NullPointerException("extension " + oid + " not present");
+ }
+
+ extGenerator.addExtension(oid, isCritical, extension.getExtnValue().getOctets());
+
+ return this;
+ }
+
+ /**
+ * Generate an X.509 certificate, based on the current issuer and subject
+ * using the passed in signer.
+ *
+ * @param signer the content signer to be used to generate the signature validating the certificate.
+ * @return a holder containing the resulting signed certificate.
+ */
+ public X509CertificateHolder build(
+ ContentSigner signer)
+ {
+ tbsGen.setSignature(signer.getAlgorithmIdentifier());
+
+ if (!extGenerator.isEmpty())
+ {
+ tbsGen.setExtensions(extGenerator.generate());
+ }
+
+ return CertUtils.generateFullCert(signer, tbsGen.generateTBSCertificate());
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/bc/BcX509ExtensionUtils.java b/pkix/src/main/java/org/spongycastle/cert/bc/BcX509ExtensionUtils.java
new file mode 100644
index 00000000..6d4dc217
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/bc/BcX509ExtensionUtils.java
@@ -0,0 +1,91 @@
+package org.spongycastle.cert.bc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.cert.X509ExtensionUtils;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.spongycastle.operator.DigestCalculator;
+
+public class BcX509ExtensionUtils
+ extends X509ExtensionUtils
+{
+ /**
+ * Create a utility class pre-configured with a SHA-1 digest calculator based on the
+ * BC implementation.
+ */
+ public BcX509ExtensionUtils()
+ {
+ super(new SHA1DigestCalculator());
+ }
+
+ public BcX509ExtensionUtils(DigestCalculator calculator)
+ {
+ super(calculator);
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(
+ AsymmetricKeyParameter publicKey)
+ throws IOException
+ {
+ return super.createAuthorityKeyIdentifier(SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+ }
+
+ /**
+ * Return a RFC 3280 type 1 key identifier. As in:
+ * <pre>
+ * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+ * value of the BIT STRING subjectPublicKey (excluding the tag,
+ * length, and number of unused bits).
+ * </pre>
+ * @param publicKey the key object containing the key identifier is to be based on.
+ * @return the key identifier.
+ */
+ public SubjectKeyIdentifier createSubjectKeyIdentifier(
+ AsymmetricKeyParameter publicKey)
+ throws IOException
+ {
+ return super.createSubjectKeyIdentifier(SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+ }
+
+ private static class SHA1DigestCalculator
+ implements DigestCalculator
+ {
+ private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1);
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return bOut;
+ }
+
+ public byte[] getDigest()
+ {
+ byte[] bytes = bOut.toByteArray();
+
+ bOut.reset();
+
+ Digest sha1 = new SHA1Digest();
+
+ sha1.update(bytes, 0, bytes.length);
+
+ byte[] digest = new byte[sha1.getDigestSize()];
+
+ sha1.doFinal(digest, 0);
+
+ return digest;
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/bc/BcX509v1CertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/bc/BcX509v1CertificateBuilder.java
new file mode 100644
index 00000000..2036d5d5
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/bc/BcX509v1CertificateBuilder.java
@@ -0,0 +1,33 @@
+package org.spongycastle.cert.bc;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Date;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.X509v1CertificateBuilder;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.util.SubjectPublicKeyInfoFactory;
+
+/**
+ * JCA helper class to allow BC lightweight objects to be used in the construction of a Version 1 certificate.
+ */
+public class BcX509v1CertificateBuilder
+ extends X509v1CertificateBuilder
+{
+ /**
+ * Initialise the builder using an AsymmetricKeyParameter.
+ *
+ * @param issuer X500Name representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject X500Name representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public BcX509v1CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, AsymmetricKeyParameter publicKey)
+ throws IOException
+ {
+ super(issuer, serial, notBefore, notAfter, subject, SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/bc/BcX509v3CertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/bc/BcX509v3CertificateBuilder.java
new file mode 100644
index 00000000..28264101
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/bc/BcX509v3CertificateBuilder.java
@@ -0,0 +1,51 @@
+package org.spongycastle.cert.bc;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Date;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.X509v3CertificateBuilder;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.util.SubjectPublicKeyInfoFactory;
+
+/**
+ * JCA helper class to allow BC lightweight objects to be used in the construction of a Version 3 certificate.
+ */
+public class BcX509v3CertificateBuilder
+ extends X509v3CertificateBuilder
+{
+ /**
+ * Initialise the builder using a PublicKey.
+ *
+ * @param issuer X500Name representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject X500Name representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public BcX509v3CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, AsymmetricKeyParameter publicKey)
+ throws IOException
+ {
+ super(issuer, serial, notBefore, notAfter, subject, SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+ }
+
+ /**
+ * Initialise the builder using the subject from the passed in issuerCert as the issuer, as well as
+ * passing through and converting the other objects provided.
+ *
+ * @param issuerCert holder for certificate who's subject is the issuer of the certificate we are building.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject principal representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public BcX509v3CertificateBuilder(X509CertificateHolder issuerCert, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, AsymmetricKeyParameter publicKey)
+ throws IOException
+ {
+ super(issuerCert.getSubject(), serial, notBefore, notAfter, subject, SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/CMPException.java b/pkix/src/main/java/org/spongycastle/cert/cmp/CMPException.java
new file mode 100644
index 00000000..dc02a03b
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/CMPException.java
@@ -0,0 +1,24 @@
+package org.spongycastle.cert.cmp;
+
+public class CMPException
+ extends Exception
+{
+ private Throwable cause;
+
+ public CMPException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public CMPException(String msg)
+ {
+ super(msg);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/CMPRuntimeException.java b/pkix/src/main/java/org/spongycastle/cert/cmp/CMPRuntimeException.java
new file mode 100644
index 00000000..07f5b819
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/CMPRuntimeException.java
@@ -0,0 +1,19 @@
+package org.spongycastle.cert.cmp;
+
+public class CMPRuntimeException
+ extends RuntimeException
+{
+ private Throwable cause;
+
+ public CMPRuntimeException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/CMPUtil.java b/pkix/src/main/java/org/spongycastle/cert/cmp/CMPUtil.java
new file mode 100644
index 00000000..6198ca84
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/CMPUtil.java
@@ -0,0 +1,26 @@
+package org.spongycastle.cert.cmp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.DEROutputStream;
+
+class CMPUtil
+{
+ static void derEncodeToStream(ASN1Encodable obj, OutputStream stream)
+ {
+ DEROutputStream dOut = new DEROutputStream(stream);
+
+ try
+ {
+ dOut.writeObject(obj);
+
+ dOut.close();
+ }
+ catch (IOException e)
+ {
+ throw new CMPRuntimeException("unable to DER encode object: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContent.java b/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContent.java
new file mode 100644
index 00000000..0f860341
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContent.java
@@ -0,0 +1,41 @@
+package org.spongycastle.cert.cmp;
+
+import org.spongycastle.asn1.cmp.CertConfirmContent;
+import org.spongycastle.asn1.cmp.CertStatus;
+import org.spongycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.spongycastle.operator.DigestAlgorithmIdentifierFinder;
+
+public class CertificateConfirmationContent
+{
+ private DigestAlgorithmIdentifierFinder digestAlgFinder;
+ private CertConfirmContent content;
+
+ public CertificateConfirmationContent(CertConfirmContent content)
+ {
+ this(content, new DefaultDigestAlgorithmIdentifierFinder());
+ }
+
+ public CertificateConfirmationContent(CertConfirmContent content, DigestAlgorithmIdentifierFinder digestAlgFinder)
+ {
+ this.digestAlgFinder = digestAlgFinder;
+ this.content = content;
+ }
+
+ public CertConfirmContent toASN1Structure()
+ {
+ return content;
+ }
+
+ public CertificateStatus[] getStatusMessages()
+ {
+ CertStatus[] statusArray = content.toCertStatusArray();
+ CertificateStatus[] ret = new CertificateStatus[statusArray.length];
+
+ for (int i = 0; i != ret.length; i++)
+ {
+ ret[i] = new CertificateStatus(digestAlgFinder, statusArray[i]);
+ }
+
+ return ret;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContentBuilder.java b/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContentBuilder.java
new file mode 100644
index 00000000..106b3e24
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateConfirmationContentBuilder.java
@@ -0,0 +1,78 @@
+package org.spongycastle.cert.cmp;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.cmp.CertConfirmContent;
+import org.spongycastle.asn1.cmp.CertStatus;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.spongycastle.operator.DigestAlgorithmIdentifierFinder;
+import org.spongycastle.operator.DigestCalculator;
+import org.spongycastle.operator.DigestCalculatorProvider;
+import org.spongycastle.operator.OperatorCreationException;
+
+public class CertificateConfirmationContentBuilder
+{
+ private DigestAlgorithmIdentifierFinder digestAlgFinder;
+ private List acceptedCerts = new ArrayList();
+ private List acceptedReqIds = new ArrayList();
+
+ public CertificateConfirmationContentBuilder()
+ {
+ this(new DefaultDigestAlgorithmIdentifierFinder());
+ }
+
+ public CertificateConfirmationContentBuilder(DigestAlgorithmIdentifierFinder digestAlgFinder)
+ {
+ this.digestAlgFinder = digestAlgFinder;
+ }
+
+ public CertificateConfirmationContentBuilder addAcceptedCertificate(X509CertificateHolder certHolder, BigInteger certReqID)
+ {
+ acceptedCerts.add(certHolder);
+ acceptedReqIds.add(certReqID);
+
+ return this;
+ }
+
+ public CertificateConfirmationContent build(DigestCalculatorProvider digesterProvider)
+ throws CMPException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != acceptedCerts.size(); i++)
+ {
+ X509CertificateHolder certHolder = (X509CertificateHolder)acceptedCerts.get(i);
+ BigInteger reqID = (BigInteger)acceptedReqIds.get(i);
+
+ AlgorithmIdentifier digAlg = digestAlgFinder.find(certHolder.toASN1Structure().getSignatureAlgorithm());
+ if (digAlg == null)
+ {
+ throw new CMPException("cannot find algorithm for digest from signature");
+ }
+
+ DigestCalculator digester;
+
+ try
+ {
+ digester = digesterProvider.get(digAlg);
+ }
+ catch (OperatorCreationException e)
+ {
+ throw new CMPException("unable to create digest: " + e.getMessage(), e);
+ }
+
+ CMPUtil.derEncodeToStream(certHolder.toASN1Structure(), digester.getOutputStream());
+
+ v.add(new CertStatus(digester.getDigest(), reqID));
+ }
+
+ return new CertificateConfirmationContent(CertConfirmContent.getInstance(new DERSequence(v)), digestAlgFinder);
+ }
+
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateStatus.java b/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateStatus.java
new file mode 100644
index 00000000..f2923878
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/CertificateStatus.java
@@ -0,0 +1,60 @@
+package org.spongycastle.cert.cmp;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.cmp.CertStatus;
+import org.spongycastle.asn1.cmp.PKIStatusInfo;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.DigestAlgorithmIdentifierFinder;
+import org.spongycastle.operator.DigestCalculator;
+import org.spongycastle.operator.DigestCalculatorProvider;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.util.Arrays;
+
+public class CertificateStatus
+{
+ private DigestAlgorithmIdentifierFinder digestAlgFinder;
+ private CertStatus certStatus;
+
+ CertificateStatus(DigestAlgorithmIdentifierFinder digestAlgFinder, CertStatus certStatus)
+ {
+ this.digestAlgFinder = digestAlgFinder;
+ this.certStatus = certStatus;
+ }
+
+ public PKIStatusInfo getStatusInfo()
+ {
+ return certStatus.getStatusInfo();
+ }
+
+ public BigInteger getCertRequestID()
+ {
+ return certStatus.getCertReqId().getValue();
+ }
+
+ public boolean isVerified(X509CertificateHolder certHolder, DigestCalculatorProvider digesterProvider)
+ throws CMPException
+ {
+ AlgorithmIdentifier digAlg = digestAlgFinder.find(certHolder.toASN1Structure().getSignatureAlgorithm());
+ if (digAlg == null)
+ {
+ throw new CMPException("cannot find algorithm for digest from signature");
+ }
+
+ DigestCalculator digester;
+
+ try
+ {
+ digester = digesterProvider.get(digAlg);
+ }
+ catch (OperatorCreationException e)
+ {
+ throw new CMPException("unable to create digester: " + e.getMessage(), e);
+ }
+
+ CMPUtil.derEncodeToStream(certHolder.toASN1Structure(), digester.getOutputStream());
+
+ return Arrays.areEqual(certStatus.getCertHash().getOctets(), digester.getDigest());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/GeneralPKIMessage.java b/pkix/src/main/java/org/spongycastle/cert/cmp/GeneralPKIMessage.java
new file mode 100644
index 00000000..5bd49907
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/GeneralPKIMessage.java
@@ -0,0 +1,82 @@
+package org.spongycastle.cert.cmp;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.cmp.PKIBody;
+import org.spongycastle.asn1.cmp.PKIHeader;
+import org.spongycastle.asn1.cmp.PKIMessage;
+import org.spongycastle.cert.CertIOException;
+
+/**
+ * General wrapper for a generic PKIMessage
+ */
+public class GeneralPKIMessage
+{
+ private final PKIMessage pkiMessage;
+
+ private static PKIMessage parseBytes(byte[] encoding)
+ throws IOException
+ {
+ try
+ {
+ return PKIMessage.getInstance(ASN1Primitive.fromByteArray(encoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Create a PKIMessage from the passed in bytes.
+ *
+ * @param encoding BER/DER encoding of the PKIMessage
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public GeneralPKIMessage(byte[] encoding)
+ throws IOException
+ {
+ this(parseBytes(encoding));
+ }
+
+ /**
+ * Wrap a PKIMessage ASN.1 structure.
+ *
+ * @param pkiMessage base PKI message.
+ */
+ public GeneralPKIMessage(PKIMessage pkiMessage)
+ {
+ this.pkiMessage = pkiMessage;
+ }
+
+ public PKIHeader getHeader()
+ {
+ return pkiMessage.getHeader();
+ }
+
+ public PKIBody getBody()
+ {
+ return pkiMessage.getBody();
+ }
+
+ /**
+ * Return true if this message has protection bits on it. A return value of true
+ * indicates the message can be used to construct a ProtectedPKIMessage.
+ *
+ * @return true if message has protection, false otherwise.
+ */
+ public boolean hasProtection()
+ {
+ return pkiMessage.getHeader().getProtectionAlg() != null;
+ }
+
+ public PKIMessage toASN1Structure()
+ {
+ return pkiMessage;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessage.java b/pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessage.java
new file mode 100644
index 00000000..b5dbb980
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessage.java
@@ -0,0 +1,198 @@
+package org.spongycastle.cert.cmp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.cmp.CMPCertificate;
+import org.spongycastle.asn1.cmp.CMPObjectIdentifiers;
+import org.spongycastle.asn1.cmp.PBMParameter;
+import org.spongycastle.asn1.cmp.PKIBody;
+import org.spongycastle.asn1.cmp.PKIHeader;
+import org.spongycastle.asn1.cmp.PKIMessage;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.crmf.PKMACBuilder;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.util.Arrays;
+
+/**
+ * Wrapper for a PKIMessage with protection attached to it.
+ */
+public class ProtectedPKIMessage
+{
+ private PKIMessage pkiMessage;
+
+ /**
+ * Base constructor.
+ *
+ * @param pkiMessage a GeneralPKIMessage with
+ */
+ public ProtectedPKIMessage(GeneralPKIMessage pkiMessage)
+ {
+ if (!pkiMessage.hasProtection())
+ {
+ throw new IllegalArgumentException("PKIMessage not protected");
+ }
+
+ this.pkiMessage = pkiMessage.toASN1Structure();
+ }
+
+ ProtectedPKIMessage(PKIMessage pkiMessage)
+ {
+ if (pkiMessage.getHeader().getProtectionAlg() == null)
+ {
+ throw new IllegalArgumentException("PKIMessage not protected");
+ }
+
+ this.pkiMessage = pkiMessage;
+ }
+
+ /**
+ * Return the message header.
+ *
+ * @return the message's PKIHeader structure.
+ */
+ public PKIHeader getHeader()
+ {
+ return pkiMessage.getHeader();
+ }
+
+ /**
+ * Return the message body.
+ *
+ * @return the message's PKIBody structure.
+ */
+ public PKIBody getBody()
+ {
+ return pkiMessage.getBody();
+ }
+
+ /**
+ * Return the underlying ASN.1 structure contained in this object.
+ *
+ * @return a PKIMessage structure.
+ */
+ public PKIMessage toASN1Structure()
+ {
+ return pkiMessage;
+ }
+
+ /**
+ * Determine whether the message is protected by a password based MAC. Use verify(PKMACBuilder, char[])
+ * to verify the message if this method returns true.
+ *
+ * @return true if protection MAC PBE based, false otherwise.
+ */
+ public boolean hasPasswordBasedMacProtection()
+ {
+ return pkiMessage.getHeader().getProtectionAlg().getAlgorithm().equals(CMPObjectIdentifiers.passwordBasedMac);
+ }
+
+ /**
+ * Return the extra certificates associated with this message.
+ *
+ * @return an array of extra certificates, zero length if none present.
+ */
+ public X509CertificateHolder[] getCertificates()
+ {
+ CMPCertificate[] certs = pkiMessage.getExtraCerts();
+
+ if (certs == null)
+ {
+ return new X509CertificateHolder[0];
+ }
+
+ X509CertificateHolder[] res = new X509CertificateHolder[certs.length];
+ for (int i = 0; i != certs.length; i++)
+ {
+ res[i] = new X509CertificateHolder(certs[i].getX509v3PKCert());
+ }
+
+ return res;
+ }
+
+ /**
+ * Verify a message with a public key based signature attached.
+ *
+ * @param verifierProvider a provider of signature verifiers.
+ * @return true if the provider is able to create a verifier that validates
+ * the signature, false otherwise.
+ * @throws CMPException if an exception is thrown trying to verify the signature.
+ */
+ public boolean verify(ContentVerifierProvider verifierProvider)
+ throws CMPException
+ {
+ ContentVerifier verifier;
+ try
+ {
+ verifier = verifierProvider.get(pkiMessage.getHeader().getProtectionAlg());
+
+ return verifySignature(pkiMessage.getProtection().getBytes(), verifier);
+ }
+ catch (Exception e)
+ {
+ throw new CMPException("unable to verify signature: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Verify a message with password based MAC protection.
+ *
+ * @param pkMacBuilder MAC builder that can be used to construct the appropriate MacCalculator
+ * @param password the MAC password
+ * @return true if the passed in password and MAC builder verify the message, false otherwise.
+ * @throws CMPException if algorithm not MAC based, or an exception is thrown verifying the MAC.
+ */
+ public boolean verify(PKMACBuilder pkMacBuilder, char[] password)
+ throws CMPException
+ {
+ if (!CMPObjectIdentifiers.passwordBasedMac.equals(pkiMessage.getHeader().getProtectionAlg().getAlgorithm()))
+ {
+ throw new CMPException("protection algorithm not mac based");
+ }
+
+ try
+ {
+ pkMacBuilder.setParameters(PBMParameter.getInstance(pkiMessage.getHeader().getProtectionAlg().getParameters()));
+ MacCalculator calculator = pkMacBuilder.build(password);
+
+ OutputStream macOut = calculator.getOutputStream();
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pkiMessage.getHeader());
+ v.add(pkiMessage.getBody());
+
+ macOut.write(new DERSequence(v).getEncoded(ASN1Encoding.DER));
+
+ macOut.close();
+
+ return Arrays.areEqual(calculator.getMac(), pkiMessage.getProtection().getBytes());
+ }
+ catch (Exception e)
+ {
+ throw new CMPException("unable to verify MAC: " + e.getMessage(), e);
+ }
+ }
+
+ private boolean verifySignature(byte[] signature, ContentVerifier verifier)
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(pkiMessage.getHeader());
+ v.add(pkiMessage.getBody());
+
+ OutputStream sOut = verifier.getOutputStream();
+
+ sOut.write(new DERSequence(v).getEncoded(ASN1Encoding.DER));
+
+ sOut.close();
+
+ return verifier.verify(signature);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessageBuilder.java b/pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessageBuilder.java
new file mode 100644
index 00000000..8d5228d1
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/ProtectedPKIMessageBuilder.java
@@ -0,0 +1,306 @@
+package org.spongycastle.cert.cmp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.cmp.CMPCertificate;
+import org.spongycastle.asn1.cmp.InfoTypeAndValue;
+import org.spongycastle.asn1.cmp.PKIBody;
+import org.spongycastle.asn1.cmp.PKIFreeText;
+import org.spongycastle.asn1.cmp.PKIHeader;
+import org.spongycastle.asn1.cmp.PKIHeaderBuilder;
+import org.spongycastle.asn1.cmp.PKIMessage;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.ContentSigner;
+import org.spongycastle.operator.MacCalculator;
+
+/**
+ * Builder for creating a protected PKI message.
+ */
+public class ProtectedPKIMessageBuilder
+{
+ private PKIHeaderBuilder hdrBuilder;
+ private PKIBody body;
+ private List generalInfos = new ArrayList();
+ private List extraCerts = new ArrayList();
+
+ /**
+ * Commence a message with the header version CMP_2000.
+ *
+ * @param sender message sender.
+ * @param recipient intended recipient.
+ */
+ public ProtectedPKIMessageBuilder(GeneralName sender, GeneralName recipient)
+ {
+ this(PKIHeader.CMP_2000, sender, recipient);
+ }
+
+ /**
+ * Commence a message with a specific header type.
+ *
+ * @param pvno the version CMP_1999 or CMP_2000.
+ * @param sender message sender.
+ * @param recipient intended recipient.
+ */
+ public ProtectedPKIMessageBuilder(int pvno, GeneralName sender, GeneralName recipient)
+ {
+ hdrBuilder = new PKIHeaderBuilder(pvno, sender, recipient);
+ }
+
+ /**
+ * Set the identifier for the transaction the new message will belong to.
+ *
+ * @param tid the transaction ID.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setTransactionID(byte[] tid)
+ {
+ hdrBuilder.setTransactionID(tid);
+
+ return this;
+ }
+
+ /**
+ * Include a human-readable message in the new message.
+ *
+ * @param freeText the contents of the human readable message,
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setFreeText(PKIFreeText freeText)
+ {
+ hdrBuilder.setFreeText(freeText);
+
+ return this;
+ }
+
+ /**
+ * Add a generalInfo data record to the header of the new message.
+ *
+ * @param genInfo the generalInfo data to be added.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder addGeneralInfo(InfoTypeAndValue genInfo)
+ {
+ generalInfos.add(genInfo);
+
+ return this;
+ }
+
+ /**
+ * Set the creation time for the new message.
+ *
+ * @param time the message creation time.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setMessageTime(Date time)
+ {
+ hdrBuilder.setMessageTime(new ASN1GeneralizedTime(time));
+
+ return this;
+ }
+
+ /**
+ * Set the recipient key identifier for the key to be used to verify the new message.
+ *
+ * @param kid a key identifier.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setRecipKID(byte[] kid)
+ {
+ hdrBuilder.setRecipKID(kid);
+
+ return this;
+ }
+
+ /**
+ * Set the recipient nonce field on the new message.
+ *
+ * @param nonce a NONCE, typically copied from the sender nonce of the previous message.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setRecipNonce(byte[] nonce)
+ {
+ hdrBuilder.setRecipNonce(nonce);
+
+ return this;
+ }
+
+ /**
+ * Set the sender key identifier for the key used to protect the new message.
+ *
+ * @param kid a key identifier.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setSenderKID(byte[] kid)
+ {
+ hdrBuilder.setSenderKID(kid);
+
+ return this;
+ }
+
+ /**
+ * Set the sender nonce field on the new message.
+ *
+ * @param nonce a NONCE, typically 128 bits of random data.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setSenderNonce(byte[] nonce)
+ {
+ hdrBuilder.setSenderNonce(nonce);
+
+ return this;
+ }
+
+ /**
+ * Set the body for the new message
+ *
+ * @param body the message body.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder setBody(PKIBody body)
+ {
+ this.body = body;
+
+ return this;
+ }
+
+ /**
+ * Add an "extra certificate" to the message.
+ *
+ * @param extraCert the extra certificate to add.
+ * @return the current builder instance.
+ */
+ public ProtectedPKIMessageBuilder addCMPCertificate(X509CertificateHolder extraCert)
+ {
+ extraCerts.add(extraCert);
+
+ return this;
+ }
+
+ /**
+ * Build a protected PKI message which has MAC based integrity protection.
+ *
+ * @param macCalculator MAC calculator.
+ * @return the resulting protected PKI message.
+ * @throws CMPException if the protection MAC cannot be calculated.
+ */
+ public ProtectedPKIMessage build(MacCalculator macCalculator)
+ throws CMPException
+ {
+ finaliseHeader(macCalculator.getAlgorithmIdentifier());
+
+ PKIHeader header = hdrBuilder.build();
+
+ try
+ {
+ DERBitString protection = new DERBitString(calculateMac(macCalculator, header, body));
+
+ return finaliseMessage(header, protection);
+ }
+ catch (IOException e)
+ {
+ throw new CMPException("unable to encode MAC input: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Build a protected PKI message which has MAC based integrity protection.
+ *
+ * @param signer the ContentSigner to be used to calculate the signature.
+ * @return the resulting protected PKI message.
+ * @throws CMPException if the protection signature cannot be calculated.
+ */
+ public ProtectedPKIMessage build(ContentSigner signer)
+ throws CMPException
+ {
+ finaliseHeader(signer.getAlgorithmIdentifier());
+
+ PKIHeader header = hdrBuilder.build();
+
+ try
+ {
+ DERBitString protection = new DERBitString(calculateSignature(signer, header, body));
+
+ return finaliseMessage(header, protection);
+ }
+ catch (IOException e)
+ {
+ throw new CMPException("unable to encode signature input: " + e.getMessage(), e);
+ }
+ }
+
+ private void finaliseHeader(AlgorithmIdentifier algorithmIdentifier)
+ {
+ hdrBuilder.setProtectionAlg(algorithmIdentifier);
+
+ if (!generalInfos.isEmpty())
+ {
+ InfoTypeAndValue[] genInfos = new InfoTypeAndValue[generalInfos.size()];
+
+ hdrBuilder.setGeneralInfo((InfoTypeAndValue[])generalInfos.toArray(genInfos));
+ }
+ }
+
+ private ProtectedPKIMessage finaliseMessage(PKIHeader header, DERBitString protection)
+ {
+ if (!extraCerts.isEmpty())
+ {
+ CMPCertificate[] cmpCerts = new CMPCertificate[extraCerts.size()];
+
+ for (int i = 0; i != cmpCerts.length; i++)
+ {
+ cmpCerts[i] = new CMPCertificate(((X509CertificateHolder)extraCerts.get(i)).toASN1Structure());
+ }
+
+ return new ProtectedPKIMessage(new PKIMessage(header, body, protection, cmpCerts));
+ }
+ else
+ {
+ return new ProtectedPKIMessage(new PKIMessage(header, body, protection));
+ }
+ }
+
+ private byte[] calculateSignature(ContentSigner signer, PKIHeader header, PKIBody body)
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(header);
+ v.add(body);
+
+ OutputStream sOut = signer.getOutputStream();
+
+ sOut.write(new DERSequence(v).getEncoded(ASN1Encoding.DER));
+
+ sOut.close();
+
+ return signer.getSignature();
+ }
+
+ private byte[] calculateMac(MacCalculator macCalculator, PKIHeader header, PKIBody body)
+ throws IOException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(header);
+ v.add(body);
+
+ OutputStream sOut = macCalculator.getOutputStream();
+
+ sOut.write(new DERSequence(v).getEncoded(ASN1Encoding.DER));
+
+ sOut.close();
+
+ return macCalculator.getMac();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetails.java b/pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetails.java
new file mode 100644
index 00000000..a9c4993c
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetails.java
@@ -0,0 +1,36 @@
+package org.spongycastle.cert.cmp;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.cmp.RevDetails;
+import org.spongycastle.asn1.x500.X500Name;
+
+public class RevocationDetails
+{
+ private RevDetails revDetails;
+
+ public RevocationDetails(RevDetails revDetails)
+ {
+ this.revDetails = revDetails;
+ }
+
+ public X500Name getSubject()
+ {
+ return revDetails.getCertDetails().getSubject();
+ }
+
+ public X500Name getIssuer()
+ {
+ return revDetails.getCertDetails().getIssuer();
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return revDetails.getCertDetails().getSerialNumber().getValue();
+ }
+
+ public RevDetails toASN1Structure()
+ {
+ return revDetails;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetailsBuilder.java b/pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetailsBuilder.java
new file mode 100644
index 00000000..bc7eaa04
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/cmp/RevocationDetailsBuilder.java
@@ -0,0 +1,59 @@
+package org.spongycastle.cert.cmp;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.cmp.RevDetails;
+import org.spongycastle.asn1.crmf.CertTemplateBuilder;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+
+public class RevocationDetailsBuilder
+{
+ private CertTemplateBuilder templateBuilder = new CertTemplateBuilder();
+
+ public RevocationDetailsBuilder setPublicKey(SubjectPublicKeyInfo publicKey)
+ {
+ if (publicKey != null)
+ {
+ templateBuilder.setPublicKey(publicKey);
+ }
+
+ return this;
+ }
+
+ public RevocationDetailsBuilder setIssuer(X500Name issuer)
+ {
+ if (issuer != null)
+ {
+ templateBuilder.setIssuer(issuer);
+ }
+
+ return this;
+ }
+
+ public RevocationDetailsBuilder setSerialNumber(BigInteger serialNumber)
+ {
+ if (serialNumber != null)
+ {
+ templateBuilder.setSerialNumber(new ASN1Integer(serialNumber));
+ }
+
+ return this;
+ }
+
+ public RevocationDetailsBuilder setSubject(X500Name subject)
+ {
+ if (subject != null)
+ {
+ templateBuilder.setSubject(subject);
+ }
+
+ return this;
+ }
+
+ public RevocationDetails build()
+ {
+ return new RevocationDetails(new RevDetails(templateBuilder.build()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/AuthenticatorControl.java b/pkix/src/main/java/org/spongycastle/cert/crmf/AuthenticatorControl.java
new file mode 100644
index 00000000..58e6c63a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/AuthenticatorControl.java
@@ -0,0 +1,57 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.crmf.CRMFObjectIdentifiers;
+
+/**
+ * Carrier for an authenticator control.
+ */
+public class AuthenticatorControl
+ implements Control
+{
+ private static final ASN1ObjectIdentifier type = CRMFObjectIdentifiers.id_regCtrl_authenticator;
+
+ private final DERUTF8String token;
+
+ /**
+ * Basic constructor - build from a UTF-8 string representing the token.
+ *
+ * @param token UTF-8 string representing the token.
+ */
+ public AuthenticatorControl(DERUTF8String token)
+ {
+ this.token = token;
+ }
+
+ /**
+ * Basic constructor - build from a string representing the token.
+ *
+ * @param token string representing the token.
+ */
+ public AuthenticatorControl(String token)
+ {
+ this.token = new DERUTF8String(token);
+ }
+
+ /**
+ * Return the type of this control.
+ *
+ * @return CRMFObjectIdentifiers.id_regCtrl_authenticator
+ */
+ public ASN1ObjectIdentifier getType()
+ {
+ return type;
+ }
+
+ /**
+ * Return the token associated with this control (a UTF8String).
+ *
+ * @return a UTF8String.
+ */
+ public ASN1Encodable getValue()
+ {
+ return token;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFException.java b/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFException.java
new file mode 100644
index 00000000..14aa0ad2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFException.java
@@ -0,0 +1,19 @@
+package org.spongycastle.cert.crmf;
+
+public class CRMFException
+ extends Exception
+{
+ private Throwable cause;
+
+ public CRMFException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFRuntimeException.java b/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFRuntimeException.java
new file mode 100644
index 00000000..cde484c4
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFRuntimeException.java
@@ -0,0 +1,19 @@
+package org.spongycastle.cert.crmf;
+
+public class CRMFRuntimeException
+ extends RuntimeException
+{
+ private Throwable cause;
+
+ public CRMFRuntimeException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFUtil.java b/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFUtil.java
new file mode 100644
index 00000000..eb6771e6
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/CRMFUtil.java
@@ -0,0 +1,42 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DEROutputStream;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.cert.CertIOException;
+
+class CRMFUtil
+{
+ static void derEncodeToStream(ASN1Encodable obj, OutputStream stream)
+ {
+ DEROutputStream dOut = new DEROutputStream(stream);
+
+ try
+ {
+ dOut.writeObject(obj);
+
+ dOut.close();
+ }
+ catch (IOException e)
+ {
+ throw new CRMFRuntimeException("unable to DER encode object: " + e.getMessage(), e);
+ }
+ }
+
+ static void addExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value)
+ throws CertIOException
+ {
+ try
+ {
+ extGenerator.addExtension(oid, isCritical, value);
+ }
+ catch (IOException e)
+ {
+ throw new CertIOException("cannot encode extension: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessage.java b/pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessage.java
new file mode 100644
index 00000000..987b6b37
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessage.java
@@ -0,0 +1,309 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.crmf.AttributeTypeAndValue;
+import org.spongycastle.asn1.crmf.CRMFObjectIdentifiers;
+import org.spongycastle.asn1.crmf.CertReqMsg;
+import org.spongycastle.asn1.crmf.CertTemplate;
+import org.spongycastle.asn1.crmf.Controls;
+import org.spongycastle.asn1.crmf.PKIArchiveOptions;
+import org.spongycastle.asn1.crmf.PKMACValue;
+import org.spongycastle.asn1.crmf.POPOSigningKey;
+import org.spongycastle.asn1.crmf.ProofOfPossession;
+import org.spongycastle.cert.CertIOException;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+import org.spongycastle.operator.OperatorCreationException;
+
+/**
+ * Carrier for a CRMF CertReqMsg.
+ */
+public class CertificateRequestMessage
+{
+ public static final int popRaVerified = ProofOfPossession.TYPE_RA_VERIFIED;
+ public static final int popSigningKey = ProofOfPossession.TYPE_SIGNING_KEY;
+ public static final int popKeyEncipherment = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
+ public static final int popKeyAgreement = ProofOfPossession.TYPE_KEY_AGREEMENT;
+
+ private final CertReqMsg certReqMsg;
+ private final Controls controls;
+
+ private static CertReqMsg parseBytes(byte[] encoding)
+ throws IOException
+ {
+ try
+ {
+ return CertReqMsg.getInstance(ASN1Primitive.fromByteArray(encoding));
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed data: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Create a CertificateRequestMessage from the passed in bytes.
+ *
+ * @param certReqMsg BER/DER encoding of the CertReqMsg structure.
+ * @throws IOException in the event of corrupted data, or an incorrect structure.
+ */
+ public CertificateRequestMessage(byte[] certReqMsg)
+ throws IOException
+ {
+ this(parseBytes(certReqMsg));
+ }
+
+ public CertificateRequestMessage(CertReqMsg certReqMsg)
+ {
+ this.certReqMsg = certReqMsg;
+ this.controls = certReqMsg.getCertReq().getControls();
+ }
+
+ /**
+ * Return the underlying ASN.1 object defining this CertificateRequestMessage object.
+ *
+ * @return a CertReqMsg.
+ */
+ public CertReqMsg toASN1Structure()
+ {
+ return certReqMsg;
+ }
+
+ /**
+ * Return the certificate template contained in this message.
+ *
+ * @return a CertTemplate structure.
+ */
+ public CertTemplate getCertTemplate()
+ {
+ return this.certReqMsg.getCertReq().getCertTemplate();
+ }
+
+ /**
+ * Return whether or not this request has control values associated with it.
+ *
+ * @return true if there are control values present, false otherwise.
+ */
+ public boolean hasControls()
+ {
+ return controls != null;
+ }
+
+ /**
+ * Return whether or not this request has a specific type of control value.
+ *
+ * @param type the type OID for the control value we are checking for.
+ * @return true if a control value of type is present, false otherwise.
+ */
+ public boolean hasControl(ASN1ObjectIdentifier type)
+ {
+ return findControl(type) != null;
+ }
+
+ /**
+ * Return a control value of the specified type.
+ *
+ * @param type the type OID for the control value we are checking for.
+ * @return the control value if present, null otherwise.
+ */
+ public Control getControl(ASN1ObjectIdentifier type)
+ {
+ AttributeTypeAndValue found = findControl(type);
+
+ if (found != null)
+ {
+ if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_pkiArchiveOptions))
+ {
+ return new PKIArchiveControl(PKIArchiveOptions.getInstance(found.getValue()));
+ }
+ if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_regToken))
+ {
+ return new RegTokenControl(DERUTF8String.getInstance(found.getValue()));
+ }
+ if (found.getType().equals(CRMFObjectIdentifiers.id_regCtrl_authenticator))
+ {
+ return new AuthenticatorControl(DERUTF8String.getInstance(found.getValue()));
+ }
+ }
+
+ return null;
+ }
+
+ private AttributeTypeAndValue findControl(ASN1ObjectIdentifier type)
+ {
+ if (controls == null)
+ {
+ return null;
+ }
+
+ AttributeTypeAndValue[] tAndVs = controls.toAttributeTypeAndValueArray();
+ AttributeTypeAndValue found = null;
+
+ for (int i = 0; i != tAndVs.length; i++)
+ {
+ if (tAndVs[i].getType().equals(type))
+ {
+ found = tAndVs[i];
+ break;
+ }
+ }
+
+ return found;
+ }
+
+ /**
+ * Return whether or not this request message has a proof-of-possession field in it.
+ *
+ * @return true if proof-of-possession is present, false otherwise.
+ */
+ public boolean hasProofOfPossession()
+ {
+ return this.certReqMsg.getPopo() != null;
+ }
+
+ /**
+ * Return the type of the proof-of-possession this request message provides.
+ *
+ * @return one of: popRaVerified, popSigningKey, popKeyEncipherment, popKeyAgreement
+ */
+ public int getProofOfPossessionType()
+ {
+ return this.certReqMsg.getPopo().getType();
+ }
+
+ /**
+ * Return whether or not the proof-of-possession (POP) is of the type popSigningKey and
+ * it has a public key MAC associated with it.
+ *
+ * @return true if POP is popSigningKey and a PKMAC is present, false otherwise.
+ */
+ public boolean hasSigningKeyProofOfPossessionWithPKMAC()
+ {
+ ProofOfPossession pop = certReqMsg.getPopo();
+
+ if (pop.getType() == popSigningKey)
+ {
+ POPOSigningKey popoSign = POPOSigningKey.getInstance(pop.getObject());
+
+ return popoSign.getPoposkInput().getPublicKeyMAC() != null;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return whether or not a signing key proof-of-possession (POP) is valid.
+ *
+ * @param verifierProvider a provider that can produce content verifiers for the signature contained in this POP.
+ * @return true if the POP is valid, false otherwise.
+ * @throws CRMFException if there is a problem in verification or content verifier creation.
+ * @throws IllegalStateException if POP not appropriate.
+ */
+ public boolean isValidSigningKeyPOP(ContentVerifierProvider verifierProvider)
+ throws CRMFException, IllegalStateException
+ {
+ ProofOfPossession pop = certReqMsg.getPopo();
+
+ if (pop.getType() == popSigningKey)
+ {
+ POPOSigningKey popoSign = POPOSigningKey.getInstance(pop.getObject());
+
+ if (popoSign.getPoposkInput() != null && popoSign.getPoposkInput().getPublicKeyMAC() != null)
+ {
+ throw new IllegalStateException("verification requires password check");
+ }
+
+ return verifySignature(verifierProvider, popoSign);
+ }
+ else
+ {
+ throw new IllegalStateException("not Signing Key type of proof of possession");
+ }
+ }
+
+ /**
+ * Return whether or not a signing key proof-of-possession (POP), with an associated PKMAC, is valid.
+ *
+ * @param verifierProvider a provider that can produce content verifiers for the signature contained in this POP.
+ * @param macBuilder a suitable PKMACBuilder to create the MAC verifier.
+ * @param password the password used to key the MAC calculation.
+ * @return true if the POP is valid, false otherwise.
+ * @throws CRMFException if there is a problem in verification or content verifier creation.
+ * @throws IllegalStateException if POP not appropriate.
+ */
+ public boolean isValidSigningKeyPOP(ContentVerifierProvider verifierProvider, PKMACBuilder macBuilder, char[] password)
+ throws CRMFException, IllegalStateException
+ {
+ ProofOfPossession pop = certReqMsg.getPopo();
+
+ if (pop.getType() == popSigningKey)
+ {
+ POPOSigningKey popoSign = POPOSigningKey.getInstance(pop.getObject());
+
+ if (popoSign.getPoposkInput() == null || popoSign.getPoposkInput().getSender() != null)
+ {
+ throw new IllegalStateException("no PKMAC present in proof of possession");
+ }
+
+ PKMACValue pkMAC = popoSign.getPoposkInput().getPublicKeyMAC();
+ PKMACValueVerifier macVerifier = new PKMACValueVerifier(macBuilder);
+
+ if (macVerifier.isValid(pkMAC, password, this.getCertTemplate().getPublicKey()))
+ {
+ return verifySignature(verifierProvider, popoSign);
+ }
+
+ return false;
+ }
+ else
+ {
+ throw new IllegalStateException("not Signing Key type of proof of possession");
+ }
+ }
+
+ private boolean verifySignature(ContentVerifierProvider verifierProvider, POPOSigningKey popoSign)
+ throws CRMFException
+ {
+ ContentVerifier verifier;
+
+ try
+ {
+ verifier = verifierProvider.get(popoSign.getAlgorithmIdentifier());
+ }
+ catch (OperatorCreationException e)
+ {
+ throw new CRMFException("unable to create verifier: " + e.getMessage(), e);
+ }
+
+ if (popoSign.getPoposkInput() != null)
+ {
+ CRMFUtil.derEncodeToStream(popoSign.getPoposkInput(), verifier.getOutputStream());
+ }
+ else
+ {
+ CRMFUtil.derEncodeToStream(certReqMsg.getCertReq(), verifier.getOutputStream());
+ }
+
+ return verifier.verify(popoSign.getSignature().getBytes());
+ }
+
+ /**
+ * Return the ASN.1 encoding of the certReqMsg we wrap.
+ *
+ * @return a byte array containing the binary encoding of the certReqMsg.
+ * @throws IOException if there is an exception creating the encoding.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return certReqMsg.getEncoded();
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessageBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessageBuilder.java
new file mode 100644
index 00000000..4bfd6451
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/CertificateRequestMessageBuilder.java
@@ -0,0 +1,279 @@
+package org.spongycastle.cert.crmf;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1Null;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.crmf.AttributeTypeAndValue;
+import org.spongycastle.asn1.crmf.CertReqMsg;
+import org.spongycastle.asn1.crmf.CertRequest;
+import org.spongycastle.asn1.crmf.CertTemplate;
+import org.spongycastle.asn1.crmf.CertTemplateBuilder;
+import org.spongycastle.asn1.crmf.OptionalValidity;
+import org.spongycastle.asn1.crmf.POPOPrivKey;
+import org.spongycastle.asn1.crmf.ProofOfPossession;
+import org.spongycastle.asn1.crmf.SubsequentMessage;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.ExtensionsGenerator;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.cert.CertIOException;
+import org.spongycastle.operator.ContentSigner;
+
+public class CertificateRequestMessageBuilder
+{
+ private final BigInteger certReqId;
+
+ private ExtensionsGenerator extGenerator;
+ private CertTemplateBuilder templateBuilder;
+ private List controls;
+ private ContentSigner popSigner;
+ private PKMACBuilder pkmacBuilder;
+ private char[] password;
+ private GeneralName sender;
+ private POPOPrivKey popoPrivKey;
+ private ASN1Null popRaVerified;
+
+ public CertificateRequestMessageBuilder(BigInteger certReqId)
+ {
+ this.certReqId = certReqId;
+
+ this.extGenerator = new ExtensionsGenerator();
+ this.templateBuilder = new CertTemplateBuilder();
+ this.controls = new ArrayList();
+ }
+
+ public CertificateRequestMessageBuilder setPublicKey(SubjectPublicKeyInfo publicKey)
+ {
+ if (publicKey != null)
+ {
+ templateBuilder.setPublicKey(publicKey);
+ }
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setIssuer(X500Name issuer)
+ {
+ if (issuer != null)
+ {
+ templateBuilder.setIssuer(issuer);
+ }
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setSubject(X500Name subject)
+ {
+ if (subject != null)
+ {
+ templateBuilder.setSubject(subject);
+ }
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setSerialNumber(BigInteger serialNumber)
+ {
+ if (serialNumber != null)
+ {
+ templateBuilder.setSerialNumber(new ASN1Integer(serialNumber));
+ }
+
+ return this;
+ }
+
+ /**
+ * Request a validity period for the certificate. Either, but not both, of the date parameters may be null.
+ *
+ * @param notBeforeDate not before date for certificate requested.
+ * @param notAfterDate not after date for the certificate requested.
+ *
+ * @return the current builder.
+ */
+ public CertificateRequestMessageBuilder setValidity(Date notBeforeDate, Date notAfterDate)
+ {
+ templateBuilder.setValidity(new OptionalValidity(createTime(notBeforeDate), createTime(notAfterDate)));
+
+ return this;
+ }
+
+ private Time createTime(Date date)
+ {
+ if (date != null)
+ {
+ return new Time(date);
+ }
+
+ return null;
+ }
+
+ public CertificateRequestMessageBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ ASN1Encodable value)
+ throws CertIOException
+ {
+ CRMFUtil.addExtension(extGenerator, oid, critical, value);
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder addExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ byte[] value)
+ {
+ extGenerator.addExtension(oid, critical, value);
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder addControl(Control control)
+ {
+ controls.add(control);
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setProofOfPossessionSigningKeySigner(ContentSigner popSigner)
+ {
+ if (popoPrivKey != null || popRaVerified != null)
+ {
+ throw new IllegalStateException("only one proof of possession allowed");
+ }
+
+ this.popSigner = popSigner;
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setProofOfPossessionSubsequentMessage(SubsequentMessage msg)
+ {
+ if (popSigner != null || popRaVerified != null)
+ {
+ throw new IllegalStateException("only one proof of possession allowed");
+ }
+
+ this.popoPrivKey = new POPOPrivKey(msg);
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setProofOfPossessionRaVerified()
+ {
+ if (popSigner != null || popoPrivKey != null)
+ {
+ throw new IllegalStateException("only one proof of possession allowed");
+ }
+
+ this.popRaVerified = DERNull.INSTANCE;
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setAuthInfoPKMAC(PKMACBuilder pkmacBuilder, char[] password)
+ {
+ this.pkmacBuilder = pkmacBuilder;
+ this.password = password;
+
+ return this;
+ }
+
+ public CertificateRequestMessageBuilder setAuthInfoSender(X500Name sender)
+ {
+ return setAuthInfoSender(new GeneralName(sender));
+ }
+
+ public CertificateRequestMessageBuilder setAuthInfoSender(GeneralName sender)
+ {
+ this.sender = sender;
+
+ return this;
+ }
+
+ public CertificateRequestMessage build()
+ throws CRMFException
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(certReqId));
+
+ if (!extGenerator.isEmpty())
+ {
+ templateBuilder.setExtensions(extGenerator.generate());
+ }
+
+ v.add(templateBuilder.build());
+
+ if (!controls.isEmpty())
+ {
+ ASN1EncodableVector controlV = new ASN1EncodableVector();
+
+ for (Iterator it = controls.iterator(); it.hasNext();)
+ {
+ Control control = (Control)it.next();
+
+ controlV.add(new AttributeTypeAndValue(control.getType(), control.getValue()));
+ }
+
+ v.add(new DERSequence(controlV));
+ }
+
+ CertRequest request = CertRequest.getInstance(new DERSequence(v));
+
+ v = new ASN1EncodableVector();
+
+ v.add(request);
+
+ if (popSigner != null)
+ {
+ CertTemplate template = request.getCertTemplate();
+
+ if (template.getSubject() == null || template.getPublicKey() == null)
+ {
+ SubjectPublicKeyInfo pubKeyInfo = request.getCertTemplate().getPublicKey();
+ ProofOfPossessionSigningKeyBuilder builder = new ProofOfPossessionSigningKeyBuilder(pubKeyInfo);
+
+ if (sender != null)
+ {
+ builder.setSender(sender);
+ }
+ else
+ {
+ PKMACValueGenerator pkmacGenerator = new PKMACValueGenerator(pkmacBuilder);
+
+ builder.setPublicKeyMac(pkmacGenerator, password);
+ }
+
+ v.add(new ProofOfPossession(builder.build(popSigner)));
+ }
+ else
+ {
+ ProofOfPossessionSigningKeyBuilder builder = new ProofOfPossessionSigningKeyBuilder(request);
+
+ v.add(new ProofOfPossession(builder.build(popSigner)));
+ }
+ }
+ else if (popoPrivKey != null)
+ {
+ v.add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, popoPrivKey));
+ }
+ else if (popRaVerified != null)
+ {
+ v.add(new ProofOfPossession());
+ }
+
+ return new CertificateRequestMessage(CertReqMsg.getInstance(new DERSequence(v)));
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/Control.java b/pkix/src/main/java/org/spongycastle/cert/crmf/Control.java
new file mode 100644
index 00000000..4454f7e7
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/Control.java
@@ -0,0 +1,24 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * Generic interface for a CertificateRequestMessage control value.
+ */
+public interface Control
+{
+ /**
+ * Return the type of this control.
+ *
+ * @return an ASN1ObjectIdentifier representing the type.
+ */
+ ASN1ObjectIdentifier getType();
+
+ /**
+ * Return the value contained in this control object.
+ *
+ * @return the value of the control.
+ */
+ ASN1Encodable getValue();
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueBuilder.java
new file mode 100644
index 00000000..6a34f49c
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueBuilder.java
@@ -0,0 +1,133 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.crmf.EncryptedValue;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.KeyWrapper;
+import org.spongycastle.operator.OperatorException;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.util.Strings;
+
+/**
+ * Builder for EncryptedValue structures.
+ */
+public class EncryptedValueBuilder
+{
+ private KeyWrapper wrapper;
+ private OutputEncryptor encryptor;
+ private EncryptedValuePadder padder;
+
+ /**
+ * Create a builder that makes EncryptedValue structures.
+ *
+ * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue.
+ * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue.
+ */
+ public EncryptedValueBuilder(KeyWrapper wrapper, OutputEncryptor encryptor)
+ {
+ this(wrapper, encryptor, null);
+ }
+
+ /**
+ * Create a builder that makes EncryptedValue structures with fixed length blocks padded using the passed in padder.
+ *
+ * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue.
+ * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue.
+ * @param padder a padder to ensure that the EncryptedValue created will always be a constant length.
+ */
+ public EncryptedValueBuilder(KeyWrapper wrapper, OutputEncryptor encryptor, EncryptedValuePadder padder)
+ {
+ this.wrapper = wrapper;
+ this.encryptor = encryptor;
+ this.padder = padder;
+ }
+
+ /**
+ * Build an EncryptedValue structure containing the passed in pass phrase.
+ *
+ * @param revocationPassphrase a revocation pass phrase.
+ * @return an EncryptedValue containing the encrypted pass phrase.
+ * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+ */
+ public EncryptedValue build(char[] revocationPassphrase)
+ throws CRMFException
+ {
+ return encryptData(padData(Strings.toUTF8ByteArray(revocationPassphrase)));
+ }
+
+ /**
+ * Build an EncryptedValue structure containing the certificate contained in
+ * the passed in holder.
+ *
+ * @param holder a holder containing a certificate.
+ * @return an EncryptedValue containing the encrypted certificate.
+ * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+ */
+ public EncryptedValue build(X509CertificateHolder holder)
+ throws CRMFException
+ {
+ try
+ {
+ return encryptData(padData(holder.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("cannot encode certificate: " + e.getMessage(), e);
+ }
+ }
+
+ private EncryptedValue encryptData(byte[] data)
+ throws CRMFException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ OutputStream eOut = encryptor.getOutputStream(bOut);
+
+ try
+ {
+ eOut.write(data);
+
+ eOut.close();
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("cannot process data: " + e.getMessage(), e);
+ }
+
+ AlgorithmIdentifier intendedAlg = null;
+ AlgorithmIdentifier symmAlg = encryptor.getAlgorithmIdentifier();
+ DERBitString encSymmKey;
+
+ try
+ {
+ wrapper.generateWrappedKey(encryptor.getKey());
+ encSymmKey = new DERBitString(wrapper.generateWrappedKey(encryptor.getKey()));
+ }
+ catch (OperatorException e)
+ {
+ throw new CRMFException("cannot wrap key: " + e.getMessage(), e);
+ }
+
+ AlgorithmIdentifier keyAlg = wrapper.getAlgorithmIdentifier();
+ ASN1OctetString valueHint = null;
+ DERBitString encValue = new DERBitString(bOut.toByteArray());
+
+ return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, encValue);
+ }
+
+ private byte[] padData(byte[] data)
+ {
+ if (padder != null)
+ {
+ return padder.getPaddedData(data);
+ }
+
+ return data;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValuePadder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValuePadder.java
new file mode 100644
index 00000000..d00b5b62
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValuePadder.java
@@ -0,0 +1,24 @@
+package org.spongycastle.cert.crmf;
+
+/**
+ * An encrypted value padder is used to make sure that prior to a value been
+ * encrypted the data is padded to a standard length.
+ */
+public interface EncryptedValuePadder
+{
+ /**
+ * Return a byte array of padded data.
+ *
+ * @param data the data to be padded.
+ * @return a padded byte array containing data.
+ */
+ byte[] getPaddedData(byte[] data);
+
+ /**
+ * Return a byte array of with padding removed.
+ *
+ * @param paddedData the data to be padded.
+ * @return an array containing the original unpadded data.
+ */
+ byte[] getUnpaddedData(byte[] paddedData);
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueParser.java b/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueParser.java
new file mode 100644
index 00000000..80804993
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/EncryptedValueParser.java
@@ -0,0 +1,103 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.asn1.crmf.EncryptedValue;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.InputDecryptor;
+import org.spongycastle.util.Strings;
+import org.spongycastle.util.io.Streams;
+
+/**
+ * Parser for EncryptedValue structures.
+ */
+public class EncryptedValueParser
+{
+ private EncryptedValue value;
+ private EncryptedValuePadder padder;
+
+ /**
+ * Basic constructor - create a parser to read the passed in value.
+ *
+ * @param value the value to be parsed.
+ */
+ public EncryptedValueParser(EncryptedValue value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Create a parser to read the passed in value, assuming the padder was
+ * applied to the data prior to encryption.
+ *
+ * @param value the value to be parsed.
+ * @param padder the padder to be used to remove padding from the decrypted value..
+ */
+ public EncryptedValueParser(EncryptedValue value, EncryptedValuePadder padder)
+ {
+ this.value = value;
+ this.padder = padder;
+ }
+
+ private byte[] decryptValue(ValueDecryptorGenerator decGen)
+ throws CRMFException
+ {
+ if (value.getIntendedAlg() != null)
+ {
+ throw new UnsupportedOperationException();
+ }
+ if (value.getValueHint() != null)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ InputDecryptor decryptor = decGen.getValueDecryptor(value.getKeyAlg(),
+ value.getSymmAlg(), value.getEncSymmKey().getBytes());
+ InputStream dataIn = decryptor.getInputStream(new ByteArrayInputStream(
+ value.getEncValue().getBytes()));
+ try
+ {
+ byte[] data = Streams.readAll(dataIn);
+
+ if (padder != null)
+ {
+ return padder.getUnpaddedData(data);
+ }
+
+ return data;
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("Cannot parse decrypted data: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Read a X.509 certificate.
+ *
+ * @param decGen the decryptor generator to decrypt the encrypted value.
+ * @return an X509CertificateHolder containing the certificate read.
+ * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+ */
+ public X509CertificateHolder readCertificateHolder(ValueDecryptorGenerator decGen)
+ throws CRMFException
+ {
+ return new X509CertificateHolder(Certificate.getInstance(decryptValue(decGen)));
+ }
+
+ /**
+ * Read a pass phrase.
+ *
+ * @param decGen the decryptor generator to decrypt the encrypted value.
+ * @return a pass phrase as recovered from the encrypted value.
+ * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+ */
+ public char[] readPassphrase(ValueDecryptorGenerator decGen)
+ throws CRMFException
+ {
+ return Strings.fromUTF8ByteArray(decryptValue(decGen)).toCharArray();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControl.java b/pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControl.java
new file mode 100644
index 00000000..0c9bd985
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControl.java
@@ -0,0 +1,104 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.cms.CMSObjectIdentifiers;
+import org.spongycastle.asn1.cms.ContentInfo;
+import org.spongycastle.asn1.cms.EnvelopedData;
+import org.spongycastle.asn1.crmf.CRMFObjectIdentifiers;
+import org.spongycastle.asn1.crmf.EncryptedKey;
+import org.spongycastle.asn1.crmf.PKIArchiveOptions;
+import org.spongycastle.cms.CMSEnvelopedData;
+import org.spongycastle.cms.CMSException;
+
+/**
+ * Carrier for a PKIArchiveOptions structure.
+ */
+public class PKIArchiveControl
+ implements Control
+{
+ public static final int encryptedPrivKey = PKIArchiveOptions.encryptedPrivKey;
+ public static final int keyGenParameters = PKIArchiveOptions.keyGenParameters;
+ public static final int archiveRemGenPrivKey = PKIArchiveOptions.archiveRemGenPrivKey;
+
+ private static final ASN1ObjectIdentifier type = CRMFObjectIdentifiers.id_regCtrl_pkiArchiveOptions;
+
+ private final PKIArchiveOptions pkiArchiveOptions;
+
+ /**
+ * Basic constructor - build from an PKIArchiveOptions structure.
+ *
+ * @param pkiArchiveOptions the ASN.1 structure that will underlie this control.
+ */
+ public PKIArchiveControl(PKIArchiveOptions pkiArchiveOptions)
+ {
+ this.pkiArchiveOptions = pkiArchiveOptions;
+ }
+
+ /**
+ * Return the type of this control.
+ *
+ * @return CRMFObjectIdentifiers.id_regCtrl_pkiArchiveOptions
+ */
+ public ASN1ObjectIdentifier getType()
+ {
+ return type;
+ }
+
+ /**
+ * Return the underlying ASN.1 object.
+ *
+ * @return a PKIArchiveOptions structure.
+ */
+ public ASN1Encodable getValue()
+ {
+ return pkiArchiveOptions;
+ }
+
+ /**
+ * Return the archive control type, one of: encryptedPrivKey,keyGenParameters,or archiveRemGenPrivKey.
+ *
+ * @return the archive control type.
+ */
+ public int getArchiveType()
+ {
+ return pkiArchiveOptions.getType();
+ }
+
+ /**
+ * Return whether this control contains enveloped data.
+ *
+ * @return true if the control contains enveloped data, false otherwise.
+ */
+ public boolean isEnvelopedData()
+ {
+ EncryptedKey encKey = EncryptedKey.getInstance(pkiArchiveOptions.getValue());
+
+ return !encKey.isEncryptedValue();
+ }
+
+ /**
+ * Return the enveloped data structure contained in this control.
+ *
+ * @return a CMSEnvelopedData object.
+ */
+ public CMSEnvelopedData getEnvelopedData()
+ throws CRMFException
+ {
+ try
+ {
+ EncryptedKey encKey = EncryptedKey.getInstance(pkiArchiveOptions.getValue());
+ EnvelopedData data = EnvelopedData.getInstance(encKey.getValue());
+
+ return new CMSEnvelopedData(new ContentInfo(CMSObjectIdentifiers.envelopedData, data));
+ }
+ catch (CMSException e)
+ {
+ throw new CRMFException("CMS parsing error: " + e.getMessage(), e.getCause());
+ }
+ catch (Exception e)
+ {
+ throw new CRMFException("CRMF parsing error: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControlBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControlBuilder.java
new file mode 100644
index 00000000..1ca860cb
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/PKIArchiveControlBuilder.java
@@ -0,0 +1,78 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.cms.EnvelopedData;
+import org.spongycastle.asn1.crmf.CRMFObjectIdentifiers;
+import org.spongycastle.asn1.crmf.EncKeyWithID;
+import org.spongycastle.asn1.crmf.EncryptedKey;
+import org.spongycastle.asn1.crmf.PKIArchiveOptions;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.cms.CMSEnvelopedData;
+import org.spongycastle.cms.CMSEnvelopedDataGenerator;
+import org.spongycastle.cms.CMSException;
+import org.spongycastle.cms.CMSProcessableByteArray;
+import org.spongycastle.cms.RecipientInfoGenerator;
+import org.spongycastle.operator.OutputEncryptor;
+
+/**
+ * Builder for a PKIArchiveControl structure.
+ */
+public class PKIArchiveControlBuilder
+{
+ private CMSEnvelopedDataGenerator envGen;
+ private CMSProcessableByteArray keyContent;
+
+ /**
+ * Basic constructor - specify the contents of the PKIArchiveControl structure.
+ *
+ * @param privateKeyInfo the private key to be archived.
+ * @param generalName the general name to be associated with the private key.
+ */
+ public PKIArchiveControlBuilder(PrivateKeyInfo privateKeyInfo, GeneralName generalName)
+ {
+ EncKeyWithID encKeyWithID = new EncKeyWithID(privateKeyInfo, generalName);
+
+ try
+ {
+ this.keyContent = new CMSProcessableByteArray(CRMFObjectIdentifiers.id_ct_encKeyWithID, encKeyWithID.getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unable to encode key and general name info");
+ }
+
+ this.envGen = new CMSEnvelopedDataGenerator();
+ }
+
+ /**
+ * Add a recipient generator to this control.
+ *
+ * @param recipientGen recipient generator created for a specific recipient.
+ * @return this builder object.
+ */
+ public PKIArchiveControlBuilder addRecipientGenerator(RecipientInfoGenerator recipientGen)
+ {
+ envGen.addRecipientInfoGenerator(recipientGen);
+
+ return this;
+ }
+
+ /**
+ * Build the PKIArchiveControl using the passed in encryptor to encrypt its contents.
+ *
+ * @param contentEncryptor a suitable content encryptor.
+ * @return a PKIArchiveControl object.
+ * @throws CMSException in the event the build fails.
+ */
+ public PKIArchiveControl build(OutputEncryptor contentEncryptor)
+ throws CMSException
+ {
+ CMSEnvelopedData envContent = envGen.generate(keyContent, contentEncryptor);
+
+ EnvelopedData envD = EnvelopedData.getInstance(envContent.toASN1Structure().getContent());
+
+ return new PKIArchiveControl(new PKIArchiveOptions(new EncryptedKey(envD)));
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACBuilder.java
new file mode 100644
index 00000000..54e9cd91
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACBuilder.java
@@ -0,0 +1,199 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.cmp.CMPObjectIdentifiers;
+import org.spongycastle.asn1.cmp.PBMParameter;
+import org.spongycastle.asn1.iana.IANAObjectIdentifiers;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.operator.RuntimeOperatorException;
+import org.spongycastle.util.Strings;
+
+public class PKMACBuilder
+{
+ private AlgorithmIdentifier owf;
+ private int iterationCount;
+ private AlgorithmIdentifier mac;
+ private int saltLength = 20;
+ private SecureRandom random;
+ private PKMACValuesCalculator calculator;
+ private PBMParameter parameters;
+ private int maxIterations;
+
+ public PKMACBuilder(PKMACValuesCalculator calculator)
+ {
+ this(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), 1000, new AlgorithmIdentifier(IANAObjectIdentifiers.hmacSHA1, DERNull.INSTANCE), calculator);
+ }
+
+ /**
+ * Create a PKMAC builder enforcing a ceiling on the maximum iteration count.
+ *
+ * @param calculator supporting calculator
+ * @param maxIterations max allowable value for iteration count.
+ */
+ public PKMACBuilder(PKMACValuesCalculator calculator, int maxIterations)
+ {
+ this.maxIterations = maxIterations;
+ this.calculator = calculator;
+ }
+
+ private PKMACBuilder(AlgorithmIdentifier hashAlgorithm, int iterationCount, AlgorithmIdentifier macAlgorithm, PKMACValuesCalculator calculator)
+ {
+ this.owf = hashAlgorithm;
+ this.iterationCount = iterationCount;
+ this.mac = macAlgorithm;
+ this.calculator = calculator;
+ }
+
+ /**
+ * Set the salt length in octets.
+ *
+ * @param saltLength length in octets of the salt to be generated.
+ * @return the generator
+ */
+ public PKMACBuilder setSaltLength(int saltLength)
+ {
+ if (saltLength < 8)
+ {
+ throw new IllegalArgumentException("salt length must be at least 8 bytes");
+ }
+
+ this.saltLength = saltLength;
+
+ return this;
+ }
+
+ public PKMACBuilder setIterationCount(int iterationCount)
+ {
+ if (iterationCount < 100)
+ {
+ throw new IllegalArgumentException("iteration count must be at least 100");
+ }
+ checkIterationCountCeiling(iterationCount);
+
+ this.iterationCount = iterationCount;
+
+ return this;
+ }
+
+ public PKMACBuilder setSecureRandom(SecureRandom random)
+ {
+ this.random = random;
+
+ return this;
+ }
+
+ public PKMACBuilder setParameters(PBMParameter parameters)
+ {
+ checkIterationCountCeiling(parameters.getIterationCount().getValue().intValue());
+
+ this.parameters = parameters;
+
+ return this;
+ }
+
+ public MacCalculator build(char[] password)
+ throws CRMFException
+ {
+ if (parameters != null)
+ {
+ return genCalculator(parameters, password);
+ }
+ else
+ {
+ byte[] salt = new byte[saltLength];
+
+ if (random == null)
+ {
+ this.random = new SecureRandom();
+ }
+
+ random.nextBytes(salt);
+
+ return genCalculator(new PBMParameter(salt, owf, iterationCount, mac), password);
+ }
+ }
+
+ private void checkIterationCountCeiling(int iterationCount)
+ {
+ if (maxIterations > 0 && iterationCount > maxIterations)
+ {
+ throw new IllegalArgumentException("iteration count exceeds limit (" + iterationCount + " > " + maxIterations + ")");
+ }
+ }
+
+ private MacCalculator genCalculator(final PBMParameter params, char[] password)
+ throws CRMFException
+ {
+ // From RFC 4211
+ //
+ // 1. Generate a random salt value S
+ //
+ // 2. Append the salt to the pw. K = pw || salt.
+ //
+ // 3. Hash the value of K. K = HASH(K)
+ //
+ // 4. Iter = Iter - 1. If Iter is greater than zero. Goto step 3.
+ //
+ // 5. Compute an HMAC as documented in [HMAC].
+ //
+ // MAC = HASH( K XOR opad, HASH( K XOR ipad, data) )
+ //
+ // Where opad and ipad are defined in [HMAC].
+ byte[] pw = Strings.toUTF8ByteArray(password);
+ byte[] salt = params.getSalt().getOctets();
+ byte[] K = new byte[pw.length + salt.length];
+
+ System.arraycopy(pw, 0, K, 0, pw.length);
+ System.arraycopy(salt, 0, K, pw.length, salt.length);
+
+ calculator.setup(params.getOwf(), params.getMac());
+
+ int iter = params.getIterationCount().getValue().intValue();
+ do
+ {
+ K = calculator.calculateDigest(K);
+ }
+ while (--iter > 0);
+
+ final byte[] key = K;
+
+ return new MacCalculator()
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(CMPObjectIdentifiers.passwordBasedMac, params);
+ }
+
+ public GenericKey getKey()
+ {
+ return new GenericKey(getAlgorithmIdentifier(), key);
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return bOut;
+ }
+
+ public byte[] getMac()
+ {
+ try
+ {
+ return calculator.calculateMac(key, bOut.toByteArray());
+ }
+ catch (CRMFException e)
+ {
+ throw new RuntimeOperatorException("exception calculating mac: " + e.getMessage(), e);
+ }
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueGenerator.java b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueGenerator.java
new file mode 100644
index 00000000..eaf215ff
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueGenerator.java
@@ -0,0 +1,41 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.crmf.PKMACValue;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.MacCalculator;
+
+class PKMACValueGenerator
+{
+ private PKMACBuilder builder;
+
+ public PKMACValueGenerator(PKMACBuilder builder)
+ {
+ this.builder = builder;
+ }
+
+ public PKMACValue generate(char[] password, SubjectPublicKeyInfo keyInfo)
+ throws CRMFException
+ {
+ MacCalculator calculator = builder.build(password);
+
+ OutputStream macOut = calculator.getOutputStream();
+
+ try
+ {
+ macOut.write(keyInfo.getEncoded(ASN1Encoding.DER));
+
+ macOut.close();
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("exception encoding mac input: " + e.getMessage(), e);
+ }
+
+ return new PKMACValue(calculator.getAlgorithmIdentifier(), new DERBitString(calculator.getMac()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueVerifier.java b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueVerifier.java
new file mode 100644
index 00000000..a65ff61d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValueVerifier.java
@@ -0,0 +1,43 @@
+package org.spongycastle.cert.crmf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.cmp.PBMParameter;
+import org.spongycastle.asn1.crmf.PKMACValue;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.MacCalculator;
+import org.spongycastle.util.Arrays;
+
+class PKMACValueVerifier
+{
+ private final PKMACBuilder builder;
+
+ public PKMACValueVerifier(PKMACBuilder builder)
+ {
+ this.builder = builder;
+ }
+
+ public boolean isValid(PKMACValue value, char[] password, SubjectPublicKeyInfo keyInfo)
+ throws CRMFException
+ {
+ builder.setParameters(PBMParameter.getInstance(value.getAlgId().getParameters()));
+ MacCalculator calculator = builder.build(password);
+
+ OutputStream macOut = calculator.getOutputStream();
+
+ try
+ {
+ macOut.write(keyInfo.getEncoded(ASN1Encoding.DER));
+
+ macOut.close();
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("exception encoding mac input: " + e.getMessage(), e);
+ }
+
+ return Arrays.areEqual(calculator.getMac(), value.getValue().getBytes());
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValuesCalculator.java b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValuesCalculator.java
new file mode 100644
index 00000000..0b4f1407
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/PKMACValuesCalculator.java
@@ -0,0 +1,15 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+
+public interface PKMACValuesCalculator
+{
+ void setup(AlgorithmIdentifier digestAlg, AlgorithmIdentifier macAlg)
+ throws CRMFException;
+
+ byte[] calculateDigest(byte[] data)
+ throws CRMFException;
+
+ byte[] calculateMac(byte[] pwd, byte[] data)
+ throws CRMFException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/ProofOfPossessionSigningKeyBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/ProofOfPossessionSigningKeyBuilder.java
new file mode 100644
index 00000000..07659593
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/ProofOfPossessionSigningKeyBuilder.java
@@ -0,0 +1,75 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.crmf.CertRequest;
+import org.spongycastle.asn1.crmf.PKMACValue;
+import org.spongycastle.asn1.crmf.POPOSigningKey;
+import org.spongycastle.asn1.crmf.POPOSigningKeyInput;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.ContentSigner;
+
+public class ProofOfPossessionSigningKeyBuilder
+{
+ private CertRequest certRequest;
+ private SubjectPublicKeyInfo pubKeyInfo;
+ private GeneralName name;
+ private PKMACValue publicKeyMAC;
+
+ public ProofOfPossessionSigningKeyBuilder(CertRequest certRequest)
+ {
+ this.certRequest = certRequest;
+ }
+
+
+ public ProofOfPossessionSigningKeyBuilder(SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.pubKeyInfo = pubKeyInfo;
+ }
+
+ public ProofOfPossessionSigningKeyBuilder setSender(GeneralName name)
+ {
+ this.name = name;
+
+ return this;
+ }
+
+ public ProofOfPossessionSigningKeyBuilder setPublicKeyMac(PKMACValueGenerator generator, char[] password)
+ throws CRMFException
+ {
+ this.publicKeyMAC = generator.generate(password, pubKeyInfo);
+
+ return this;
+ }
+
+ public POPOSigningKey build(ContentSigner signer)
+ {
+ if (name != null && publicKeyMAC != null)
+ {
+ throw new IllegalStateException("name and publicKeyMAC cannot both be set.");
+ }
+
+ POPOSigningKeyInput popo;
+
+ if (certRequest != null)
+ {
+ popo = null;
+
+ CRMFUtil.derEncodeToStream(certRequest, signer.getOutputStream());
+ }
+ else if (name != null)
+ {
+ popo = new POPOSigningKeyInput(name, pubKeyInfo);
+
+ CRMFUtil.derEncodeToStream(popo, signer.getOutputStream());
+ }
+ else
+ {
+ popo = new POPOSigningKeyInput(publicKeyMAC, pubKeyInfo);
+
+ CRMFUtil.derEncodeToStream(popo, signer.getOutputStream());
+ }
+
+ return new POPOSigningKey(popo, signer.getAlgorithmIdentifier(), new DERBitString(signer.getSignature()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/RegTokenControl.java b/pkix/src/main/java/org/spongycastle/cert/crmf/RegTokenControl.java
new file mode 100644
index 00000000..b1a6eb7e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/RegTokenControl.java
@@ -0,0 +1,57 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERUTF8String;
+import org.spongycastle.asn1.crmf.CRMFObjectIdentifiers;
+
+/**
+ * Carrier for a registration token control.
+ */
+public class RegTokenControl
+ implements Control
+{
+ private static final ASN1ObjectIdentifier type = CRMFObjectIdentifiers.id_regCtrl_regToken;
+
+ private final DERUTF8String token;
+
+ /**
+ * Basic constructor - build from a UTF-8 string representing the token.
+ *
+ * @param token UTF-8 string representing the token.
+ */
+ public RegTokenControl(DERUTF8String token)
+ {
+ this.token = token;
+ }
+
+ /**
+ * Basic constructor - build from a string representing the token.
+ *
+ * @param token string representing the token.
+ */
+ public RegTokenControl(String token)
+ {
+ this.token = new DERUTF8String(token);
+ }
+
+ /**
+ * Return the type of this control.
+ *
+ * @return CRMFObjectIdentifiers.id_regCtrl_regToken
+ */
+ public ASN1ObjectIdentifier getType()
+ {
+ return type;
+ }
+
+ /**
+ * Return the token associated with this control (a UTF8String).
+ *
+ * @return a UTF8String.
+ */
+ public ASN1Encodable getValue()
+ {
+ return token;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/ValueDecryptorGenerator.java b/pkix/src/main/java/org/spongycastle/cert/crmf/ValueDecryptorGenerator.java
new file mode 100644
index 00000000..3fcee4eb
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/ValueDecryptorGenerator.java
@@ -0,0 +1,10 @@
+package org.spongycastle.cert.crmf;
+
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.operator.InputDecryptor;
+
+public interface ValueDecryptorGenerator
+{
+ InputDecryptor getValueDecryptor(AlgorithmIdentifier keyAlg, AlgorithmIdentifier symmAlg, byte[] encKey)
+ throws CRMFException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/bc/BcFixedLengthMGF1Padder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/bc/BcFixedLengthMGF1Padder.java
new file mode 100644
index 00000000..756acd7d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/bc/BcFixedLengthMGF1Padder.java
@@ -0,0 +1,121 @@
+package org.spongycastle.cert.crmf.bc;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.cert.crmf.EncryptedValuePadder;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.digests.SHA1Digest;
+import org.spongycastle.crypto.generators.MGF1BytesGenerator;
+import org.spongycastle.crypto.params.MGFParameters;
+
+/**
+ * An encrypted value padder that uses MGF1 as the basis of the padding.
+ */
+public class BcFixedLengthMGF1Padder
+ implements EncryptedValuePadder
+{
+ private int length;
+ private SecureRandom random;
+ private Digest dig = new SHA1Digest();
+
+ /**
+ * Create a padder to so that padded output will always be at least
+ * length bytes long.
+ *
+ * @param length fixed length for padded output.
+ */
+ public BcFixedLengthMGF1Padder(int length)
+ {
+ this(length, null);
+ }
+
+ /**
+ * Create a padder to so that padded output will always be at least
+ * length bytes long, using the passed in source of randomness to
+ * provide the random material for the padder.
+ *
+ * @param length fixed length for padded output.
+ * @param random a source of randomness.
+ */
+ public BcFixedLengthMGF1Padder(int length, SecureRandom random)
+ {
+ this.length = length;
+ this.random = random;
+ }
+
+ public byte[] getPaddedData(byte[] data)
+ {
+ byte[] bytes = new byte[length];
+ byte[] seed = new byte[dig.getDigestSize()];
+ byte[] mask = new byte[length - dig.getDigestSize()];
+
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ random.nextBytes(seed);
+
+ MGF1BytesGenerator maskGen = new MGF1BytesGenerator(dig);
+
+ maskGen.init(new MGFParameters(seed));
+
+ maskGen.generateBytes(mask, 0, mask.length);
+
+ System.arraycopy(seed, 0, bytes, 0, seed.length);
+ System.arraycopy(data, 0, bytes, seed.length, data.length);
+
+ for (int i = seed.length + data.length + 1; i != bytes.length; i++)
+ {
+ bytes[i] = (byte)(1 + random.nextInt(255));
+ }
+
+ for (int i = 0; i != mask.length; i++)
+ {
+ bytes[i + seed.length] ^= mask[i];
+ }
+
+ return bytes;
+ }
+
+ public byte[] getUnpaddedData(byte[] paddedData)
+ {
+ byte[] seed = new byte[dig.getDigestSize()];
+ byte[] mask = new byte[length - dig.getDigestSize()];
+
+ System.arraycopy(paddedData, 0, seed, 0, seed.length);
+
+ MGF1BytesGenerator maskGen = new MGF1BytesGenerator(dig);
+
+ maskGen.init(new MGFParameters(seed));
+
+ maskGen.generateBytes(mask, 0, mask.length);
+
+ for (int i = 0; i != mask.length; i++)
+ {
+ paddedData[i + seed.length] ^= mask[i];
+ }
+
+ int end = 0;
+
+ for (int i = paddedData.length - 1; i != seed.length; i--)
+ {
+ if (paddedData[i] == 0)
+ {
+ end = i;
+ break;
+ }
+ }
+
+ if (end == 0)
+ {
+ throw new IllegalStateException("bad padding in encoding");
+ }
+
+ byte[] data = new byte[end - seed.length];
+
+ System.arraycopy(paddedData, seed.length, data, 0, data.length);
+
+ return data;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/CRMFHelper.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/CRMFHelper.java
new file mode 100644
index 00000000..92d7faa6
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/CRMFHelper.java
@@ -0,0 +1,450 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.io.IOException;
+import java.security.AlgorithmParameterGenerator;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Null;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.iana.IANAObjectIdentifiers;
+import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.cert.crmf.CRMFException;
+import org.spongycastle.cms.CMSAlgorithm;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceUtils;
+
+class CRMFHelper
+{
+ protected static final Map BASE_CIPHER_NAMES = new HashMap();
+ protected static final Map CIPHER_ALG_NAMES = new HashMap();
+ protected static final Map DIGEST_ALG_NAMES = new HashMap();
+ protected static final Map KEY_ALG_NAMES = new HashMap();
+ protected static final Map MAC_ALG_NAMES = new HashMap();
+
+ static
+ {
+ BASE_CIPHER_NAMES.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESEDE");
+ BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes128_CBC, "AES");
+ BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes192_CBC, "AES");
+ BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes256_CBC, "AES");
+
+ CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_EDE3_CBC, "DESEDE/CBC/PKCS5Padding");
+ CIPHER_ALG_NAMES.put(CMSAlgorithm.AES128_CBC, "AES/CBC/PKCS5Padding");
+ CIPHER_ALG_NAMES.put(CMSAlgorithm.AES192_CBC, "AES/CBC/PKCS5Padding");
+ CIPHER_ALG_NAMES.put(CMSAlgorithm.AES256_CBC, "AES/CBC/PKCS5Padding");
+ CIPHER_ALG_NAMES.put(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()), "RSA/ECB/PKCS1Padding");
+
+ DIGEST_ALG_NAMES.put(OIWObjectIdentifiers.idSHA1, "SHA1");
+ DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha224, "SHA224");
+ DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha256, "SHA256");
+ DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha384, "SHA384");
+ DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+
+ MAC_ALG_NAMES.put(IANAObjectIdentifiers.hmacSHA1, "HMACSHA1");
+ MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA1, "HMACSHA1");
+ MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA224, "HMACSHA224");
+ MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA256, "HMACSHA256");
+ MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA384, "HMACSHA384");
+ MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA512, "HMACSHA512");
+
+ KEY_ALG_NAMES.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+ KEY_ALG_NAMES.put(X9ObjectIdentifiers.id_dsa, "DSA");
+ }
+
+ private JcaJceHelper helper;
+
+ CRMFHelper(JcaJceHelper helper)
+ {
+ this.helper = helper;
+ }
+
+ PublicKey toPublicKey(SubjectPublicKeyInfo subjectPublicKeyInfo)
+ throws CRMFException
+ {
+ try
+ {
+ X509EncodedKeySpec xspec = new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded());
+ AlgorithmIdentifier keyAlg = subjectPublicKeyInfo.getAlgorithm();
+
+ return createKeyFactory(keyAlg.getAlgorithm()).generatePublic(xspec);
+ }
+ catch (Exception e)
+ {
+ throw new CRMFException("invalid key: " + e.getMessage(), e);
+ }
+ }
+
+ Cipher createCipher(ASN1ObjectIdentifier algorithm)
+ throws CRMFException
+ {
+ try
+ {
+ String cipherName = (String)CIPHER_ALG_NAMES.get(algorithm);
+
+ if (cipherName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createCipher(cipherName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createCipher(algorithm.getId());
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
+ }
+ }
+
+ public KeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm)
+ throws CRMFException
+ {
+ try
+ {
+ String cipherName = (String)BASE_CIPHER_NAMES.get(algorithm);
+
+ if (cipherName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createKeyGenerator(cipherName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createKeyGenerator(algorithm.getId());
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("cannot create key generator: " + e.getMessage(), e);
+ }
+ }
+
+
+
+ Cipher createContentCipher(final Key sKey, final AlgorithmIdentifier encryptionAlgID)
+ throws CRMFException
+ {
+ return (Cipher)execute(new JCECallback()
+ {
+ public Object doInJCE()
+ throws CRMFException, InvalidAlgorithmParameterException,
+ InvalidKeyException, InvalidParameterSpecException, NoSuchAlgorithmException,
+ NoSuchPaddingException, NoSuchProviderException
+ {
+ Cipher cipher = createCipher(encryptionAlgID.getAlgorithm());
+ ASN1Primitive sParams = (ASN1Primitive)encryptionAlgID.getParameters();
+ ASN1ObjectIdentifier encAlg = encryptionAlgID.getAlgorithm();
+
+ if (sParams != null && !(sParams instanceof ASN1Null))
+ {
+ try
+ {
+ AlgorithmParameters params = createAlgorithmParameters(encryptionAlgID.getAlgorithm());
+
+ try
+ {
+ JcaJceUtils.loadParameters(params, sParams);
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("error decoding algorithm parameters.", e);
+ }
+
+ cipher.init(Cipher.DECRYPT_MODE, sKey, params);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ if (encAlg.equals(CMSAlgorithm.DES_EDE3_CBC)
+ || encAlg.equals(CMSAlgorithm.IDEA_CBC)
+ || encAlg.equals(CMSAlgorithm.AES128_CBC)
+ || encAlg.equals(CMSAlgorithm.AES192_CBC)
+ || encAlg.equals(CMSAlgorithm.AES256_CBC))
+ {
+ cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(
+ ASN1OctetString.getInstance(sParams).getOctets()));
+ }
+ else
+ {
+ throw e;
+ }
+ }
+ }
+ else
+ {
+ if (encAlg.equals(CMSAlgorithm.DES_EDE3_CBC)
+ || encAlg.equals(CMSAlgorithm.IDEA_CBC)
+ || encAlg.equals(CMSAlgorithm.CAST5_CBC))
+ {
+ cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(new byte[8]));
+ }
+ else
+ {
+ cipher.init(Cipher.DECRYPT_MODE, sKey);
+ }
+ }
+
+ return cipher;
+ }
+ });
+ }
+
+ AlgorithmParameters createAlgorithmParameters(ASN1ObjectIdentifier algorithm)
+ throws NoSuchAlgorithmException, NoSuchProviderException
+ {
+ String algorithmName = (String)BASE_CIPHER_NAMES.get(algorithm);
+
+ if (algorithmName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createAlgorithmParameters(algorithmName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createAlgorithmParameters(algorithm.getId());
+ }
+
+ KeyFactory createKeyFactory(ASN1ObjectIdentifier algorithm)
+ throws CRMFException
+ {
+ try
+ {
+ String algName = (String)KEY_ALG_NAMES.get(algorithm);
+
+ if (algName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createKeyFactory(algName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createKeyFactory(algorithm.getId());
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
+ }
+ }
+
+ MessageDigest createDigest(ASN1ObjectIdentifier algorithm)
+ throws CRMFException
+ {
+ try
+ {
+ String digestName = (String)DIGEST_ALG_NAMES.get(algorithm);
+
+ if (digestName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createDigest(digestName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createDigest(algorithm.getId());
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
+ }
+ }
+
+ Mac createMac(ASN1ObjectIdentifier algorithm)
+ throws CRMFException
+ {
+ try
+ {
+ String macName = (String)MAC_ALG_NAMES.get(algorithm);
+
+ if (macName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createMac(macName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createMac(algorithm.getId());
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("cannot create mac: " + e.getMessage(), e);
+ }
+ }
+
+ AlgorithmParameterGenerator createAlgorithmParameterGenerator(ASN1ObjectIdentifier algorithm)
+ throws GeneralSecurityException
+ {
+ String algorithmName = (String)BASE_CIPHER_NAMES.get(algorithm);
+
+ if (algorithmName != null)
+ {
+ try
+ {
+ // this is reversed as the Sun policy files now allow unlimited strength RSA
+ return helper.createAlgorithmParameterGenerator(algorithmName);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // Ignore
+ }
+ }
+ return helper.createAlgorithmParameterGenerator(algorithm.getId());
+ }
+
+ AlgorithmParameters generateParameters(ASN1ObjectIdentifier encryptionOID, SecretKey encKey, SecureRandom rand)
+ throws CRMFException
+ {
+ try
+ {
+ AlgorithmParameterGenerator pGen = createAlgorithmParameterGenerator(encryptionOID);
+
+ if (encryptionOID.equals(CMSAlgorithm.RC2_CBC))
+ {
+ byte[] iv = new byte[8];
+
+ rand.nextBytes(iv);
+
+ try
+ {
+ pGen.init(new RC2ParameterSpec(encKey.getEncoded().length * 8, iv), rand);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw new CRMFException("parameters generation error: " + e, e);
+ }
+ }
+
+ return pGen.generateParameters();
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ return null;
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("exception creating algorithm parameter generator: " + e, e);
+ }
+ }
+
+ AlgorithmIdentifier getAlgorithmIdentifier(ASN1ObjectIdentifier encryptionOID, AlgorithmParameters params)
+ throws CRMFException
+ {
+ ASN1Encodable asn1Params;
+ if (params != null)
+ {
+ try
+ {
+ asn1Params = JcaJceUtils.extractParameters(params);
+ }
+ catch (IOException e)
+ {
+ throw new CRMFException("cannot encode parameters: " + e.getMessage(), e);
+ }
+ }
+ else
+ {
+ asn1Params = DERNull.INSTANCE;
+ }
+
+ return new AlgorithmIdentifier(
+ encryptionOID,
+ asn1Params);
+ }
+
+ static Object execute(JCECallback callback) throws CRMFException
+ {
+ try
+ {
+ return callback.doInJCE();
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new CRMFException("can't find algorithm.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CRMFException("key invalid in message.", e);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new CRMFException("can't find provider.", e);
+ }
+ catch (NoSuchPaddingException e)
+ {
+ throw new CRMFException("required padding not supported.", e);
+ }
+ catch (InvalidAlgorithmParameterException e)
+ {
+ throw new CRMFException("algorithm parameters invalid.", e);
+ }
+ catch (InvalidParameterSpecException e)
+ {
+ throw new CRMFException("MAC algorithm parameter spec invalid.", e);
+ }
+ }
+
+ static interface JCECallback
+ {
+ Object doInJCE()
+ throws CRMFException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidParameterSpecException,
+ NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessage.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessage.java
new file mode 100644
index 00000000..9b10e78e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessage.java
@@ -0,0 +1,84 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.io.IOException;
+import java.security.Provider;
+import java.security.PublicKey;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.crmf.CertReqMsg;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.crmf.CRMFException;
+import org.spongycastle.cert.crmf.CertificateRequestMessage;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+
+public class JcaCertificateRequestMessage
+ extends CertificateRequestMessage
+{
+ private CRMFHelper helper = new CRMFHelper(new DefaultJcaJceHelper());
+
+ public JcaCertificateRequestMessage(byte[] certReqMsg)
+ {
+ this(CertReqMsg.getInstance(certReqMsg));
+ }
+
+ public JcaCertificateRequestMessage(CertificateRequestMessage certReqMsg)
+ {
+ this(certReqMsg.toASN1Structure());
+ }
+
+ public JcaCertificateRequestMessage(CertReqMsg certReqMsg)
+ {
+ super(certReqMsg);
+ }
+
+ public JcaCertificateRequestMessage setProvider(String providerName)
+ {
+ this.helper = new CRMFHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ public JcaCertificateRequestMessage setProvider(Provider provider)
+ {
+ this.helper = new CRMFHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ public X500Principal getSubjectX500Principal()
+ {
+ X500Name subject = this.getCertTemplate().getSubject();
+
+ if (subject != null)
+ {
+ try
+ {
+ return new X500Principal(subject.getEncoded(ASN1Encoding.DER));
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("unable to construct DER encoding of name: " + e.getMessage());
+ }
+ }
+
+ return null;
+ }
+
+ public PublicKey getPublicKey()
+ throws CRMFException
+ {
+ SubjectPublicKeyInfo subjectPublicKeyInfo = getCertTemplate().getPublicKey();
+
+ if (subjectPublicKeyInfo != null)
+ {
+ return helper.toPublicKey(subjectPublicKeyInfo);
+ }
+
+ return null;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessageBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessageBuilder.java
new file mode 100644
index 00000000..1d9318dd
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaCertificateRequestMessageBuilder.java
@@ -0,0 +1,57 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.crmf.CertificateRequestMessageBuilder;
+
+public class JcaCertificateRequestMessageBuilder
+ extends CertificateRequestMessageBuilder
+{
+ public JcaCertificateRequestMessageBuilder(BigInteger certReqId)
+ {
+ super(certReqId);
+ }
+
+ public JcaCertificateRequestMessageBuilder setIssuer(X500Principal issuer)
+ {
+ if (issuer != null)
+ {
+ setIssuer(X500Name.getInstance(issuer.getEncoded()));
+ }
+
+ return this;
+ }
+
+ public JcaCertificateRequestMessageBuilder setSubject(X500Principal subject)
+ {
+ if (subject != null)
+ {
+ setSubject(X500Name.getInstance(subject.getEncoded()));
+ }
+
+ return this;
+ }
+
+ public JcaCertificateRequestMessageBuilder setAuthInfoSender(X500Principal sender)
+ {
+ if (sender != null)
+ {
+ setAuthInfoSender(new GeneralName(X500Name.getInstance(sender.getEncoded())));
+ }
+
+ return this;
+ }
+
+ public JcaCertificateRequestMessageBuilder setPublicKey(PublicKey publicKey)
+ {
+ setPublicKey(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+
+ return this;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
new file mode 100644
index 00000000..bed393dc
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
@@ -0,0 +1,26 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.asn1.crmf.EncryptedValue;
+import org.spongycastle.cert.crmf.CRMFException;
+import org.spongycastle.cert.crmf.EncryptedValueBuilder;
+import org.spongycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.spongycastle.operator.KeyWrapper;
+import org.spongycastle.operator.OutputEncryptor;
+
+public class JcaEncryptedValueBuilder
+ extends EncryptedValueBuilder
+{
+ public JcaEncryptedValueBuilder(KeyWrapper wrapper, OutputEncryptor encryptor)
+ {
+ super(wrapper, encryptor);
+ }
+
+ public EncryptedValue build(X509Certificate certificate)
+ throws CertificateEncodingException, CRMFException
+ {
+ return build(new JcaX509CertificateHolder(certificate));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaPKIArchiveControlBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaPKIArchiveControlBuilder.java
new file mode 100644
index 00000000..de527e07
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcaPKIArchiveControlBuilder.java
@@ -0,0 +1,29 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.security.PrivateKey;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.cert.crmf.PKIArchiveControlBuilder;
+
+public class JcaPKIArchiveControlBuilder
+ extends PKIArchiveControlBuilder
+{
+ public JcaPKIArchiveControlBuilder(PrivateKey privateKey, X500Name name)
+ {
+ this(privateKey, new GeneralName(name));
+ }
+
+ public JcaPKIArchiveControlBuilder(PrivateKey privateKey, X500Principal name)
+ {
+ this(privateKey, X500Name.getInstance(name.getEncoded()));
+ }
+
+ public JcaPKIArchiveControlBuilder(PrivateKey privateKey, GeneralName generalName)
+ {
+ super(PrivateKeyInfo.getInstance(privateKey.getEncoded()), generalName);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceAsymmetricValueDecryptorGenerator.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceAsymmetricValueDecryptorGenerator.java
new file mode 100644
index 00000000..8ad510bb
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceAsymmetricValueDecryptorGenerator.java
@@ -0,0 +1,120 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.ProviderException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.crmf.CRMFException;
+import org.spongycastle.cert.crmf.ValueDecryptorGenerator;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.InputDecryptor;
+
+public class JceAsymmetricValueDecryptorGenerator
+ implements ValueDecryptorGenerator
+{
+ private PrivateKey recipientKey;
+ private CRMFHelper helper = new CRMFHelper(new DefaultJcaJceHelper());
+
+ public JceAsymmetricValueDecryptorGenerator(PrivateKey recipientKey)
+ {
+ this.recipientKey = recipientKey;
+ }
+
+ public JceAsymmetricValueDecryptorGenerator setProvider(Provider provider)
+ {
+ this.helper = new CRMFHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ public JceAsymmetricValueDecryptorGenerator setProvider(String providerName)
+ {
+ this.helper = new CRMFHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ private Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey)
+ throws CRMFException
+ {
+ try
+ {
+ Key sKey = null;
+
+ Cipher keyCipher = helper.createCipher(keyEncryptionAlgorithm.getAlgorithm());
+
+ try
+ {
+ keyCipher.init(Cipher.UNWRAP_MODE, recipientKey);
+ sKey = keyCipher.unwrap(encryptedContentEncryptionKey, contentEncryptionAlgorithm.getAlgorithm().getId(), Cipher.SECRET_KEY);
+ }
+ catch (GeneralSecurityException e)
+ {
+ }
+ catch (IllegalStateException e)
+ {
+ }
+ catch (UnsupportedOperationException e)
+ {
+ }
+ catch (ProviderException e)
+ {
+ }
+
+ // some providers do not support UNWRAP (this appears to be only for asymmetric algorithms)
+ if (sKey == null)
+ {
+ keyCipher.init(Cipher.DECRYPT_MODE, recipientKey);
+ sKey = new SecretKeySpec(keyCipher.doFinal(encryptedContentEncryptionKey), contentEncryptionAlgorithm.getAlgorithm().getId());
+ }
+
+ return sKey;
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CRMFException("key invalid in message.", e);
+ }
+ catch (IllegalBlockSizeException e)
+ {
+ throw new CRMFException("illegal blocksize in message.", e);
+ }
+ catch (BadPaddingException e)
+ {
+ throw new CRMFException("bad padding in message.", e);
+ }
+ }
+
+ public InputDecryptor getValueDecryptor(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey)
+ throws CRMFException
+ {
+ Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentEncryptionAlgorithm, encryptedContentEncryptionKey);
+
+ final Cipher dataCipher = helper.createContentCipher(secretKey, contentEncryptionAlgorithm);
+
+ return new InputDecryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return contentEncryptionAlgorithm;
+ }
+
+ public InputStream getInputStream(InputStream dataIn)
+ {
+ return new CipherInputStream(dataIn, dataCipher);
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceCRMFEncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceCRMFEncryptorBuilder.java
new file mode 100644
index 00000000..6147184f
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JceCRMFEncryptorBuilder.java
@@ -0,0 +1,136 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.io.OutputStream;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.crmf.CRMFException;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.operator.jcajce.JceGenericKey;
+
+public class JceCRMFEncryptorBuilder
+{
+ private final ASN1ObjectIdentifier encryptionOID;
+ private final int keySize;
+
+ private CRMFHelper helper = new CRMFHelper(new DefaultJcaJceHelper());
+ private SecureRandom random;
+
+ public JceCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
+ {
+ this(encryptionOID, -1);
+ }
+
+ public JceCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
+ {
+ this.encryptionOID = encryptionOID;
+ this.keySize = keySize;
+ }
+
+ public JceCRMFEncryptorBuilder setProvider(Provider provider)
+ {
+ this.helper = new CRMFHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ public JceCRMFEncryptorBuilder setProvider(String providerName)
+ {
+ this.helper = new CRMFHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ public JceCRMFEncryptorBuilder setSecureRandom(SecureRandom random)
+ {
+ this.random = random;
+
+ return this;
+ }
+
+ public OutputEncryptor build()
+ throws CRMFException
+ {
+ return new CRMFOutputEncryptor(encryptionOID, keySize, random);
+ }
+
+ private class CRMFOutputEncryptor
+ implements OutputEncryptor
+ {
+ private SecretKey encKey;
+ private AlgorithmIdentifier algorithmIdentifier;
+ private Cipher cipher;
+
+ CRMFOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
+ throws CRMFException
+ {
+ KeyGenerator keyGen = helper.createKeyGenerator(encryptionOID);
+
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ if (keySize < 0)
+ {
+ keyGen.init(random);
+ }
+ else
+ {
+ keyGen.init(keySize, random);
+ }
+
+ cipher = helper.createCipher(encryptionOID);
+ encKey = keyGen.generateKey();
+ AlgorithmParameters params = helper.generateParameters(encryptionOID, encKey, random);
+
+ try
+ {
+ cipher.init(Cipher.ENCRYPT_MODE, encKey, params, random);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("unable to initialize cipher: " + e.getMessage(), e);
+ }
+
+ //
+ // If params are null we try and second guess on them as some providers don't provide
+ // algorithm parameter generation explicity but instead generate them under the hood.
+ //
+ if (params == null)
+ {
+ params = cipher.getParameters();
+ }
+
+ algorithmIdentifier = helper.getAlgorithmIdentifier(encryptionOID, params);
+ }
+
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return algorithmIdentifier;
+ }
+
+ public OutputStream getOutputStream(OutputStream dOut)
+ {
+ return new CipherOutputStream(dOut, cipher);
+ }
+
+ public GenericKey getKey()
+ {
+ return new JceGenericKey(algorithmIdentifier, encKey);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcePKMACValuesCalculator.java b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcePKMACValuesCalculator.java
new file mode 100644
index 00000000..ceaf78cb
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/crmf/jcajce/JcePKMACValuesCalculator.java
@@ -0,0 +1,69 @@
+package org.spongycastle.cert.crmf.jcajce;
+
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.Provider;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.cert.crmf.CRMFException;
+import org.spongycastle.cert.crmf.PKMACValuesCalculator;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+
+public class JcePKMACValuesCalculator
+ implements PKMACValuesCalculator
+{
+ private MessageDigest digest;
+ private Mac mac;
+ private CRMFHelper helper;
+
+ public JcePKMACValuesCalculator()
+ {
+ this.helper = new CRMFHelper(new DefaultJcaJceHelper());
+ }
+
+ public JcePKMACValuesCalculator setProvider(Provider provider)
+ {
+ this.helper = new CRMFHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ public JcePKMACValuesCalculator setProvider(String providerName)
+ {
+ this.helper = new CRMFHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ public void setup(AlgorithmIdentifier digAlg, AlgorithmIdentifier macAlg)
+ throws CRMFException
+ {
+ digest = helper.createDigest(digAlg.getAlgorithm());
+ mac = helper.createMac(macAlg.getAlgorithm());
+ }
+
+ public byte[] calculateDigest(byte[] data)
+ {
+ return digest.digest(data);
+ }
+
+ public byte[] calculateMac(byte[] pwd, byte[] data)
+ throws CRMFException
+ {
+ try
+ {
+ mac.init(new SecretKeySpec(pwd, mac.getAlgorithm()));
+
+ return mac.doFinal(data);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CRMFException("failure in setup: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/CertHelper.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/CertHelper.java
new file mode 100644
index 00000000..1c5679bf
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/CertHelper.java
@@ -0,0 +1,17 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+abstract class CertHelper
+{
+ public CertificateFactory getCertificateFactory(String type)
+ throws NoSuchProviderException, CertificateException
+ {
+ return createCertificateFactory(type);
+ }
+
+ protected abstract CertificateFactory createCertificateFactory(String type)
+ throws CertificateException, NoSuchProviderException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/DefaultCertHelper.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/DefaultCertHelper.java
new file mode 100644
index 00000000..d8713bf3
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/DefaultCertHelper.java
@@ -0,0 +1,14 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+class DefaultCertHelper
+ extends CertHelper
+{
+ protected CertificateFactory createCertificateFactory(String type)
+ throws CertificateException
+ {
+ return CertificateFactory.getInstance(type);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttrCertStore.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttrCertStore.java
new file mode 100644
index 00000000..ed354335
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttrCertStore.java
@@ -0,0 +1,62 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.util.CollectionStore;
+import org.spongycastle.x509.X509AttributeCertificate;
+
+/**
+ * Class for storing Attribute Certificates for later lookup.
+ * <p>
+ * The class will convert X509AttributeCertificate objects into X509AttributeCertificateHolder objects.
+ * </p>
+ */
+public class JcaAttrCertStore
+ extends CollectionStore
+{
+ /**
+ * Basic constructor.
+ *
+ * @param collection - initial contents for the store, this is copied.
+ */
+ public JcaAttrCertStore(Collection collection)
+ throws IOException
+ {
+ super(convertCerts(collection));
+ }
+
+ public JcaAttrCertStore(X509AttributeCertificate attrCert)
+ throws IOException
+ {
+ this(Collections.singletonList(attrCert));
+ }
+
+ private static Collection convertCerts(Collection collection)
+ throws IOException
+ {
+ List list = new ArrayList(collection.size());
+
+ for (Iterator it = collection.iterator(); it.hasNext();)
+ {
+ Object o = it.next();
+
+ if (o instanceof X509AttributeCertificate)
+ {
+ X509AttributeCertificate cert = (X509AttributeCertificate)o;
+
+ list.add(new JcaX509AttributeCertificateHolder(cert));
+ }
+ else
+ {
+ list.add(o);
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttributeCertificateIssuer.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttributeCertificateIssuer.java
new file mode 100644
index 00000000..54ee46ee
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaAttributeCertificateIssuer.java
@@ -0,0 +1,32 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.AttributeCertificateIssuer;
+
+public class JcaAttributeCertificateIssuer
+ extends AttributeCertificateIssuer
+{
+ /**
+ * Base constructor.
+ *
+ * @param issuerCert certificate for the issuer of the attribute certificate.
+ */
+ public JcaAttributeCertificateIssuer(X509Certificate issuerCert)
+ {
+ this(issuerCert.getIssuerX500Principal());
+ }
+
+ /**
+ * Base constructor.
+ *
+ * @param issuerDN X.500 DN for the issuer of the attribute certificate.
+ */
+ public JcaAttributeCertificateIssuer(X500Principal issuerDN)
+ {
+ super(X500Name.getInstance(issuerDN.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCRLStore.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCRLStore.java
new file mode 100644
index 00000000..08493d13
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCRLStore.java
@@ -0,0 +1,63 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.IOException;
+import java.security.cert.CRLException;
+import java.security.cert.X509CRL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.cert.X509CRLHolder;
+import org.spongycastle.util.CollectionStore;
+
+/**
+ * Class for storing CRLs for later lookup.
+ * <p>
+ * The class will convert X509CRL objects into X509CRLHolder objects.
+ * </p>
+ */
+public class JcaCRLStore
+ extends CollectionStore
+{
+ /**
+ * Basic constructor.
+ *
+ * @param collection - initial contents for the store, this is copied.
+ */
+ public JcaCRLStore(Collection collection)
+ throws CRLException
+ {
+ super(convertCRLs(collection));
+ }
+
+ private static Collection convertCRLs(Collection collection)
+ throws CRLException
+ {
+ List list = new ArrayList(collection.size());
+
+ for (Iterator it = collection.iterator(); it.hasNext();)
+ {
+ Object crl = it.next();
+
+ if (crl instanceof X509CRL)
+ {
+ try
+ {
+ list.add(new X509CRLHolder(((X509CRL)crl).getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new CRLException("cannot read encoding: " + e.getMessage());
+
+ }
+ }
+ else
+ {
+ list.add((X509CRLHolder)crl);
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStore.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStore.java
new file mode 100644
index 00000000..49766814
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStore.java
@@ -0,0 +1,64 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.util.CollectionStore;
+
+/**
+ * Class for storing Certificates for later lookup.
+ * <p>
+ * The class will convert X509Certificate objects into X509CertificateHolder objects.
+ * </p>
+ */
+public class JcaCertStore
+ extends CollectionStore
+{
+ /**
+ * Basic constructor.
+ *
+ * @param collection - initial contents for the store, this is copied.
+ */
+ public JcaCertStore(Collection collection)
+ throws CertificateEncodingException
+ {
+ super(convertCerts(collection));
+ }
+
+ private static Collection convertCerts(Collection collection)
+ throws CertificateEncodingException
+ {
+ List list = new ArrayList(collection.size());
+
+ for (Iterator it = collection.iterator(); it.hasNext();)
+ {
+ Object o = it.next();
+
+ if (o instanceof X509Certificate)
+ {
+ X509Certificate cert = (X509Certificate)o;
+
+ try
+ {
+ list.add(new X509CertificateHolder(cert.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new CertificateEncodingException("unable to read encoding: " + e.getMessage());
+ }
+ }
+ else
+ {
+ list.add((X509CertificateHolder)o);
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStoreBuilder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStoreBuilder.java
new file mode 100644
index 00000000..fbb26cb4
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaCertStoreBuilder.java
@@ -0,0 +1,148 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.security.cert.CRLException;
+import java.security.cert.CertStore;
+import java.security.cert.CertificateException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.cert.X509CRLHolder;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.util.Store;
+
+/**
+ * Builder to create a CertStore from certificate and CRL stores.
+ */
+public class JcaCertStoreBuilder
+{
+ private List certs = new ArrayList();
+ private List crls = new ArrayList();
+ private Object provider;
+ private JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
+ private JcaX509CRLConverter crlConverter = new JcaX509CRLConverter();
+ private String type = "Collection";
+
+ /**
+ * Add a store full of X509CertificateHolder objects.
+ *
+ * @param certStore a store of X509CertificateHolder objects.
+ */
+ public JcaCertStoreBuilder addCertificates(Store certStore)
+ {
+ certs.addAll(certStore.getMatches(null));
+
+ return this;
+ }
+
+ /**
+ * Add a single certificate.
+ *
+ * @param cert the X509 certificate holder containing the certificate.
+ */
+ public JcaCertStoreBuilder addCertificate(X509CertificateHolder cert)
+ {
+ certs.add(cert);
+
+ return this;
+ }
+
+ /**
+ * Add a store full of X509CRLHolder objects.
+ * @param crlStore a store of X509CRLHolder objects.
+ */
+ public JcaCertStoreBuilder addCRLs(Store crlStore)
+ {
+ crls.addAll(crlStore.getMatches(null));
+
+ return this;
+ }
+
+ /**
+ * Add a single CRL.
+ *
+ * @param crl the X509 CRL holder containing the CRL.
+ */
+ public JcaCertStoreBuilder addCRL(X509CRLHolder crl)
+ {
+ crls.add(crl);
+
+ return this;
+ }
+
+ public JcaCertStoreBuilder setProvider(String providerName)
+ {
+ certificateConverter.setProvider(providerName);
+ crlConverter.setProvider(providerName);
+ this.provider = providerName;
+
+ return this;
+ }
+
+ public JcaCertStoreBuilder setProvider(Provider provider)
+ {
+ certificateConverter.setProvider(provider);
+ crlConverter.setProvider(provider);
+ this.provider = provider;
+
+ return this;
+ }
+
+ /**
+ * Set the type of the CertStore generated. By default it is "Collection".
+ *
+ * @param type type of CertStore passed to CertStore.getInstance().
+ * @return the current builder.
+ */
+ public JcaCertStoreBuilder setType(String type)
+ {
+ this.type = type;
+
+ return this;
+ }
+
+ /**
+ * Build the CertStore from the current inputs.
+ *
+ * @return a CertStore.
+ * @throws GeneralSecurityException
+ */
+ public CertStore build()
+ throws GeneralSecurityException
+ {
+ CollectionCertStoreParameters params = convertHolders(certificateConverter, crlConverter);
+
+ if (provider instanceof String)
+ {
+ return CertStore.getInstance(type, params, (String)provider);
+ }
+
+ if (provider instanceof Provider)
+ {
+ return CertStore.getInstance(type, params, (Provider)provider);
+ }
+
+ return CertStore.getInstance(type, params);
+ }
+
+ private CollectionCertStoreParameters convertHolders(JcaX509CertificateConverter certificateConverter, JcaX509CRLConverter crlConverter)
+ throws CertificateException, CRLException
+ {
+ List jcaObjs = new ArrayList(certs.size() + crls.size());
+
+ for (Iterator it = certs.iterator(); it.hasNext();)
+ {
+ jcaObjs.add(certificateConverter.getCertificate((X509CertificateHolder)it.next()));
+ }
+
+ for (Iterator it = crls.iterator(); it.hasNext();)
+ {
+ jcaObjs.add(crlConverter.getCRL((X509CRLHolder)it.next()));
+ }
+
+ return new CollectionCertStoreParameters(jcaObjs);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX500NameUtil.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX500NameUtil.java
new file mode 100644
index 00000000..d6b6ae89
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX500NameUtil.java
@@ -0,0 +1,29 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x500.X500NameStyle;
+
+public class JcaX500NameUtil
+{
+ public static X500Name getIssuer(X509Certificate certificate)
+ {
+ return X500Name.getInstance(certificate.getIssuerX500Principal().getEncoded());
+ }
+
+ public static X500Name getSubject(X509Certificate certificate)
+ {
+ return X500Name.getInstance(certificate.getSubjectX500Principal().getEncoded());
+ }
+
+ public static X500Name getIssuer(X500NameStyle style, X509Certificate certificate)
+ {
+ return X500Name.getInstance(style, certificate.getIssuerX500Principal().getEncoded());
+ }
+
+ public static X500Name getSubject(X500NameStyle style, X509Certificate certificate)
+ {
+ return X500Name.getInstance(style, certificate.getSubjectX500Principal().getEncoded());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509AttributeCertificateHolder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509AttributeCertificateHolder.java
new file mode 100644
index 00000000..35076252
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509AttributeCertificateHolder.java
@@ -0,0 +1,26 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.x509.AttributeCertificate;
+import org.spongycastle.cert.X509AttributeCertificateHolder;
+import org.spongycastle.x509.X509AttributeCertificate;
+
+/**
+ * JCA helper class for converting an old style X509AttributeCertificate into a X509AttributeCertificateHolder object.
+ */
+public class JcaX509AttributeCertificateHolder
+ extends X509AttributeCertificateHolder
+{
+ /**
+ * Base constructor.
+ *
+ * @param cert AttributeCertificate to be used a the source for the holder creation.
+ * @throws IOException if there is a problem extracting the attribute certificate information.
+ */
+ public JcaX509AttributeCertificateHolder(X509AttributeCertificate cert)
+ throws IOException
+ {
+ super(AttributeCertificate.getInstance(cert.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLConverter.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLConverter.java
new file mode 100644
index 00000000..7040f1aa
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLConverter.java
@@ -0,0 +1,103 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.cert.CRLException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509CRL;
+
+import org.spongycastle.cert.X509CRLHolder;
+
+/**
+ * Class for converting an X509CRLHolder into a corresponding X509CRL object tied to a
+ * particular JCA provider.
+ */
+public class JcaX509CRLConverter
+{
+ private CertHelper helper = new DefaultCertHelper();
+
+ /**
+ * Base constructor, configure with the default provider.
+ */
+ public JcaX509CRLConverter()
+ {
+ this.helper = new DefaultCertHelper();
+ }
+
+ /**
+ * Set the provider to use from a Provider object.
+ *
+ * @param provider the provider to use.
+ * @return the converter instance.
+ */
+ public JcaX509CRLConverter setProvider(Provider provider)
+ {
+ this.helper = new ProviderCertHelper(provider);
+
+ return this;
+ }
+
+ /**
+ * Set the provider to use by name.
+ *
+ * @param providerName name of the provider to use.
+ * @return the converter instance.
+ */
+ public JcaX509CRLConverter setProvider(String providerName)
+ {
+ this.helper = new NamedCertHelper(providerName);
+
+ return this;
+ }
+
+ /**
+ * Use the configured converter to produce a X509CRL object from a X509CRLHolder object.
+ *
+ * @param crlHolder the holder to be converted
+ * @return a X509CRL object
+ * @throws CRLException if the conversion is unable to be made.
+ */
+ public X509CRL getCRL(X509CRLHolder crlHolder)
+ throws CRLException
+ {
+ try
+ {
+ CertificateFactory cFact = helper.getCertificateFactory("X.509");
+
+ return (X509CRL)cFact.generateCRL(new ByteArrayInputStream(crlHolder.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new ExCRLException("exception parsing certificate: " + e.getMessage(), e);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new ExCRLException("cannot find required provider:" + e.getMessage(), e);
+ }
+ catch (CertificateException e)
+ {
+ throw new ExCRLException("cannot create factory: " + e.getMessage(), e);
+ }
+ }
+
+ private class ExCRLException
+ extends CRLException
+ {
+ private Throwable cause;
+
+ public ExCRLException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLHolder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLHolder.java
new file mode 100644
index 00000000..91bcd88d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CRLHolder.java
@@ -0,0 +1,26 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.cert.CRLException;
+import java.security.cert.X509CRL;
+
+import org.spongycastle.asn1.x509.CertificateList;
+import org.spongycastle.cert.X509CRLHolder;
+
+/**
+ * JCA helper class for converting an X509CRL into a X509CRLHolder object.
+ */
+public class JcaX509CRLHolder
+ extends X509CRLHolder
+{
+ /**
+ * Base constructor.
+ *
+ * @param crl CRL to be used a the source for the holder creation.
+ * @throws CRLException if there is a problem extracting the CRL information.
+ */
+ public JcaX509CRLHolder(X509CRL crl)
+ throws CRLException
+ {
+ super(CertificateList.getInstance(crl.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateConverter.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateConverter.java
new file mode 100644
index 00000000..5e46a17d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateConverter.java
@@ -0,0 +1,116 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.cert.X509CertificateHolder;
+
+/**
+ * Converter for producing X509Certificate objects tied to a specific provider from X509CertificateHolder objects.
+ */
+public class JcaX509CertificateConverter
+{
+ private CertHelper helper = new DefaultCertHelper();
+
+ /**
+ * Base constructor, configure with the default provider.
+ */
+ public JcaX509CertificateConverter()
+ {
+ this.helper = new DefaultCertHelper();
+ }
+
+ /**
+ * Set the provider to use from a Provider object.
+ *
+ * @param provider the provider to use.
+ * @return the converter instance.
+ */
+ public JcaX509CertificateConverter setProvider(Provider provider)
+ {
+ this.helper = new ProviderCertHelper(provider);
+
+ return this;
+ }
+
+ /**
+ * Set the provider to use by name.
+ *
+ * @param providerName name of the provider to use.
+ * @return the converter instance.
+ */
+ public JcaX509CertificateConverter setProvider(String providerName)
+ {
+ this.helper = new NamedCertHelper(providerName);
+
+ return this;
+ }
+
+ /**
+ * Use the configured converter to produce a X509Certificate object from a X509CertificateHolder object.
+ *
+ * @param certHolder the holder to be converted
+ * @return a X509Certificate object
+ * @throws CertificateException if the conversion is unable to be made.
+ */
+ public X509Certificate getCertificate(X509CertificateHolder certHolder)
+ throws CertificateException
+ {
+ try
+ {
+ CertificateFactory cFact = helper.getCertificateFactory("X.509");
+
+ return (X509Certificate)cFact.generateCertificate(new ByteArrayInputStream(certHolder.getEncoded()));
+ }
+ catch (IOException e)
+ {
+ throw new ExCertificateParsingException("exception parsing certificate: " + e.getMessage(), e);
+ }
+ catch (NoSuchProviderException e)
+ {
+ throw new ExCertificateException("cannot find required provider:" + e.getMessage(), e);
+ }
+ }
+
+ private class ExCertificateParsingException
+ extends CertificateParsingException
+ {
+ private Throwable cause;
+
+ public ExCertificateParsingException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+ }
+
+ private class ExCertificateException
+ extends CertificateException
+ {
+ private Throwable cause;
+
+ public ExCertificateException(String msg, Throwable cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateHolder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateHolder.java
new file mode 100644
index 00000000..a523f975
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509CertificateHolder.java
@@ -0,0 +1,26 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.cert.X509CertificateHolder;
+
+/**
+ * JCA helper class for converting an X509Certificate into a X509CertificateHolder object.
+ */
+public class JcaX509CertificateHolder
+ extends X509CertificateHolder
+{
+ /**
+ * Base constructor.
+ *
+ * @param cert certificate to be used a the source for the holder creation.
+ * @throws CertificateEncodingException if there is a problem extracting the certificate information.
+ */
+ public JcaX509CertificateHolder(X509Certificate cert)
+ throws CertificateEncodingException
+ {
+ super(Certificate.getInstance(cert.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java
new file mode 100644
index 00000000..46eb3b43
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java
@@ -0,0 +1,50 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.Provider;
+import java.security.cert.CertificateException;
+
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.X509ContentVerifierProviderBuilder;
+import org.spongycastle.operator.ContentVerifierProvider;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
+
+public class JcaX509ContentVerifierProviderBuilder
+ implements X509ContentVerifierProviderBuilder
+{
+ private JcaContentVerifierProviderBuilder builder = new JcaContentVerifierProviderBuilder();
+
+ public JcaX509ContentVerifierProviderBuilder setProvider(Provider provider)
+ {
+ this.builder.setProvider(provider);
+
+ return this;
+ }
+
+ public JcaX509ContentVerifierProviderBuilder setProvider(String providerName)
+ {
+ this.builder.setProvider(providerName);
+
+ return this;
+ }
+
+ public ContentVerifierProvider build(SubjectPublicKeyInfo validatingKeyInfo)
+ throws OperatorCreationException
+ {
+ return builder.build(validatingKeyInfo);
+ }
+
+ public ContentVerifierProvider build(X509CertificateHolder validatingKeyInfo)
+ throws OperatorCreationException
+ {
+ try
+ {
+ return builder.build(validatingKeyInfo);
+ }
+ catch (CertificateException e)
+ {
+ throw new OperatorCreationException("Unable to process certificate: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ExtensionUtils.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ExtensionUtils.java
new file mode 100644
index 00000000..b601e24e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509ExtensionUtils.java
@@ -0,0 +1,145 @@
+package org.spongycastle.cert.jcajce;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.X509ExtensionUtils;
+import org.spongycastle.operator.DigestCalculator;
+
+public class JcaX509ExtensionUtils
+ extends X509ExtensionUtils
+{
+ /**
+ * Create a utility class pre-configured with a SHA-1 digest calculator based on the
+ * default implementation.
+ *
+ * @throws NoSuchAlgorithmException
+ */
+ public JcaX509ExtensionUtils()
+ throws NoSuchAlgorithmException
+ {
+ super(new SHA1DigestCalculator(MessageDigest.getInstance("SHA1")));
+ }
+
+ public JcaX509ExtensionUtils(DigestCalculator calculator)
+ {
+ super(calculator);
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(
+ X509Certificate cert)
+ throws CertificateEncodingException
+ {
+ return super.createAuthorityKeyIdentifier(new JcaX509CertificateHolder(cert));
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(
+ PublicKey pubKey)
+ {
+ return super.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()));
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(PublicKey pubKey, X500Principal name, BigInteger serial)
+ {
+ return super.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()), new GeneralNames(new GeneralName(X500Name.getInstance(name.getEncoded()))), serial);
+ }
+
+ public AuthorityKeyIdentifier createAuthorityKeyIdentifier(PublicKey pubKey, GeneralNames generalNames, BigInteger serial)
+ {
+ return super.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()), generalNames, serial);
+ }
+
+ /**
+ * Return a RFC 3280 type 1 key identifier. As in:
+ * <pre>
+ * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
+ * value of the BIT STRING subjectPublicKey (excluding the tag,
+ * length, and number of unused bits).
+ * </pre>
+ * @param publicKey the key object containing the key identifier is to be based on.
+ * @return the key identifier.
+ */
+ public SubjectKeyIdentifier createSubjectKeyIdentifier(
+ PublicKey publicKey)
+ {
+ return super.createSubjectKeyIdentifier(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Return a RFC 3280 type 2 key identifier. As in:
+ * <pre>
+ * (2) The keyIdentifier is composed of a four bit type field with
+ * the value 0100 followed by the least significant 60 bits of the
+ * SHA-1 hash of the value of the BIT STRING subjectPublicKey.
+ * </pre>
+ * @param publicKey the key object of interest.
+ * @return the key identifier.
+ */
+ public SubjectKeyIdentifier createTruncatedSubjectKeyIdentifier(PublicKey publicKey)
+ {
+ return super.createSubjectKeyIdentifier(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Return the ASN.1 object contained in a byte[] returned by a getExtensionValue() call.
+ *
+ * @param encExtValue DER encoded OCTET STRING containing the DER encoded extension object.
+ * @return an ASN.1 object
+ * @throws java.io.IOException on a parsing error.
+ */
+ public static ASN1Primitive parseExtensionValue(byte[] encExtValue)
+ throws IOException
+ {
+ return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(encExtValue).getOctets());
+ }
+
+ private static class SHA1DigestCalculator
+ implements DigestCalculator
+ {
+ private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ private MessageDigest digest;
+
+ public SHA1DigestCalculator(MessageDigest digest)
+ {
+ this.digest = digest;
+ }
+
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1);
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return bOut;
+ }
+
+ public byte[] getDigest()
+ {
+ byte[] bytes = digest.digest(bOut.toByteArray());
+
+ bOut.reset();
+
+ return bytes;
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v1CertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v1CertificateBuilder.java
new file mode 100644
index 00000000..e5e4f83d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v1CertificateBuilder.java
@@ -0,0 +1,48 @@
+package org.spongycastle.cert.jcajce;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.X509v1CertificateBuilder;
+
+/**
+ * JCA helper class to allow JCA objects to be used in the construction of a Version 1 certificate.
+ */
+public class JcaX509v1CertificateBuilder
+ extends X509v1CertificateBuilder
+{
+ /**
+ * Initialise the builder using a PublicKey.
+ *
+ * @param issuer X500Name representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject X500Name representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v1CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, PublicKey publicKey)
+ {
+ super(issuer, serial, notBefore, notAfter, subject, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Initialise the builder using X500Principal objects and a PublicKey.
+ *
+ * @param issuer principal representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject principal representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v1CertificateBuilder(X500Principal issuer, BigInteger serial, Date notBefore, Date notAfter, X500Principal subject, PublicKey publicKey)
+ {
+ super(X500Name.getInstance(issuer.getEncoded()), serial, notBefore, notAfter, X500Name.getInstance(subject.getEncoded()), SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v2CRLBuilder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v2CRLBuilder.java
new file mode 100644
index 00000000..97d544bd
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v2CRLBuilder.java
@@ -0,0 +1,23 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.X509v2CRLBuilder;
+
+public class JcaX509v2CRLBuilder
+ extends X509v2CRLBuilder
+{
+ public JcaX509v2CRLBuilder(X500Principal issuer, Date now)
+ {
+ super(X500Name.getInstance(issuer.getEncoded()), now);
+ }
+
+ public JcaX509v2CRLBuilder(X509Certificate issuerCert, Date now)
+ {
+ this(issuerCert.getSubjectX500Principal(), now);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v3CertificateBuilder.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v3CertificateBuilder.java
new file mode 100644
index 00000000..238edbe8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/JcaX509v3CertificateBuilder.java
@@ -0,0 +1,119 @@
+package org.spongycastle.cert.jcajce;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x509.Time;
+import org.spongycastle.cert.X509v3CertificateBuilder;
+
+/**
+ * JCA helper class to allow JCA objects to be used in the construction of a Version 3 certificate.
+ */
+public class JcaX509v3CertificateBuilder
+ extends X509v3CertificateBuilder
+{
+ /**
+ * Initialise the builder using a PublicKey.
+ *
+ * @param issuer X500Name representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject X500Name representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v3CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, PublicKey publicKey)
+ {
+ super(issuer, serial, notBefore, notAfter, subject, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Initialise the builder using a PublicKey.
+ *
+ * @param issuer X500Name representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore Time before which the certificate is not valid.
+ * @param notAfter Time after which the certificate is not valid.
+ * @param subject X500Name representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v3CertificateBuilder(X500Name issuer, BigInteger serial, Time notBefore, Time notAfter, X500Name subject, PublicKey publicKey)
+ {
+ super(issuer, serial, notBefore, notAfter, subject, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Initialise the builder using X500Principal objects and a PublicKey.
+ *
+ * @param issuer principal representing the issuer of this certificate.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject principal representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v3CertificateBuilder(X500Principal issuer, BigInteger serial, Date notBefore, Date notAfter, X500Principal subject, PublicKey publicKey)
+ {
+ super(X500Name.getInstance(issuer.getEncoded()), serial, notBefore, notAfter, X500Name.getInstance(subject.getEncoded()), SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+ }
+
+ /**
+ * Initialise the builder using the subject from the passed in issuerCert as the issuer, as well as
+ * passing through and converting the other objects provided.
+ *
+ * @param issuerCert certificate who's subject is the issuer of the certificate we are building.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject principal representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v3CertificateBuilder(X509Certificate issuerCert, BigInteger serial, Date notBefore, Date notAfter, X500Principal subject, PublicKey publicKey)
+ {
+ this(issuerCert.getSubjectX500Principal(), serial, notBefore, notAfter, subject, publicKey);
+ }
+
+ /**
+ * Initialise the builder using the subject from the passed in issuerCert as the issuer, as well as
+ * passing through and converting the other objects provided.
+ *
+ * @param issuerCert certificate who's subject is the issuer of the certificate we are building.
+ * @param serial the serial number for the certificate.
+ * @param notBefore date before which the certificate is not valid.
+ * @param notAfter date after which the certificate is not valid.
+ * @param subject principal representing the subject of this certificate.
+ * @param publicKey the public key to be associated with the certificate.
+ */
+ public JcaX509v3CertificateBuilder(X509Certificate issuerCert, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, PublicKey publicKey)
+ {
+ this(X500Name.getInstance(issuerCert.getSubjectX500Principal().getEncoded()), serial, notBefore, notAfter, subject, publicKey);
+ }
+
+ /**
+ * Add a given extension field for the standard extensions tag (tag 3)
+ * copying the extension value from another certificate.
+ *
+ * @param oid the type of the extension to be copied.
+ * @param critical true if the extension is to be marked critical, false otherwise.
+ * @param certificate the source of the extension to be copied.
+ * @return the builder instance.
+ */
+ public JcaX509v3CertificateBuilder copyAndAddExtension(
+ ASN1ObjectIdentifier oid,
+ boolean critical,
+ X509Certificate certificate)
+ throws CertificateEncodingException
+ {
+ this.copyAndAddExtension(oid, critical, new JcaX509CertificateHolder(certificate));
+
+ return this;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/NamedCertHelper.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/NamedCertHelper.java
new file mode 100644
index 00000000..89584138
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/NamedCertHelper.java
@@ -0,0 +1,22 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+class NamedCertHelper
+ extends CertHelper
+{
+ private final String providerName;
+
+ NamedCertHelper(String providerName)
+ {
+ this.providerName = providerName;
+ }
+
+ protected CertificateFactory createCertificateFactory(String type)
+ throws CertificateException, NoSuchProviderException
+ {
+ return CertificateFactory.getInstance(type, providerName);
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/jcajce/ProviderCertHelper.java b/pkix/src/main/java/org/spongycastle/cert/jcajce/ProviderCertHelper.java
new file mode 100644
index 00000000..ffe37e9b
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/jcajce/ProviderCertHelper.java
@@ -0,0 +1,22 @@
+package org.spongycastle.cert.jcajce;
+
+import java.security.Provider;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+class ProviderCertHelper
+ extends CertHelper
+{
+ private final Provider provider;
+
+ ProviderCertHelper(Provider provider)
+ {
+ this.provider = provider;
+ }
+
+ protected CertificateFactory createCertificateFactory(String type)
+ throws CertificateException
+ {
+ return CertificateFactory.getInstance(type, provider);
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPResp.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPResp.java
new file mode 100644
index 00000000..f3c65670
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPResp.java
@@ -0,0 +1,212 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ocsp.BasicOCSPResponse;
+import org.spongycastle.asn1.ocsp.ResponseData;
+import org.spongycastle.asn1.ocsp.SingleResponse;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+
+/**
+ * <pre>
+ * BasicOCSPResponse ::= SEQUENCE {
+ * tbsResponseData ResponseData,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ * </pre>
+ */
+public class BasicOCSPResp
+{
+ private BasicOCSPResponse resp;
+ private ResponseData data;
+ private Extensions extensions;
+
+ public BasicOCSPResp(
+ BasicOCSPResponse resp)
+ {
+ this.resp = resp;
+ this.data = resp.getTbsResponseData();
+ this.extensions = Extensions.getInstance(resp.getTbsResponseData().getResponseExtensions());
+ }
+
+ /**
+ * Return the DER encoding of the tbsResponseData field.
+ * @return DER encoding of tbsResponseData
+ */
+ public byte[] getTBSResponseData()
+ {
+ try
+ {
+ return resp.getTbsResponseData().getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+
+ public int getVersion()
+ {
+ return data.getVersion().getValue().intValue() + 1;
+ }
+
+ public RespID getResponderId()
+ {
+ return new RespID(data.getResponderID());
+ }
+
+ public Date getProducedAt()
+ {
+ return OCSPUtils.extractDate(data.getProducedAt());
+ }
+
+ public SingleResp[] getResponses()
+ {
+ ASN1Sequence s = data.getResponses();
+ SingleResp[] rs = new SingleResp[s.size()];
+
+ for (int i = 0; i != rs.length; i++)
+ {
+ rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i)));
+ }
+
+ return rs;
+ }
+
+ public boolean hasExtensions()
+ {
+ return extensions != null;
+ }
+
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ public List getExtensionOIDs()
+ {
+ return OCSPUtils.getExtensionOIDs(extensions);
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return OCSPUtils.getCriticalExtensionOIDs(extensions);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
+ }
+
+
+ public ASN1ObjectIdentifier getSignatureAlgOID()
+ {
+ return resp.getSignatureAlgorithm().getAlgorithm();
+ }
+
+ public byte[] getSignature()
+ {
+ return resp.getSignature().getBytes();
+ }
+
+ public X509CertificateHolder[] getCerts()
+ {
+ //
+ // load the certificates if we have any
+ //
+ if (resp.getCerts() != null)
+ {
+ ASN1Sequence s = resp.getCerts();
+
+ if (s != null)
+ {
+ X509CertificateHolder[] certs = new X509CertificateHolder[s.size()];
+
+ for (int i = 0; i != certs.length; i++)
+ {
+ certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i)));
+ }
+
+ return certs;
+ }
+
+ return OCSPUtils.EMPTY_CERTS;
+ }
+ else
+ {
+ return OCSPUtils.EMPTY_CERTS;
+ }
+ }
+
+ /**
+ * verify the signature against the tbsResponseData object we contain.
+ */
+ public boolean isSignatureValid(
+ ContentVerifierProvider verifierProvider)
+ throws OCSPException
+ {
+ try
+ {
+ ContentVerifier verifier = verifierProvider.get(resp.getSignatureAlgorithm());
+ OutputStream vOut = verifier.getOutputStream();
+
+ vOut.write(resp.getTbsResponseData().getEncoded(ASN1Encoding.DER));
+ vOut.close();
+
+ return verifier.verify(this.getSignature());
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("exception processing sig: " + e, e);
+ }
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return resp.getEncoded();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof BasicOCSPResp))
+ {
+ return false;
+ }
+
+ BasicOCSPResp r = (BasicOCSPResp)o;
+
+ return resp.equals(r.resp);
+ }
+
+ public int hashCode()
+ {
+ return resp.hashCode();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPRespBuilder.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPRespBuilder.java
new file mode 100644
index 00000000..ac759de6
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/BasicOCSPRespBuilder.java
@@ -0,0 +1,264 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERGeneralizedTime;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.ocsp.BasicOCSPResponse;
+import org.spongycastle.asn1.ocsp.CertStatus;
+import org.spongycastle.asn1.ocsp.ResponseData;
+import org.spongycastle.asn1.ocsp.RevokedInfo;
+import org.spongycastle.asn1.ocsp.SingleResponse;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.CRLReason;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.ContentSigner;
+import org.spongycastle.operator.DigestCalculator;
+
+/**
+ * Generator for basic OCSP response objects.
+ */
+public class BasicOCSPRespBuilder
+{
+ private List list = new ArrayList();
+ private Extensions responseExtensions = null;
+ private RespID responderID;
+
+ private class ResponseObject
+ {
+ CertificateID certId;
+ CertStatus certStatus;
+ ASN1GeneralizedTime thisUpdate;
+ ASN1GeneralizedTime nextUpdate;
+ Extensions extensions;
+
+ public ResponseObject(
+ CertificateID certId,
+ CertificateStatus certStatus,
+ Date thisUpdate,
+ Date nextUpdate,
+ Extensions extensions)
+ {
+ this.certId = certId;
+
+ if (certStatus == null)
+ {
+ this.certStatus = new CertStatus();
+ }
+ else if (certStatus instanceof UnknownStatus)
+ {
+ this.certStatus = new CertStatus(2, DERNull.INSTANCE);
+ }
+ else
+ {
+ RevokedStatus rs = (RevokedStatus)certStatus;
+
+ if (rs.hasRevocationReason())
+ {
+ this.certStatus = new CertStatus(
+ new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), CRLReason.lookup(rs.getRevocationReason())));
+ }
+ else
+ {
+ this.certStatus = new CertStatus(
+ new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), null));
+ }
+ }
+
+ this.thisUpdate = new DERGeneralizedTime(thisUpdate);
+
+ if (nextUpdate != null)
+ {
+ this.nextUpdate = new DERGeneralizedTime(nextUpdate);
+ }
+ else
+ {
+ this.nextUpdate = null;
+ }
+
+ this.extensions = extensions;
+ }
+
+ public SingleResponse toResponse()
+ throws Exception
+ {
+ return new SingleResponse(certId.toASN1Object(), certStatus, thisUpdate, nextUpdate, extensions);
+ }
+ }
+
+ /**
+ * basic constructor
+ */
+ public BasicOCSPRespBuilder(
+ RespID responderID)
+ {
+ this.responderID = responderID;
+ }
+
+ /**
+ * construct with the responderID to be the SHA-1 keyHash of the passed in public key.
+ *
+ * @param key the key info of the responder public key.
+ * @param digCalc a SHA-1 digest calculator
+ */
+ public BasicOCSPRespBuilder(
+ SubjectPublicKeyInfo key,
+ DigestCalculator digCalc)
+ throws OCSPException
+ {
+ this.responderID = new RespID(key, digCalc);
+ }
+
+ /**
+ * Add a response for a particular Certificate ID.
+ *
+ * @param certID certificate ID details
+ * @param certStatus status of the certificate - null if okay
+ */
+ public BasicOCSPRespBuilder addResponse(
+ CertificateID certID,
+ CertificateStatus certStatus)
+ {
+ list.add(new ResponseObject(certID, certStatus, new Date(), null, null));
+
+ return this;
+ }
+
+ /**
+ * Add a response for a particular Certificate ID.
+ *
+ * @param certID certificate ID details
+ * @param certStatus status of the certificate - null if okay
+ * @param singleExtensions optional extensions
+ */
+ public BasicOCSPRespBuilder addResponse(
+ CertificateID certID,
+ CertificateStatus certStatus,
+ Extensions singleExtensions)
+ {
+ list.add(new ResponseObject(certID, certStatus, new Date(), null, singleExtensions));
+
+ return this;
+ }
+
+ /**
+ * Add a response for a particular Certificate ID.
+ *
+ * @param certID certificate ID details
+ * @param nextUpdate date when next update should be requested
+ * @param certStatus status of the certificate - null if okay
+ * @param singleExtensions optional extensions
+ */
+ public BasicOCSPRespBuilder addResponse(
+ CertificateID certID,
+ CertificateStatus certStatus,
+ Date nextUpdate,
+ Extensions singleExtensions)
+ {
+ list.add(new ResponseObject(certID, certStatus, new Date(), nextUpdate, singleExtensions));
+
+ return this;
+ }
+
+ /**
+ * Add a response for a particular Certificate ID.
+ *
+ * @param certID certificate ID details
+ * @param thisUpdate date this response was valid on
+ * @param nextUpdate date when next update should be requested
+ * @param certStatus status of the certificate - null if okay
+ * @param singleExtensions optional extensions
+ */
+ public BasicOCSPRespBuilder addResponse(
+ CertificateID certID,
+ CertificateStatus certStatus,
+ Date thisUpdate,
+ Date nextUpdate,
+ Extensions singleExtensions)
+ {
+ list.add(new ResponseObject(certID, certStatus, thisUpdate, nextUpdate, singleExtensions));
+
+ return this;
+ }
+
+ /**
+ * Set the extensions for the response.
+ *
+ * @param responseExtensions the extension object to carry.
+ */
+ public BasicOCSPRespBuilder setResponseExtensions(
+ Extensions responseExtensions)
+ {
+ this.responseExtensions = responseExtensions;
+
+ return this;
+ }
+
+ public BasicOCSPResp build(
+ ContentSigner signer,
+ X509CertificateHolder[] chain,
+ Date producedAt)
+ throws OCSPException
+ {
+ Iterator it = list.iterator();
+
+ ASN1EncodableVector responses = new ASN1EncodableVector();
+
+ while (it.hasNext())
+ {
+ try
+ {
+ responses.add(((ResponseObject)it.next()).toResponse());
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("exception creating Request", e);
+ }
+ }
+
+ ResponseData tbsResp = new ResponseData(responderID.toASN1Object(), new ASN1GeneralizedTime(producedAt), new DERSequence(responses), responseExtensions);
+ DERBitString bitSig;
+
+ try
+ {
+ OutputStream sigOut = signer.getOutputStream();
+
+ sigOut.write(tbsResp.getEncoded(ASN1Encoding.DER));
+ sigOut.close();
+
+ bitSig = new DERBitString(signer.getSignature());
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("exception processing TBSRequest: " + e.getMessage(), e);
+ }
+
+ AlgorithmIdentifier sigAlgId = signer.getAlgorithmIdentifier();
+
+ DERSequence chainSeq = null;
+ if (chain != null && chain.length > 0)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != chain.length; i++)
+ {
+ v.add(chain[i].toASN1Structure());
+ }
+
+ chainSeq = new DERSequence(v);
+ }
+
+ return new BasicOCSPResp(new BasicOCSPResponse(tbsResp, sigAlgId, bitSig, chainSeq));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateID.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateID.java
new file mode 100644
index 00000000..aac029ca
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateID.java
@@ -0,0 +1,156 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.ocsp.CertID;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.DigestCalculator;
+import org.spongycastle.operator.DigestCalculatorProvider;
+import org.spongycastle.operator.OperatorCreationException;
+
+public class CertificateID
+{
+ public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+
+ private final CertID id;
+
+ public CertificateID(
+ CertID id)
+ {
+ if (id == null)
+ {
+ throw new IllegalArgumentException("'id' cannot be null");
+ }
+ this.id = id;
+ }
+
+ /**
+ * create from an issuer certificate and the serial number of the
+ * certificate it signed.
+ *
+ * @param issuerCert issuing certificate
+ * @param number serial number
+ *
+ * @exception OCSPException if any problems occur creating the id fields.
+ */
+ public CertificateID(
+ DigestCalculator digestCalculator, X509CertificateHolder issuerCert,
+ BigInteger number)
+ throws OCSPException
+ {
+ this.id = createCertID(digestCalculator, issuerCert, new ASN1Integer(number));
+ }
+
+ public ASN1ObjectIdentifier getHashAlgOID()
+ {
+ return id.getHashAlgorithm().getAlgorithm();
+ }
+
+ public byte[] getIssuerNameHash()
+ {
+ return id.getIssuerNameHash().getOctets();
+ }
+
+ public byte[] getIssuerKeyHash()
+ {
+ return id.getIssuerKeyHash().getOctets();
+ }
+
+ /**
+ * return the serial number for the certificate associated
+ * with this request.
+ */
+ public BigInteger getSerialNumber()
+ {
+ return id.getSerialNumber().getValue();
+ }
+
+ public boolean matchesIssuer(X509CertificateHolder issuerCert, DigestCalculatorProvider digCalcProvider)
+ throws OCSPException
+ {
+ try
+ {
+ return createCertID(digCalcProvider.get(id.getHashAlgorithm()), issuerCert, id.getSerialNumber()).equals(id);
+ }
+ catch (OperatorCreationException e)
+ {
+ throw new OCSPException("unable to create digest calculator: " + e.getMessage(), e);
+ }
+ }
+
+ public CertID toASN1Object()
+ {
+ return id;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof CertificateID))
+ {
+ return false;
+ }
+
+ CertificateID obj = (CertificateID)o;
+
+ return id.toASN1Primitive().equals(obj.id.toASN1Primitive());
+ }
+
+ public int hashCode()
+ {
+ return id.toASN1Primitive().hashCode();
+ }
+
+ /**
+ * Create a new CertificateID for a new serial number derived from a previous one
+ * calculated for the same CA certificate.
+ *
+ * @param original the previously calculated CertificateID for the CA.
+ * @param newSerialNumber the serial number for the new certificate of interest.
+ *
+ * @return a new CertificateID for newSerialNumber
+ */
+ public static CertificateID deriveCertificateID(CertificateID original, BigInteger newSerialNumber)
+ {
+ return new CertificateID(new CertID(original.id.getHashAlgorithm(), original.id.getIssuerNameHash(), original.id.getIssuerKeyHash(), new ASN1Integer(newSerialNumber)));
+ }
+
+ private static CertID createCertID(DigestCalculator digCalc, X509CertificateHolder issuerCert, ASN1Integer serialNumber)
+ throws OCSPException
+ {
+ try
+ {
+ OutputStream dgOut = digCalc.getOutputStream();
+
+ dgOut.write(issuerCert.toASN1Structure().getSubject().getEncoded(ASN1Encoding.DER));
+ dgOut.close();
+
+ ASN1OctetString issuerNameHash = new DEROctetString(digCalc.getDigest());
+
+ SubjectPublicKeyInfo info = issuerCert.getSubjectPublicKeyInfo();
+
+ dgOut = digCalc.getOutputStream();
+
+ dgOut.write(info.getPublicKeyData().getBytes());
+ dgOut.close();
+
+ ASN1OctetString issuerKeyHash = new DEROctetString(digCalc.getDigest());
+
+ return new CertID(digCalc.getAlgorithmIdentifier(), issuerNameHash, issuerKeyHash, serialNumber);
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("problem creating ID: " + e, e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateStatus.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateStatus.java
new file mode 100644
index 00000000..ba84b8f8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/CertificateStatus.java
@@ -0,0 +1,6 @@
+package org.spongycastle.cert.ocsp;
+
+public interface CertificateStatus
+{
+ public static final CertificateStatus GOOD = null;
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPException.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPException.java
new file mode 100644
index 00000000..be91e3d8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPException.java
@@ -0,0 +1,27 @@
+package org.spongycastle.cert.ocsp;
+
+public class OCSPException
+ extends Exception
+{
+ private Throwable cause;
+
+ public OCSPException(
+ String name)
+ {
+ super(name);
+ }
+
+ public OCSPException(
+ String name,
+ Throwable cause)
+ {
+ super(name);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReq.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReq.java
new file mode 100644
index 00000000..17089673
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReq.java
@@ -0,0 +1,259 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ASN1Exception;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OutputStream;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ocsp.OCSPRequest;
+import org.spongycastle.asn1.ocsp.Request;
+import org.spongycastle.asn1.x509.Certificate;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.cert.CertIOException;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.ContentVerifier;
+import org.spongycastle.operator.ContentVerifierProvider;
+
+/**
+ * <pre>
+ * OCSPRequest ::= SEQUENCE {
+ * tbsRequest TBSRequest,
+ * optionalSignature [0] EXPLICIT Signature OPTIONAL }
+ *
+ * TBSRequest ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ * requestList SEQUENCE OF Request,
+ * requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+ *
+ * Signature ::= SEQUENCE {
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
+ *
+ * Version ::= INTEGER { v1(0) }
+ *
+ * Request ::= SEQUENCE {
+ * reqCert CertID,
+ * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+ *
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ * serialNumber CertificateSerialNumber }
+ * </pre>
+ */
+public class OCSPReq
+{
+ private static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0];
+
+ private OCSPRequest req;
+ private Extensions extensions;
+
+ public OCSPReq(
+ OCSPRequest req)
+ {
+ this.req = req;
+ this.extensions = req.getTbsRequest().getRequestExtensions();
+ }
+
+ public OCSPReq(
+ byte[] req)
+ throws IOException
+ {
+ this(new ASN1InputStream(req));
+ }
+
+ private OCSPReq(
+ ASN1InputStream aIn)
+ throws IOException
+ {
+ try
+ {
+ this.req = OCSPRequest.getInstance(aIn.readObject());
+ if (req == null)
+ {
+ throw new CertIOException("malformed request: no request data found");
+ }
+ this.extensions = req.getTbsRequest().getRequestExtensions();
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed request: " + e.getMessage(), e);
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed request: " + e.getMessage(), e);
+ }
+ catch (ASN1Exception e)
+ {
+ throw new CertIOException("malformed request: " + e.getMessage(), e);
+ }
+ }
+
+ public int getVersionNumber()
+ {
+ return req.getTbsRequest().getVersion().getValue().intValue() + 1;
+ }
+
+ public GeneralName getRequestorName()
+ {
+ return GeneralName.getInstance(req.getTbsRequest().getRequestorName());
+ }
+
+ public Req[] getRequestList()
+ {
+ ASN1Sequence seq = req.getTbsRequest().getRequestList();
+ Req[] requests = new Req[seq.size()];
+
+ for (int i = 0; i != requests.length; i++)
+ {
+ requests[i] = new Req(Request.getInstance(seq.getObjectAt(i)));
+ }
+
+ return requests;
+ }
+
+ public boolean hasExtensions()
+ {
+ return extensions != null;
+ }
+
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ public List getExtensionOIDs()
+ {
+ return OCSPUtils.getExtensionOIDs(extensions);
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return OCSPUtils.getCriticalExtensionOIDs(extensions);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
+ }
+
+ /**
+ * return the object identifier representing the signature algorithm
+ */
+ public ASN1ObjectIdentifier getSignatureAlgOID()
+ {
+ if (!this.isSigned())
+ {
+ return null;
+ }
+
+ return req.getOptionalSignature().getSignatureAlgorithm().getAlgorithm();
+ }
+
+ public byte[] getSignature()
+ {
+ if (!this.isSigned())
+ {
+ return null;
+ }
+
+ return req.getOptionalSignature().getSignature().getBytes();
+ }
+
+ public X509CertificateHolder[] getCerts()
+ {
+ //
+ // load the certificates if we have any
+ //
+ if (req.getOptionalSignature() != null)
+ {
+ ASN1Sequence s = req.getOptionalSignature().getCerts();
+
+ if (s != null)
+ {
+ X509CertificateHolder[] certs = new X509CertificateHolder[s.size()];
+
+ for (int i = 0; i != certs.length; i++)
+ {
+ certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i)));
+ }
+
+ return certs;
+ }
+
+ return EMPTY_CERTS;
+ }
+ else
+ {
+ return EMPTY_CERTS;
+ }
+ }
+
+ /**
+ * Return whether or not this request is signed.
+ *
+ * @return true if signed false otherwise.
+ */
+ public boolean isSigned()
+ {
+ return req.getOptionalSignature() != null;
+ }
+
+ /**
+ * verify the signature against the TBSRequest object we contain.
+ */
+ public boolean isSignatureValid(
+ ContentVerifierProvider verifierProvider)
+ throws OCSPException
+ {
+ if (!this.isSigned())
+ {
+ throw new OCSPException("attempt to verify signature on unsigned object");
+ }
+
+ try
+ {
+ ContentVerifier verifier = verifierProvider.get(req.getOptionalSignature().getSignatureAlgorithm());
+ OutputStream sOut = verifier.getOutputStream();
+
+ sOut.write(req.getTbsRequest().getEncoded(ASN1Encoding.DER));
+
+ return verifier.verify(this.getSignature());
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("exception processing signature: " + e, e);
+ }
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(req);
+
+ return bOut.toByteArray();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReqBuilder.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReqBuilder.java
new file mode 100644
index 00000000..2bc0a6d1
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPReqBuilder.java
@@ -0,0 +1,199 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.DERBitString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.ocsp.OCSPRequest;
+import org.spongycastle.asn1.ocsp.Request;
+import org.spongycastle.asn1.ocsp.Signature;
+import org.spongycastle.asn1.ocsp.TBSRequest;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.operator.ContentSigner;
+
+public class OCSPReqBuilder
+{
+ private List list = new ArrayList();
+ private GeneralName requestorName = null;
+ private Extensions requestExtensions = null;
+
+ private class RequestObject
+ {
+ CertificateID certId;
+ Extensions extensions;
+
+ public RequestObject(
+ CertificateID certId,
+ Extensions extensions)
+ {
+ this.certId = certId;
+ this.extensions = extensions;
+ }
+
+ public Request toRequest()
+ throws Exception
+ {
+ return new Request(certId.toASN1Object(), extensions);
+ }
+ }
+
+ /**
+ * Add a request for the given CertificateID.
+ *
+ * @param certId certificate ID of interest
+ */
+ public OCSPReqBuilder addRequest(
+ CertificateID certId)
+ {
+ list.add(new RequestObject(certId, null));
+
+ return this;
+ }
+
+ /**
+ * Add a request with extensions
+ *
+ * @param certId certificate ID of interest
+ * @param singleRequestExtensions the extensions to attach to the request
+ */
+ public OCSPReqBuilder addRequest(
+ CertificateID certId,
+ Extensions singleRequestExtensions)
+ {
+ list.add(new RequestObject(certId, singleRequestExtensions));
+
+ return this;
+ }
+
+ /**
+ * Set the requestor name to the passed in X500Principal
+ *
+ * @param requestorName a X500Principal representing the requestor name.
+ */
+ public OCSPReqBuilder setRequestorName(
+ X500Name requestorName)
+ {
+ this.requestorName = new GeneralName(GeneralName.directoryName, requestorName);
+
+ return this;
+ }
+
+ public OCSPReqBuilder setRequestorName(
+ GeneralName requestorName)
+ {
+ this.requestorName = requestorName;
+
+ return this;
+ }
+
+ public OCSPReqBuilder setRequestExtensions(
+ Extensions requestExtensions)
+ {
+ this.requestExtensions = requestExtensions;
+
+ return this;
+ }
+
+ private OCSPReq generateRequest(
+ ContentSigner contentSigner,
+ X509CertificateHolder[] chain)
+ throws OCSPException
+ {
+ Iterator it = list.iterator();
+
+ ASN1EncodableVector requests = new ASN1EncodableVector();
+
+ while (it.hasNext())
+ {
+ try
+ {
+ requests.add(((RequestObject)it.next()).toRequest());
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("exception creating Request", e);
+ }
+ }
+
+ TBSRequest tbsReq = new TBSRequest(requestorName, new DERSequence(requests), requestExtensions);
+
+ Signature signature = null;
+
+ if (contentSigner != null)
+ {
+ if (requestorName == null)
+ {
+ throw new OCSPException("requestorName must be specified if request is signed.");
+ }
+
+ try
+ {
+ OutputStream sOut = contentSigner.getOutputStream();
+
+ sOut.write(tbsReq.getEncoded(ASN1Encoding.DER));
+
+ sOut.close();
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("exception processing TBSRequest: " + e, e);
+ }
+
+ DERBitString bitSig = new DERBitString(contentSigner.getSignature());
+
+ AlgorithmIdentifier sigAlgId = contentSigner.getAlgorithmIdentifier();
+
+ if (chain != null && chain.length > 0)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (int i = 0; i != chain.length; i++)
+ {
+ v.add(chain[i].toASN1Structure());
+ }
+
+ signature = new Signature(sigAlgId, bitSig, new DERSequence(v));
+ }
+ else
+ {
+ signature = new Signature(sigAlgId, bitSig);
+ }
+ }
+
+ return new OCSPReq(new OCSPRequest(tbsReq, signature));
+ }
+
+ /**
+ * Generate an unsigned request
+ *
+ * @return the OCSPReq
+ * @throws org.spongycastle.ocsp.OCSPException
+ */
+ public OCSPReq build()
+ throws OCSPException
+ {
+ return generateRequest(null, null);
+ }
+
+ public OCSPReq build(
+ ContentSigner signer,
+ X509CertificateHolder[] chain)
+ throws OCSPException, IllegalArgumentException
+ {
+ if (signer == null)
+ {
+ throw new IllegalArgumentException("no signer specified");
+ }
+
+ return generateRequest(signer, chain);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPResp.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPResp.java
new file mode 100644
index 00000000..0b587f70
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPResp.java
@@ -0,0 +1,141 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.spongycastle.asn1.ASN1Exception;
+import org.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ocsp.BasicOCSPResponse;
+import org.spongycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import org.spongycastle.asn1.ocsp.OCSPResponse;
+import org.spongycastle.asn1.ocsp.ResponseBytes;
+import org.spongycastle.cert.CertIOException;
+
+public class OCSPResp
+{
+ public static final int SUCCESSFUL = 0; // Response has valid confirmations
+ public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request
+ public static final int INTERNAL_ERROR = 2; // Internal error in issuer
+ public static final int TRY_LATER = 3; // Try again later
+ // (4) is not used
+ public static final int SIG_REQUIRED = 5; // Must sign the request
+ public static final int UNAUTHORIZED = 6; // Request unauthorized
+
+ private OCSPResponse resp;
+
+ public OCSPResp(
+ OCSPResponse resp)
+ {
+ this.resp = resp;
+ }
+
+ public OCSPResp(
+ byte[] resp)
+ throws IOException
+ {
+ this(new ByteArrayInputStream(resp));
+ }
+
+ public OCSPResp(
+ InputStream resp)
+ throws IOException
+ {
+ this(new ASN1InputStream(resp));
+ }
+
+ private OCSPResp(
+ ASN1InputStream aIn)
+ throws IOException
+ {
+ try
+ {
+ this.resp = OCSPResponse.getInstance(aIn.readObject());
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new CertIOException("malformed response: " + e.getMessage(), e);
+ }
+ catch (ClassCastException e)
+ {
+ throw new CertIOException("malformed response: " + e.getMessage(), e);
+ }
+ catch (ASN1Exception e)
+ {
+ throw new CertIOException("malformed response: " + e.getMessage(), e);
+ }
+
+ if (resp == null)
+ {
+ throw new CertIOException("malformed response: no response data found");
+ }
+ }
+
+ public int getStatus()
+ {
+ return this.resp.getResponseStatus().getValue().intValue();
+ }
+
+ public Object getResponseObject()
+ throws OCSPException
+ {
+ ResponseBytes rb = this.resp.getResponseBytes();
+
+ if (rb == null)
+ {
+ return null;
+ }
+
+ if (rb.getResponseType().equals(OCSPObjectIdentifiers.id_pkix_ocsp_basic))
+ {
+ try
+ {
+ ASN1Primitive obj = ASN1Primitive.fromByteArray(rb.getResponse().getOctets());
+ return new BasicOCSPResp(BasicOCSPResponse.getInstance(obj));
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("problem decoding object: " + e, e);
+ }
+ }
+
+ return rb.getResponse();
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ return resp.getEncoded();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ {
+ return true;
+ }
+
+ if (!(o instanceof OCSPResp))
+ {
+ return false;
+ }
+
+ OCSPResp r = (OCSPResp)o;
+
+ return resp.equals(r.resp);
+ }
+
+ public int hashCode()
+ {
+ return resp.hashCode();
+ }
+
+ public OCSPResponse toASN1Structure()
+ {
+ return resp;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPRespBuilder.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPRespBuilder.java
new file mode 100644
index 00000000..fe2da11d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPRespBuilder.java
@@ -0,0 +1,59 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import org.spongycastle.asn1.ocsp.OCSPResponse;
+import org.spongycastle.asn1.ocsp.OCSPResponseStatus;
+import org.spongycastle.asn1.ocsp.ResponseBytes;
+
+/**
+ * base generator for an OCSP response - at the moment this only supports the
+ * generation of responses containing BasicOCSP responses.
+ */
+public class OCSPRespBuilder
+{
+ public static final int SUCCESSFUL = 0; // Response has valid confirmations
+ public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request
+ public static final int INTERNAL_ERROR = 2; // Internal error in issuer
+ public static final int TRY_LATER = 3; // Try again later
+ // (4) is not used
+ public static final int SIG_REQUIRED = 5; // Must sign the request
+ public static final int UNAUTHORIZED = 6; // Request unauthorized
+
+ public OCSPResp build(
+ int status,
+ Object response)
+ throws OCSPException
+ {
+ if (response == null)
+ {
+ return new OCSPResp(new OCSPResponse(new OCSPResponseStatus(status), null));
+ }
+
+ if (response instanceof BasicOCSPResp)
+ {
+ BasicOCSPResp r = (BasicOCSPResp)response;
+ ASN1OctetString octs;
+
+ try
+ {
+ octs = new DEROctetString(r.getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new OCSPException("can't encode object.", e);
+ }
+
+ ResponseBytes rb = new ResponseBytes(
+ OCSPObjectIdentifiers.id_pkix_ocsp_basic, octs);
+
+ return new OCSPResp(new OCSPResponse(
+ new OCSPResponseStatus(status), rb));
+ }
+
+ throw new OCSPException("unknown response object");
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPUtils.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPUtils.java
new file mode 100644
index 00000000..a58ca071
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/OCSPUtils.java
@@ -0,0 +1,64 @@
+package org.spongycastle.cert.ocsp;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.x509.Extensions;
+import org.spongycastle.cert.X509CertificateHolder;
+
+class OCSPUtils
+{
+ static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0];
+
+ static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
+ static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
+
+ static Date extractDate(ASN1GeneralizedTime time)
+ {
+ try
+ {
+ return time.getDate();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException("exception processing GeneralizedTime: " + e.getMessage());
+ }
+ }
+
+ static Set getCriticalExtensionOIDs(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return EMPTY_SET;
+ }
+
+ return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs())));
+ }
+
+ static Set getNonCriticalExtensionOIDs(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return EMPTY_SET;
+ }
+
+ // TODO: should probably produce a set that imposes correct ordering
+ return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
+ }
+
+ static List getExtensionOIDs(Extensions extensions)
+ {
+ if (extensions == null)
+ {
+ return EMPTY_LIST;
+ }
+
+ return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs()));
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/Req.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/Req.java
new file mode 100644
index 00000000..52c174dd
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/Req.java
@@ -0,0 +1,25 @@
+package org.spongycastle.cert.ocsp;
+
+import org.spongycastle.asn1.ocsp.Request;
+import org.spongycastle.asn1.x509.Extensions;
+
+public class Req
+{
+ private Request req;
+
+ public Req(
+ Request req)
+ {
+ this.req = req;
+ }
+
+ public CertificateID getCertID()
+ {
+ return new CertificateID(req.getReqCert());
+ }
+
+ public Extensions getSingleRequestExtensions()
+ {
+ return req.getSingleRequestExtensions();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/RespData.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/RespData.java
new file mode 100644
index 00000000..3db55a3e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/RespData.java
@@ -0,0 +1,52 @@
+package org.spongycastle.cert.ocsp;
+
+import java.util.Date;
+
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.ocsp.ResponseData;
+import org.spongycastle.asn1.ocsp.SingleResponse;
+import org.spongycastle.asn1.x509.Extensions;
+
+public class RespData
+{
+ private ResponseData data;
+
+ public RespData(
+ ResponseData data)
+ {
+ this.data = data;
+ }
+
+ public int getVersion()
+ {
+ return data.getVersion().getValue().intValue() + 1;
+ }
+
+ public RespID getResponderId()
+ {
+ return new RespID(data.getResponderID());
+ }
+
+ public Date getProducedAt()
+ {
+ return OCSPUtils.extractDate(data.getProducedAt());
+ }
+
+ public SingleResp[] getResponses()
+ {
+ ASN1Sequence s = data.getResponses();
+ SingleResp[] rs = new SingleResp[s.size()];
+
+ for (int i = 0; i != rs.length; i++)
+ {
+ rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i)));
+ }
+
+ return rs;
+ }
+
+ public Extensions getResponseExtensions()
+ {
+ return data.getResponseExtensions();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/RespID.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/RespID.java
new file mode 100644
index 00000000..7510200d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/RespID.java
@@ -0,0 +1,89 @@
+package org.spongycastle.cert.ocsp;
+
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.ocsp.ResponderID;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.operator.DigestCalculator;
+
+/**
+ * Carrier for a ResponderID.
+ */
+public class RespID
+{
+ public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
+
+ ResponderID id;
+
+ public RespID(
+ ResponderID id)
+ {
+ this.id = id;
+ }
+
+ public RespID(
+ X500Name name)
+ {
+ this.id = new ResponderID(name);
+ }
+
+ /**
+ * Calculate a RespID based on the public key of the responder.
+ *
+ * @param subjectPublicKeyInfo the info structure for the responder public key.
+ * @param digCalc a SHA-1 digest calculator.
+ * @throws OCSPException on exception creating ID.
+ */
+ public RespID(
+ SubjectPublicKeyInfo subjectPublicKeyInfo,
+ DigestCalculator digCalc)
+ throws OCSPException
+ {
+ try
+ {
+ if (!digCalc.getAlgorithmIdentifier().equals(HASH_SHA1))
+ {
+ throw new IllegalArgumentException("only SHA-1 can be used with RespID");
+ }
+
+ OutputStream digOut = digCalc.getOutputStream();
+
+ digOut.write(subjectPublicKeyInfo.getPublicKeyData().getBytes());
+ digOut.close();
+
+ this.id = new ResponderID(new DEROctetString(digCalc.getDigest()));
+ }
+ catch (Exception e)
+ {
+ throw new OCSPException("problem creating ID: " + e, e);
+ }
+ }
+
+ public ResponderID toASN1Object()
+ {
+ return id;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof RespID))
+ {
+ return false;
+ }
+
+ RespID obj = (RespID)o;
+
+ return id.equals(obj.id);
+ }
+
+ public int hashCode()
+ {
+ return id.hashCode();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/RevokedStatus.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/RevokedStatus.java
new file mode 100644
index 00000000..0842ea58
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/RevokedStatus.java
@@ -0,0 +1,55 @@
+package org.spongycastle.cert.ocsp;
+
+import java.util.Date;
+
+import org.spongycastle.asn1.ASN1GeneralizedTime;
+import org.spongycastle.asn1.ocsp.RevokedInfo;
+import org.spongycastle.asn1.x509.CRLReason;
+
+/**
+ * wrapper for the RevokedInfo object
+ */
+public class RevokedStatus
+ implements CertificateStatus
+{
+ RevokedInfo info;
+
+ public RevokedStatus(
+ RevokedInfo info)
+ {
+ this.info = info;
+ }
+
+ public RevokedStatus(
+ Date revocationDate,
+ int reason)
+ {
+ this.info = new RevokedInfo(new ASN1GeneralizedTime(revocationDate), CRLReason.lookup(reason));
+ }
+
+ public Date getRevocationTime()
+ {
+ return OCSPUtils.extractDate(info.getRevocationTime());
+ }
+
+ public boolean hasRevocationReason()
+ {
+ return (info.getRevocationReason() != null);
+ }
+
+ /**
+ * return the revocation reason. Note: this field is optional, test for it
+ * with hasRevocationReason() first.
+ * @return the revocation reason value.
+ * @exception IllegalStateException if a reason is asked for and none is avaliable
+ */
+ public int getRevocationReason()
+ {
+ if (info.getRevocationReason() == null)
+ {
+ throw new IllegalStateException("attempt to get a reason where none is available");
+ }
+
+ return info.getRevocationReason().getValue().intValue();
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/SingleResp.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/SingleResp.java
new file mode 100644
index 00000000..98beb8b3
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/SingleResp.java
@@ -0,0 +1,102 @@
+package org.spongycastle.cert.ocsp;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ocsp.CertStatus;
+import org.spongycastle.asn1.ocsp.RevokedInfo;
+import org.spongycastle.asn1.ocsp.SingleResponse;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.Extensions;
+
+public class SingleResp
+{
+ private SingleResponse resp;
+ private Extensions extensions;
+
+ public SingleResp(
+ SingleResponse resp)
+ {
+ this.resp = resp;
+ this.extensions = resp.getSingleExtensions();
+ }
+
+ public CertificateID getCertID()
+ {
+ return new CertificateID(resp.getCertID());
+ }
+
+ /**
+ * Return the status object for the response - null indicates good.
+ *
+ * @return the status object for the response, null if it is good.
+ */
+ public CertificateStatus getCertStatus()
+ {
+ CertStatus s = resp.getCertStatus();
+
+ if (s.getTagNo() == 0)
+ {
+ return null; // good
+ }
+ else if (s.getTagNo() == 1)
+ {
+ return new RevokedStatus(RevokedInfo.getInstance(s.getStatus()));
+ }
+
+ return new UnknownStatus();
+ }
+
+ public Date getThisUpdate()
+ {
+ return OCSPUtils.extractDate(resp.getThisUpdate());
+ }
+
+ /**
+ * return the NextUpdate value - note: this is an optional field so may
+ * be returned as null.
+ *
+ * @return nextUpdate, or null if not present.
+ */
+ public Date getNextUpdate()
+ {
+ if (resp.getNextUpdate() == null)
+ {
+ return null;
+ }
+
+ return OCSPUtils.extractDate(resp.getNextUpdate());
+ }
+
+ public boolean hasExtensions()
+ {
+ return extensions != null;
+ }
+
+ public Extension getExtension(ASN1ObjectIdentifier oid)
+ {
+ if (extensions != null)
+ {
+ return extensions.getExtension(oid);
+ }
+
+ return null;
+ }
+
+ public List getExtensionOIDs()
+ {
+ return OCSPUtils.getExtensionOIDs(extensions);
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return OCSPUtils.getCriticalExtensionOIDs(extensions);
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return OCSPUtils.getNonCriticalExtensionOIDs(extensions);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/UnknownStatus.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/UnknownStatus.java
new file mode 100644
index 00000000..42eda721
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/UnknownStatus.java
@@ -0,0 +1,12 @@
+package org.spongycastle.cert.ocsp;
+
+/**
+ * wrapper for the UnknownInfo object
+ */
+public class UnknownStatus
+ implements CertificateStatus
+{
+ public UnknownStatus()
+ {
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java
new file mode 100644
index 00000000..cbd591db
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java
@@ -0,0 +1,18 @@
+package org.spongycastle.cert.ocsp.jcajce;
+
+import java.security.PublicKey;
+
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.ocsp.BasicOCSPRespBuilder;
+import org.spongycastle.cert.ocsp.OCSPException;
+import org.spongycastle.operator.DigestCalculator;
+
+public class JcaBasicOCSPRespBuilder
+ extends BasicOCSPRespBuilder
+{
+ public JcaBasicOCSPRespBuilder(PublicKey key, DigestCalculator digCalc)
+ throws OCSPException
+ {
+ super(SubjectPublicKeyInfo.getInstance(key.getEncoded()), digCalc);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaCertificateID.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaCertificateID.java
new file mode 100644
index 00000000..d59e7e04
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaCertificateID.java
@@ -0,0 +1,20 @@
+package org.spongycastle.cert.ocsp.jcajce;
+
+import java.math.BigInteger;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.spongycastle.cert.ocsp.CertificateID;
+import org.spongycastle.cert.ocsp.OCSPException;
+import org.spongycastle.operator.DigestCalculator;
+
+public class JcaCertificateID
+ extends CertificateID
+{
+ public JcaCertificateID(DigestCalculator digestCalculator, X509Certificate issuerCert, BigInteger number)
+ throws OCSPException, CertificateEncodingException
+ {
+ super(digestCalculator, new JcaX509CertificateHolder(issuerCert), number);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaRespID.java b/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaRespID.java
new file mode 100644
index 00000000..55a390be
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/ocsp/jcajce/JcaRespID.java
@@ -0,0 +1,26 @@
+package org.spongycastle.cert.ocsp.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.cert.ocsp.OCSPException;
+import org.spongycastle.cert.ocsp.RespID;
+import org.spongycastle.operator.DigestCalculator;
+
+public class JcaRespID
+ extends RespID
+{
+ public JcaRespID(X500Principal name)
+ {
+ super(X500Name.getInstance(name.getEncoded()));
+ }
+
+ public JcaRespID(PublicKey pubKey, DigestCalculator digCalc)
+ throws OCSPException
+ {
+ super(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()), digCalc);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPath.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPath.java
new file mode 100644
index 00000000..bbb20a4d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPath.java
@@ -0,0 +1,80 @@
+package org.spongycastle.cert.path;
+
+import org.spongycastle.cert.X509CertificateHolder;
+
+public class CertPath
+{
+ private final X509CertificateHolder[] certificates;
+
+ public CertPath(X509CertificateHolder[] certificates)
+ {
+ this.certificates = copyArray(certificates);
+ }
+
+ public X509CertificateHolder[] getCertificates()
+ {
+ return copyArray(certificates);
+ }
+
+ public CertPathValidationResult validate(CertPathValidation[] ruleSet)
+ {
+ CertPathValidationContext context = new CertPathValidationContext(CertPathUtils.getCriticalExtensionsOIDs(certificates));
+
+ for (int i = 0; i != ruleSet.length; i++)
+ {
+ for (int j = certificates.length - 1; j >= 0; j--)
+ {
+ try
+ {
+ context.setIsEndEntity(j == 0);
+ ruleSet[i].validate(context, certificates[j]);
+ }
+ catch (CertPathValidationException e)
+ { // TODO: introduce object to hold (i and e)
+ return new CertPathValidationResult(context, j, i, e);
+ }
+ }
+ }
+
+ return new CertPathValidationResult(context);
+ }
+
+ public CertPathValidationResult evaluate(CertPathValidation[] ruleSet)
+ {
+ CertPathValidationContext context = new CertPathValidationContext(CertPathUtils.getCriticalExtensionsOIDs(certificates));
+
+ CertPathValidationResultBuilder builder = new CertPathValidationResultBuilder();
+
+ for (int i = 0; i != ruleSet.length; i++)
+ {
+ for (int j = certificates.length - 1; j >= 0; j--)
+ {
+ try
+ {
+ context.setIsEndEntity(j == 0);
+ ruleSet[i].validate(context, certificates[j]);
+ }
+ catch (CertPathValidationException e)
+ {
+ builder.addException(e);
+ }
+ }
+ }
+
+ return builder.build();
+ }
+
+ private X509CertificateHolder[] copyArray(X509CertificateHolder[] array)
+ {
+ X509CertificateHolder[] rv = new X509CertificateHolder[array.length];
+
+ System.arraycopy(array, 0, rv, 0, rv.length);
+
+ return rv;
+ }
+
+ public int length()
+ {
+ return certificates.length;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPathUtils.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPathUtils.java
new file mode 100644
index 00000000..257e0bb8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPathUtils.java
@@ -0,0 +1,21 @@
+package org.spongycastle.cert.path;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.spongycastle.cert.X509CertificateHolder;
+
+class CertPathUtils
+{
+ static Set getCriticalExtensionsOIDs(X509CertificateHolder[] certificates)
+ {
+ Set criticalExtensions = new HashSet();
+
+ for (int i = 0; i != certificates.length; i++)
+ {
+ criticalExtensions.addAll(certificates[i].getCriticalExtensionOIDs());
+ }
+
+ return criticalExtensions;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidation.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidation.java
new file mode 100644
index 00000000..11f48367
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidation.java
@@ -0,0 +1,11 @@
+package org.spongycastle.cert.path;
+
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.util.Memoable;
+
+public interface CertPathValidation
+ extends Memoable
+{
+ public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+ throws CertPathValidationException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationContext.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationContext.java
new file mode 100644
index 00000000..010b4ef6
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationContext.java
@@ -0,0 +1,61 @@
+package org.spongycastle.cert.path;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.util.Memoable;
+
+public class CertPathValidationContext
+ implements Memoable
+{
+ private Set criticalExtensions;
+
+ private Set handledExtensions = new HashSet();
+ private boolean endEntity;
+ private int index;
+
+ public CertPathValidationContext(Set criticalExtensionsOIDs)
+ {
+ this.criticalExtensions = criticalExtensionsOIDs;
+ }
+
+ public void addHandledExtension(ASN1ObjectIdentifier extensionIdentifier)
+ {
+ this.handledExtensions.add(extensionIdentifier);
+ }
+
+ public void setIsEndEntity(boolean isEndEntity)
+ {
+ this.endEntity = isEndEntity;
+ }
+
+ public Set getUnhandledCriticalExtensionOIDs()
+ {
+ Set rv = new HashSet(criticalExtensions);
+
+ rv.removeAll(handledExtensions);
+
+ return rv;
+ }
+
+ /**
+ * Returns true if the current certificate is the end-entity certificate.
+ *
+ * @return if current cert end-entity, false otherwise.
+ */
+ public boolean isEndEntity()
+ {
+ return endEntity;
+ }
+
+ public Memoable copy()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void reset(Memoable other)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationException.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationException.java
new file mode 100644
index 00000000..0a1188d1
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationException.java
@@ -0,0 +1,24 @@
+package org.spongycastle.cert.path;
+
+public class CertPathValidationException
+ extends Exception
+{
+ private final Exception cause;
+
+ public CertPathValidationException(String msg)
+ {
+ this(msg, null);
+ }
+
+ public CertPathValidationException(String msg, Exception cause)
+ {
+ super(msg);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResult.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResult.java
new file mode 100644
index 00000000..276ef8d4
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResult.java
@@ -0,0 +1,66 @@
+package org.spongycastle.cert.path;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class CertPathValidationResult
+{
+ private final boolean isValid;
+ private final CertPathValidationException cause;
+ private final Set unhandledCriticalExtensionOIDs;
+
+ private int[] certIndexes;
+
+ public CertPathValidationResult(CertPathValidationContext context)
+ {
+ this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
+ this.isValid = this.unhandledCriticalExtensionOIDs.isEmpty();
+ cause = null;
+ }
+
+ public CertPathValidationResult(CertPathValidationContext context, int certIndex, int ruleIndex, CertPathValidationException cause)
+ {
+ this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
+ this.isValid = false;
+ this.cause = cause;
+ }
+
+ public CertPathValidationResult(CertPathValidationContext context, int[] certIndexes, int[] ruleIndexes, CertPathValidationException[] cause)
+ {
+ // TODO
+ this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
+ this.isValid = false;
+ this.cause = cause[0];
+ this.certIndexes = certIndexes;
+ }
+
+ public boolean isValid()
+ {
+ return isValid;
+ }
+
+ public Exception getCause()
+ {
+ if (cause != null)
+ {
+ return cause;
+ }
+
+ if (!unhandledCriticalExtensionOIDs.isEmpty())
+ {
+ return new CertPathValidationException("Unhandled Critical Extensions");
+ }
+
+ return null;
+ }
+
+ public Set getUnhandledCriticalExtensionOIDs()
+ {
+ return unhandledCriticalExtensionOIDs;
+ }
+
+ public boolean isDetailed()
+ {
+ return this.certIndexes != null;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResultBuilder.java b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResultBuilder.java
new file mode 100644
index 00000000..80bf7ff2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/CertPathValidationResultBuilder.java
@@ -0,0 +1,14 @@
+package org.spongycastle.cert.path;
+
+class CertPathValidationResultBuilder
+{
+ public CertPathValidationResult build()
+ {
+ return new CertPathValidationResult(null, 0, 0, null);
+ }
+
+ public void addException(CertPathValidationException exception)
+ {
+ //To change body of created methods use File | Settings | File Templates.
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/BasicConstraintsValidation.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/BasicConstraintsValidation.java
new file mode 100644
index 00000000..bfe0f1a4
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/BasicConstraintsValidation.java
@@ -0,0 +1,103 @@
+package org.spongycastle.cert.path.validations;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.x509.BasicConstraints;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.path.CertPathValidation;
+import org.spongycastle.cert.path.CertPathValidationContext;
+import org.spongycastle.cert.path.CertPathValidationException;
+import org.spongycastle.util.Memoable;
+
+public class BasicConstraintsValidation
+ implements CertPathValidation
+{
+ private boolean isMandatory;
+ private BasicConstraints bc;
+ private int maxPathLength;
+
+ public BasicConstraintsValidation()
+ {
+ this(true);
+ }
+
+ public BasicConstraintsValidation(boolean isMandatory)
+ {
+ this.isMandatory = isMandatory;
+ }
+
+ public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+ throws CertPathValidationException
+ {
+ if (maxPathLength < 0)
+ {
+ throw new CertPathValidationException("BasicConstraints path length exceeded");
+ }
+
+ context.addHandledExtension(Extension.basicConstraints);
+
+ BasicConstraints certBC = BasicConstraints.fromExtensions(certificate.getExtensions());
+
+ if (certBC != null)
+ {
+ if (bc != null)
+ {
+ if (certBC.isCA())
+ {
+ BigInteger pathLengthConstraint = certBC.getPathLenConstraint();
+
+ if (pathLengthConstraint != null)
+ {
+ int plc = pathLengthConstraint.intValue();
+
+ if (plc < maxPathLength)
+ {
+ maxPathLength = plc;
+ bc = certBC;
+ }
+ }
+ }
+ }
+ else
+ {
+ bc = certBC;
+ if (certBC.isCA())
+ {
+ maxPathLength = certBC.getPathLenConstraint().intValue();
+ }
+ }
+ }
+ else
+ {
+ if (bc != null)
+ {
+ maxPathLength--;
+ }
+ }
+
+ if (isMandatory && bc == null)
+ {
+ throw new CertPathValidationException("BasicConstraints not present in path");
+ }
+ }
+
+ public Memoable copy()
+ {
+ BasicConstraintsValidation v = new BasicConstraintsValidation(isMandatory);
+
+ v.bc = this.bc;
+ v.maxPathLength = this.maxPathLength;
+
+ return v;
+ }
+
+ public void reset(Memoable other)
+ {
+ BasicConstraintsValidation v = (BasicConstraintsValidation)other;
+
+ this.isMandatory = v.isMandatory;
+ this.bc = v.bc;
+ this.maxPathLength = v.maxPathLength;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/CRLValidation.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/CRLValidation.java
new file mode 100644
index 00000000..325126e1
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/CRLValidation.java
@@ -0,0 +1,78 @@
+package org.spongycastle.cert.path.validations;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.X509CRLHolder;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.path.CertPathValidation;
+import org.spongycastle.cert.path.CertPathValidationContext;
+import org.spongycastle.cert.path.CertPathValidationException;
+import org.spongycastle.util.Memoable;
+import org.spongycastle.util.Selector;
+import org.spongycastle.util.Store;
+
+public class CRLValidation
+ implements CertPathValidation
+{
+ private Store crls;
+ private X500Name workingIssuerName;
+
+ public CRLValidation(X500Name trustAnchorName, Store crls)
+ {
+ this.workingIssuerName = trustAnchorName;
+ this.crls = crls;
+ }
+
+ public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+ throws CertPathValidationException
+ {
+ // TODO: add handling of delta CRLs
+ Collection matches = crls.getMatches(new Selector()
+ {
+ public boolean match(Object obj)
+ {
+ X509CRLHolder crl = (X509CRLHolder)obj;
+
+ return (crl.getIssuer().equals(workingIssuerName));
+ }
+
+ public Object clone()
+ {
+ return this;
+ }
+ });
+
+ if (matches.isEmpty())
+ {
+ throw new CertPathValidationException("CRL for " + workingIssuerName + " not found");
+ }
+
+ for (Iterator it = matches.iterator(); it.hasNext();)
+ {
+ X509CRLHolder crl = (X509CRLHolder)it.next();
+
+ // TODO: not quite right!
+ if (crl.getRevokedCertificate(certificate.getSerialNumber()) != null)
+ {
+ throw new CertPathValidationException("Certificate revoked");
+ }
+ }
+
+ this.workingIssuerName = certificate.getSubject();
+ }
+
+ public Memoable copy()
+ {
+ return new CRLValidation(workingIssuerName, crls);
+ }
+
+ public void reset(Memoable other)
+ {
+ CRLValidation v = (CRLValidation)other;
+
+ this.workingIssuerName = v.workingIssuerName;
+ this.crls = v.crls;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidation.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidation.java
new file mode 100644
index 00000000..7015adb0
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidation.java
@@ -0,0 +1,146 @@
+package org.spongycastle.cert.path.validations;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.PolicyConstraints;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.path.CertPathValidation;
+import org.spongycastle.cert.path.CertPathValidationContext;
+import org.spongycastle.cert.path.CertPathValidationException;
+import org.spongycastle.util.Memoable;
+
+public class CertificatePoliciesValidation
+ implements CertPathValidation
+{
+ private int explicitPolicy;
+ private int policyMapping;
+ private int inhibitAnyPolicy;
+
+ CertificatePoliciesValidation(int pathLength)
+ {
+ this(pathLength, false, false, false);
+ }
+
+ CertificatePoliciesValidation(int pathLength, boolean isExplicitPolicyRequired, boolean isAnyPolicyInhibited, boolean isPolicyMappingInhibited)
+ {
+ //
+ // (d)
+ //
+
+ if (isExplicitPolicyRequired)
+ {
+ explicitPolicy = 0;
+ }
+ else
+ {
+ explicitPolicy = pathLength + 1;
+ }
+
+ //
+ // (e)
+ //
+ if (isAnyPolicyInhibited)
+ {
+ inhibitAnyPolicy = 0;
+ }
+ else
+ {
+ inhibitAnyPolicy = pathLength + 1;
+ }
+
+ //
+ // (f)
+ //
+ if (isPolicyMappingInhibited)
+ {
+ policyMapping = 0;
+ }
+ else
+ {
+ policyMapping = pathLength + 1;
+ }
+ }
+
+ public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+ throws CertPathValidationException
+ {
+ context.addHandledExtension(Extension.policyConstraints);
+ context.addHandledExtension(Extension.inhibitAnyPolicy);
+
+ if (!context.isEndEntity())
+ {
+ if (!ValidationUtils.isSelfIssued(certificate))
+ {
+ //
+ // H (1), (2), (3)
+ //
+ explicitPolicy = countDown(explicitPolicy);
+ policyMapping = countDown(policyMapping);
+ inhibitAnyPolicy = countDown(inhibitAnyPolicy);
+
+ //
+ // I (1), (2)
+ //
+ PolicyConstraints policyConstraints = PolicyConstraints.fromExtensions(certificate.getExtensions());
+
+ if (policyConstraints != null)
+ {
+ BigInteger requireExplicitPolicyMapping = policyConstraints.getRequireExplicitPolicyMapping();
+ if (requireExplicitPolicyMapping != null)
+ {
+ if (requireExplicitPolicyMapping.intValue() < explicitPolicy)
+ {
+ explicitPolicy = requireExplicitPolicyMapping.intValue();
+ }
+ }
+
+ BigInteger inhibitPolicyMapping = policyConstraints.getInhibitPolicyMapping();
+ if (inhibitPolicyMapping != null)
+ {
+ if (inhibitPolicyMapping.intValue() < policyMapping)
+ {
+ policyMapping = inhibitPolicyMapping.intValue();
+ }
+ }
+ }
+
+ //
+ // J
+ //
+ Extension ext = certificate.getExtension(Extension.inhibitAnyPolicy);
+
+ if (ext != null)
+ {
+ int extValue = ASN1Integer.getInstance(ext.getParsedValue()).getValue().intValue();
+
+ if (extValue < inhibitAnyPolicy)
+ {
+ inhibitAnyPolicy = extValue;
+ }
+ }
+ }
+ }
+ }
+
+ private int countDown(int policyCounter)
+ {
+ if (policyCounter != 0)
+ {
+ return policyCounter - 1;
+ }
+
+ return 0;
+ }
+
+ public Memoable copy()
+ {
+ return new CertificatePoliciesValidation(0); // TODO:
+ }
+
+ public void reset(Memoable other)
+ {
+ CertificatePoliciesValidation v = (CertificatePoliciesValidation)other; // TODO:
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java
new file mode 100644
index 00000000..44817b61
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java
@@ -0,0 +1,35 @@
+package org.spongycastle.cert.path.validations;
+
+import org.spongycastle.cert.path.CertPath;
+
+public class CertificatePoliciesValidationBuilder
+{
+ private boolean isExplicitPolicyRequired;
+ private boolean isAnyPolicyInhibited;
+ private boolean isPolicyMappingInhibited;
+
+ public void setAnyPolicyInhibited(boolean anyPolicyInhibited)
+ {
+ isAnyPolicyInhibited = anyPolicyInhibited;
+ }
+
+ public void setExplicitPolicyRequired(boolean explicitPolicyRequired)
+ {
+ isExplicitPolicyRequired = explicitPolicyRequired;
+ }
+
+ public void setPolicyMappingInhibited(boolean policyMappingInhibited)
+ {
+ isPolicyMappingInhibited = policyMappingInhibited;
+ }
+
+ public CertificatePoliciesValidation build(int pathLen)
+ {
+ return new CertificatePoliciesValidation(pathLen, isExplicitPolicyRequired, isAnyPolicyInhibited, isPolicyMappingInhibited);
+ }
+
+ public CertificatePoliciesValidation build(CertPath path)
+ {
+ return build(path.length());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/KeyUsageValidation.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/KeyUsageValidation.java
new file mode 100644
index 00000000..7211b7cd
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/KeyUsageValidation.java
@@ -0,0 +1,63 @@
+package org.spongycastle.cert.path.validations;
+
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.KeyUsage;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.path.CertPathValidation;
+import org.spongycastle.cert.path.CertPathValidationContext;
+import org.spongycastle.cert.path.CertPathValidationException;
+import org.spongycastle.util.Memoable;
+
+public class KeyUsageValidation
+ implements CertPathValidation
+{
+ private boolean isMandatory;
+
+ public KeyUsageValidation()
+ {
+ this(true);
+ }
+
+ public KeyUsageValidation(boolean isMandatory)
+ {
+ this.isMandatory = isMandatory;
+ }
+
+ public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+ throws CertPathValidationException
+ {
+ context.addHandledExtension(Extension.keyUsage);
+
+ if (!context.isEndEntity())
+ {
+ KeyUsage usage = KeyUsage.fromExtensions(certificate.getExtensions());
+
+ if (usage != null)
+ {
+ if (!usage.hasUsages(KeyUsage.keyCertSign))
+ {
+ throw new CertPathValidationException("Issuer certificate KeyUsage extension does not permit key signing");
+ }
+ }
+ else
+ {
+ if (isMandatory)
+ {
+ throw new CertPathValidationException("KeyUsage extension not present in CA certificate");
+ }
+ }
+ }
+ }
+
+ public Memoable copy()
+ {
+ return new KeyUsageValidation(isMandatory);
+ }
+
+ public void reset(Memoable other)
+ {
+ KeyUsageValidation v = (KeyUsageValidation)other;
+
+ this.isMandatory = v.isMandatory;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/ParentCertIssuedValidation.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/ParentCertIssuedValidation.java
new file mode 100644
index 00000000..dff47fb7
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/ParentCertIssuedValidation.java
@@ -0,0 +1,127 @@
+package org.spongycastle.cert.path.validations;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1Null;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.CertException;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.cert.X509ContentVerifierProviderBuilder;
+import org.spongycastle.cert.path.CertPathValidation;
+import org.spongycastle.cert.path.CertPathValidationContext;
+import org.spongycastle.cert.path.CertPathValidationException;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.util.Memoable;
+
+public class ParentCertIssuedValidation
+ implements CertPathValidation
+{
+ private X509ContentVerifierProviderBuilder contentVerifierProvider;
+
+ private X500Name workingIssuerName;
+ private SubjectPublicKeyInfo workingPublicKey;
+ private AlgorithmIdentifier workingAlgId;
+
+ public ParentCertIssuedValidation(X509ContentVerifierProviderBuilder contentVerifierProvider)
+ {
+ this.contentVerifierProvider = contentVerifierProvider;
+ }
+
+ public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+ throws CertPathValidationException
+ {
+ if (workingIssuerName != null)
+ {
+ if (!workingIssuerName.equals(certificate.getIssuer()))
+ {
+ throw new CertPathValidationException("Certificate issue does not match parent");
+ }
+ }
+
+ if (workingPublicKey != null)
+ {
+ try
+ {
+ SubjectPublicKeyInfo validatingKeyInfo;
+
+ if (workingPublicKey.getAlgorithm().equals(workingAlgId))
+ {
+ validatingKeyInfo = workingPublicKey;
+ }
+ else
+ {
+ validatingKeyInfo = new SubjectPublicKeyInfo(workingAlgId, workingPublicKey.parsePublicKey());
+ }
+
+ if (!certificate.isSignatureValid(contentVerifierProvider.build(validatingKeyInfo)))
+ {
+ throw new CertPathValidationException("Certificate signature not for public key in parent");
+ }
+ }
+ catch (OperatorCreationException e)
+ {
+ throw new CertPathValidationException("Unable to create verifier: " + e.getMessage(), e);
+ }
+ catch (CertException e)
+ {
+ throw new CertPathValidationException("Unable to validate signature: " + e.getMessage(), e);
+ }
+ catch (IOException e)
+ {
+ throw new CertPathValidationException("Unable to build public key: " + e.getMessage(), e);
+ }
+ }
+
+ workingIssuerName = certificate.getSubject();
+ workingPublicKey = certificate.getSubjectPublicKeyInfo();
+
+ if (workingAlgId != null)
+ {
+ // check for inherited parameters
+ if (workingPublicKey.getAlgorithm().getAlgorithm().equals(workingAlgId.getAlgorithm()))
+ {
+ if (!isNull(workingPublicKey.getAlgorithm().getParameters()))
+ {
+ workingAlgId = workingPublicKey.getAlgorithm();
+ }
+ }
+ else
+ {
+ workingAlgId = workingPublicKey.getAlgorithm();
+ }
+ }
+ else
+ {
+ workingAlgId = workingPublicKey.getAlgorithm();
+ }
+ }
+
+ private boolean isNull(ASN1Encodable obj)
+ {
+ return obj == null || obj instanceof ASN1Null;
+ }
+
+ public Memoable copy()
+ {
+ ParentCertIssuedValidation v = new ParentCertIssuedValidation(contentVerifierProvider);
+
+ v.workingAlgId = this.workingAlgId;
+ v.workingIssuerName = this.workingIssuerName;
+ v.workingPublicKey = this.workingPublicKey;
+
+ return v;
+ }
+
+ public void reset(Memoable other)
+ {
+ ParentCertIssuedValidation v = (ParentCertIssuedValidation)other;
+
+ this.contentVerifierProvider = v.contentVerifierProvider;
+ this.workingAlgId = v.workingAlgId;
+ this.workingIssuerName = v.workingIssuerName;
+ this.workingPublicKey = v.workingPublicKey;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/path/validations/ValidationUtils.java b/pkix/src/main/java/org/spongycastle/cert/path/validations/ValidationUtils.java
new file mode 100644
index 00000000..5dbf495a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/path/validations/ValidationUtils.java
@@ -0,0 +1,11 @@
+package org.spongycastle.cert.path.validations;
+
+import org.spongycastle.cert.X509CertificateHolder;
+
+class ValidationUtils
+{
+ static boolean isSelfIssued(X509CertificateHolder cert)
+ {
+ return cert.getSubject().equals(cert.getIssuer());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/MSOutlookKeyIdCalculator.java b/pkix/src/main/java/org/spongycastle/cert/selector/MSOutlookKeyIdCalculator.java
new file mode 100644
index 00000000..23748ac3
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/MSOutlookKeyIdCalculator.java
@@ -0,0 +1,422 @@
+package org.spongycastle.cert.selector;
+
+import java.io.IOException;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.util.Pack;
+
+class MSOutlookKeyIdCalculator
+{
+ // This is less than ideal, but it seems to be the best way of supporting this without exposing SHA-1
+ // as the class is only used to workout the MSOutlook Key ID, you can think of the fact it's SHA-1 as
+ // a coincidence...
+ static byte[] calculateKeyId(SubjectPublicKeyInfo info)
+ {
+ SHA1Digest dig = new SHA1Digest();
+ byte[] hash = new byte[dig.getDigestSize()];
+ byte[] spkiEnc = new byte[0];
+ try
+ {
+ spkiEnc = info.getEncoded(ASN1Encoding.DER);
+ }
+ catch (IOException e)
+ {
+ return new byte[0];
+ }
+
+ // try the outlook 2010 calculation
+ dig.update(spkiEnc, 0, spkiEnc.length);
+
+ dig.doFinal(hash, 0);
+
+ return hash;
+ }
+
+ private static abstract class GeneralDigest
+ {
+ private static final int BYTE_LENGTH = 64;
+ private byte[] xBuf;
+ private int xBufOff;
+
+ private long byteCount;
+
+ /**
+ * Standard constructor
+ */
+ protected GeneralDigest()
+ {
+ xBuf = new byte[4];
+ xBufOff = 0;
+ }
+
+ /**
+ * Copy constructor. We are using copy constructors in place
+ * of the Object.clone() interface as this interface is not
+ * supported by J2ME.
+ */
+ protected GeneralDigest(GeneralDigest t)
+ {
+ xBuf = new byte[t.xBuf.length];
+
+ copyIn(t);
+ }
+
+ protected void copyIn(GeneralDigest t)
+ {
+ System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
+
+ xBufOff = t.xBufOff;
+ byteCount = t.byteCount;
+ }
+
+ public void update(
+ byte in)
+ {
+ xBuf[xBufOff++] = in;
+
+ if (xBufOff == xBuf.length)
+ {
+ processWord(xBuf, 0);
+ xBufOff = 0;
+ }
+
+ byteCount++;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((xBufOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > xBuf.length)
+ {
+ processWord(in, inOff);
+
+ inOff += xBuf.length;
+ len -= xBuf.length;
+ byteCount += xBuf.length;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+ }
+
+ public void finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ //
+ // add the pad bytes.
+ //
+ update((byte)128);
+
+ while (xBufOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processLength(bitLength);
+
+ processBlock();
+ }
+
+ public void reset()
+ {
+ byteCount = 0;
+
+ xBufOff = 0;
+ for (int i = 0; i < xBuf.length; i++)
+ {
+ xBuf[i] = 0;
+ }
+ }
+
+ protected abstract void processWord(byte[] in, int inOff);
+
+ protected abstract void processLength(long bitLength);
+
+ protected abstract void processBlock();
+ }
+
+ private static class SHA1Digest
+ extends GeneralDigest
+ {
+ private static final int DIGEST_LENGTH = 20;
+
+ private int H1, H2, H3, H4, H5;
+
+ private int[] X = new int[80];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public SHA1Digest()
+ {
+ reset();
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-1";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ // Note: Inlined for performance
+ // X[xOff] = Pack.bigEndianToInt(in, inOff);
+ int n = in[ inOff] << 24;
+ n |= (in[++inOff] & 0xff) << 16;
+ n |= (in[++inOff] & 0xff) << 8;
+ n |= (in[++inOff] & 0xff);
+ X[xOff] = n;
+
+ if (++xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength >>> 32);
+ X[15] = (int)(bitLength & 0xffffffff);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.intToBigEndian(H1, out, outOff);
+ Pack.intToBigEndian(H2, out, outOff + 4);
+ Pack.intToBigEndian(H3, out, outOff + 8);
+ Pack.intToBigEndian(H4, out, outOff + 12);
+ Pack.intToBigEndian(H5, out, outOff + 16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+ H5 = 0xc3d2e1f0;
+
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // Additive constants
+ //
+ private static final int Y1 = 0x5a827999;
+ private static final int Y2 = 0x6ed9eba1;
+ private static final int Y3 = 0x8f1bbcdc;
+ private static final int Y4 = 0xca62c1d6;
+
+ private int f(
+ int u,
+ int v,
+ int w)
+ {
+ return ((u & v) | ((~u) & w));
+ }
+
+ private int h(
+ int u,
+ int v,
+ int w)
+ {
+ return (u ^ v ^ w);
+ }
+
+ private int g(
+ int u,
+ int v,
+ int w)
+ {
+ return ((u & v) | (u & w) | (v & w));
+ }
+
+ protected void processBlock()
+ {
+ //
+ // expand 16 word block into 80 word block.
+ //
+ for (int i = 16; i < 80; i++)
+ {
+ int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16];
+ X[i] = t << 1 | t >>> 31;
+ }
+
+ //
+ // set up working variables.
+ //
+ int A = H1;
+ int B = H2;
+ int C = H3;
+ int D = H4;
+ int E = H5;
+
+ //
+ // round 1
+ //
+ int idx = 0;
+
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1;
+ C = C << 30 | C >>> 2;
+ }
+
+ //
+ // round 2
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2;
+ C = C << 30 | C >>> 2;
+ }
+
+ //
+ // round 3
+ //
+ for (int j = 0; j < 4; j++)
+ {
+ // E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3;
+ C = C << 30 | C >>> 2;
+ }
+
+ //
+ // round 4
+ //
+ for (int j = 0; j <= 3; j++)
+ {
+ // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4
+ // B = rotateLeft(B, 30)
+ E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4;
+ B = B << 30 | B >>> 2;
+
+ D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4;
+ A = A << 30 | A >>> 2;
+
+ C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4;
+ E = E << 30 | E >>> 2;
+
+ B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4;
+ D = D << 30 | D >>> 2;
+
+ A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4;
+ C = C << 30 | C >>> 2;
+ }
+
+
+ H1 += A;
+ H2 += B;
+ H3 += C;
+ H4 += D;
+ H5 += E;
+
+ //
+ // reset start of the buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i < 16; i++)
+ {
+ X[i] = 0;
+ }
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelector.java b/pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelector.java
new file mode 100644
index 00000000..3ae30b33
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelector.java
@@ -0,0 +1,268 @@
+package org.spongycastle.cert.selector;
+
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Date;
+
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.Target;
+import org.spongycastle.asn1.x509.TargetInformation;
+import org.spongycastle.asn1.x509.Targets;
+import org.spongycastle.cert.AttributeCertificateHolder;
+import org.spongycastle.cert.AttributeCertificateIssuer;
+import org.spongycastle.cert.X509AttributeCertificateHolder;
+import org.spongycastle.util.Selector;
+
+/**
+ * This class is an <code>Selector</code> like implementation to select
+ * attribute certificates from a given set of criteria.
+ */
+public class X509AttributeCertificateHolderSelector
+ implements Selector
+{
+
+ // TODO: name constraints???
+
+ private final AttributeCertificateHolder holder;
+
+ private final AttributeCertificateIssuer issuer;
+
+ private final BigInteger serialNumber;
+
+ private final Date attributeCertificateValid;
+
+ private final X509AttributeCertificateHolder attributeCert;
+
+ private final Collection targetNames;
+
+ private final Collection targetGroups;
+
+ X509AttributeCertificateHolderSelector(
+ AttributeCertificateHolder holder,
+ AttributeCertificateIssuer issuer,
+ BigInteger serialNumber,
+ Date attributeCertificateValid,
+ X509AttributeCertificateHolder attributeCert,
+ Collection targetNames,
+ Collection targetGroups)
+ {
+ this.holder = holder;
+ this.issuer = issuer;
+ this.serialNumber = serialNumber;
+ this.attributeCertificateValid = attributeCertificateValid;
+ this.attributeCert = attributeCert;
+ this.targetNames = targetNames;
+ this.targetGroups = targetGroups;
+ }
+
+ /**
+ * Decides if the given attribute certificate should be selected.
+ *
+ * @param obj The X509AttributeCertificateHolder which should be checked.
+ * @return <code>true</code> if the attribute certificate is a match
+ * <code>false</code> otherwise.
+ */
+ public boolean match(Object obj)
+ {
+ if (!(obj instanceof X509AttributeCertificateHolder))
+ {
+ return false;
+ }
+
+ X509AttributeCertificateHolder attrCert = (X509AttributeCertificateHolder)obj;
+
+ if (this.attributeCert != null)
+ {
+ if (!this.attributeCert.equals(attrCert))
+ {
+ return false;
+ }
+ }
+ if (serialNumber != null)
+ {
+ if (!attrCert.getSerialNumber().equals(serialNumber))
+ {
+ return false;
+ }
+ }
+ if (holder != null)
+ {
+ if (!attrCert.getHolder().equals(holder))
+ {
+ return false;
+ }
+ }
+ if (issuer != null)
+ {
+ if (!attrCert.getIssuer().equals(issuer))
+ {
+ return false;
+ }
+ }
+
+ if (attributeCertificateValid != null)
+ {
+ if (!attrCert.isValidOn(attributeCertificateValid))
+ {
+ return false;
+ }
+ }
+ if (!targetNames.isEmpty() || !targetGroups.isEmpty())
+ {
+ Extension targetInfoExt = attrCert.getExtension(Extension.targetInformation);
+ if (targetInfoExt != null)
+ {
+ TargetInformation targetinfo;
+ try
+ {
+ targetinfo = TargetInformation.getInstance(targetInfoExt.getParsedValue());
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+ Targets[] targetss = targetinfo.getTargetsObjects();
+ if (!targetNames.isEmpty())
+ {
+ boolean found = false;
+
+ for (int i=0; i<targetss.length; i++)
+ {
+ Targets t = targetss[i];
+ Target[] targets = t.getTargets();
+ for (int j=0; j<targets.length; j++)
+ {
+ if (targetNames.contains(GeneralName.getInstance(targets[j]
+ .getTargetName())))
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ {
+ return false;
+ }
+ }
+ if (!targetGroups.isEmpty())
+ {
+ boolean found = false;
+
+ for (int i=0; i<targetss.length; i++)
+ {
+ Targets t = targetss[i];
+ Target[] targets = t.getTargets();
+ for (int j=0; j<targets.length; j++)
+ {
+ if (targetGroups.contains(GeneralName.getInstance(targets[j]
+ .getTargetGroup())))
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a clone of this object.
+ *
+ * @return the clone.
+ */
+ public Object clone()
+ {
+ X509AttributeCertificateHolderSelector sel = new X509AttributeCertificateHolderSelector(
+ holder, issuer, serialNumber, attributeCertificateValid, attributeCert, targetNames, targetGroups);
+
+ return sel;
+ }
+
+ /**
+ * Returns the attribute certificate holder which must be matched.
+ *
+ * @return Returns an X509AttributeCertificateHolder
+ */
+ public X509AttributeCertificateHolder getAttributeCert()
+ {
+ return attributeCert;
+ }
+
+ /**
+ * Get the criteria for the validity.
+ *
+ * @return Returns the attributeCertificateValid.
+ */
+ public Date getAttributeCertificateValid()
+ {
+ if (attributeCertificateValid != null)
+ {
+ return new Date(attributeCertificateValid.getTime());
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the holder.
+ *
+ * @return Returns the holder.
+ */
+ public AttributeCertificateHolder getHolder()
+ {
+ return holder;
+ }
+
+ /**
+ * Returns the issuer criterion.
+ *
+ * @return Returns the issuer.
+ */
+ public AttributeCertificateIssuer getIssuer()
+ {
+ return issuer;
+ }
+
+ /**
+ * Gets the serial number the attribute certificate must have.
+ *
+ * @return Returns the serialNumber.
+ */
+ public BigInteger getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ /**
+ * Gets the target names. The collection consists of GeneralName objects.
+ * <p>
+ * The returned collection is immutable.
+ *
+ * @return The collection of target names
+ */
+ public Collection getTargetNames()
+ {
+ return targetNames;
+ }
+
+ /**
+ * Gets the target groups. The collection consists of GeneralName objects.
+ * <p>
+ * The returned collection is immutable.
+ *
+ * @return The collection of target groups.
+ */
+ public Collection getTargetGroups()
+ {
+ return targetGroups;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java b/pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
new file mode 100644
index 00000000..35df571d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
@@ -0,0 +1,194 @@
+package org.spongycastle.cert.selector;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.cert.AttributeCertificateHolder;
+import org.spongycastle.cert.AttributeCertificateIssuer;
+import org.spongycastle.cert.X509AttributeCertificateHolder;
+
+/**
+ * This class builds selectors according to the set criteria.
+ */
+public class X509AttributeCertificateHolderSelectorBuilder
+{
+
+ // TODO: name constraints???
+
+ private AttributeCertificateHolder holder;
+
+ private AttributeCertificateIssuer issuer;
+
+ private BigInteger serialNumber;
+
+ private Date attributeCertificateValid;
+
+ private X509AttributeCertificateHolder attributeCert;
+
+ private Collection targetNames = new HashSet();
+
+ private Collection targetGroups = new HashSet();
+
+ public X509AttributeCertificateHolderSelectorBuilder()
+ {
+ }
+
+ /**
+ * Set the attribute certificate to be matched. If <code>null</code> is
+ * given any will do.
+ *
+ * @param attributeCert The attribute certificate holder to set.
+ */
+ public void setAttributeCert(X509AttributeCertificateHolder attributeCert)
+ {
+ this.attributeCert = attributeCert;
+ }
+
+ /**
+ * Set the time, when the certificate must be valid. If <code>null</code>
+ * is given any will do.
+ *
+ * @param attributeCertificateValid The attribute certificate validation
+ * time to set.
+ */
+ public void setAttributeCertificateValid(Date attributeCertificateValid)
+ {
+ if (attributeCertificateValid != null)
+ {
+ this.attributeCertificateValid = new Date(attributeCertificateValid
+ .getTime());
+ }
+ else
+ {
+ this.attributeCertificateValid = null;
+ }
+ }
+
+ /**
+ * Sets the holder. If <code>null</code> is given any will do.
+ *
+ * @param holder The holder to set.
+ */
+ public void setHolder(AttributeCertificateHolder holder)
+ {
+ this.holder = holder;
+ }
+
+ /**
+ * Sets the issuer the attribute certificate must have. If <code>null</code>
+ * is given any will do.
+ *
+ * @param issuer The issuer to set.
+ */
+ public void setIssuer(AttributeCertificateIssuer issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ /**
+ * Sets the serial number the attribute certificate must have. If
+ * <code>null</code> is given any will do.
+ *
+ * @param serialNumber The serialNumber to set.
+ */
+ public void setSerialNumber(BigInteger serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ /**
+ * Adds a target name criterion for the attribute certificate to the target
+ * information extension criteria. The <code>X509AttributeCertificateHolder</code>
+ * must contain at least one of the specified target names.
+ * <p>
+ * Each attribute certificate may contain a target information extension
+ * limiting the servers where this attribute certificate can be used. If
+ * this extension is not present, the attribute certificate is not targeted
+ * and may be accepted by any server.
+ *
+ * @param name The name as a GeneralName (not <code>null</code>)
+ */
+ public void addTargetName(GeneralName name)
+ {
+ targetNames.add(name);
+ }
+
+ /**
+ * Adds a collection with target names criteria. If <code>null</code> is
+ * given any will do.
+ * <p>
+ * The collection consists of either GeneralName objects or byte[] arrays representing
+ * DER encoded GeneralName structures.
+ *
+ * @param names A collection of target names.
+ * @throws java.io.IOException if a parsing error occurs.
+ * @see #addTargetName(org.spongycastle.asn1.x509.GeneralName)
+ */
+ public void setTargetNames(Collection names) throws IOException
+ {
+ targetNames = extractGeneralNames(names);
+ }
+
+ /**
+ * Adds a target group criterion for the attribute certificate to the target
+ * information extension criteria. The <code>X509AttributeCertificateHolder</code>
+ * must contain at least one of the specified target groups.
+ * <p>
+ * Each attribute certificate may contain a target information extension
+ * limiting the servers where this attribute certificate can be used. If
+ * this extension is not present, the attribute certificate is not targeted
+ * and may be accepted by any server.
+ *
+ * @param group The group as GeneralName form (not <code>null</code>)
+ */
+ public void addTargetGroup(GeneralName group)
+ {
+ targetGroups.add(group);
+ }
+
+ /**
+ * Adds a collection with target groups criteria. If <code>null</code> is
+ * given any will do.
+ * <p>
+ * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code representing DER
+ * encoded GeneralNames.
+ *
+ * @param names A collection of target groups.
+ * @throws java.io.IOException if a parsing error occurs.
+ * @see #addTargetGroup(org.spongycastle.asn1.x509.GeneralName)
+ */
+ public void setTargetGroups(Collection names) throws IOException
+ {
+ targetGroups = extractGeneralNames(names);
+ }
+
+ private Set extractGeneralNames(Collection names)
+ throws IOException
+ {
+ if (names == null || names.isEmpty())
+ {
+ return new HashSet();
+ }
+ Set temp = new HashSet();
+ for (Iterator it = names.iterator(); it.hasNext();)
+ {
+ temp.add(GeneralName.getInstance(it.next()));
+ }
+ return temp;
+ }
+
+ public X509AttributeCertificateHolderSelector build()
+ {
+ X509AttributeCertificateHolderSelector sel = new X509AttributeCertificateHolderSelector(
+ holder, issuer, serialNumber, attributeCertificateValid, attributeCert, Collections.unmodifiableCollection(new HashSet(targetNames)), Collections.unmodifiableCollection(new HashSet(targetGroups)));
+
+ return sel;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/X509CertificateHolderSelector.java b/pkix/src/main/java/org/spongycastle/cert/selector/X509CertificateHolderSelector.java
new file mode 100644
index 00000000..8b6b88e3
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/X509CertificateHolderSelector.java
@@ -0,0 +1,152 @@
+package org.spongycastle.cert.selector;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.cms.IssuerAndSerialNumber;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Selector;
+
+/**
+ * a basic index for a X509CertificateHolder class
+ */
+public class X509CertificateHolderSelector
+ implements Selector
+{
+ private byte[] subjectKeyId;
+
+ private X500Name issuer;
+ private BigInteger serialNumber;
+
+ /**
+ * Construct a selector with the value of a public key's subjectKeyId.
+ *
+ * @param subjectKeyId a subjectKeyId
+ */
+ public X509CertificateHolderSelector(byte[] subjectKeyId)
+ {
+ this(null, null, subjectKeyId);
+ }
+
+ /**
+ * Construct a signer ID based on the issuer and serial number of the signer's associated
+ * certificate.
+ *
+ * @param issuer the issuer of the signer's associated certificate.
+ * @param serialNumber the serial number of the signer's associated certificate.
+ */
+ public X509CertificateHolderSelector(X500Name issuer, BigInteger serialNumber)
+ {
+ this(issuer, serialNumber, null);
+ }
+
+ /**
+ * Construct a signer ID based on the issuer and serial number of the signer's associated
+ * certificate.
+ *
+ * @param issuer the issuer of the signer's associated certificate.
+ * @param serialNumber the serial number of the signer's associated certificate.
+ * @param subjectKeyId the subject key identifier to use to match the signers associated certificate.
+ */
+ public X509CertificateHolderSelector(X500Name issuer, BigInteger serialNumber, byte[] subjectKeyId)
+ {
+ this.issuer = issuer;
+ this.serialNumber = serialNumber;
+ this.subjectKeyId = subjectKeyId;
+ }
+
+ public X500Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public byte[] getSubjectKeyIdentifier()
+ {
+ return Arrays.clone(subjectKeyId);
+ }
+
+ public int hashCode()
+ {
+ int code = Arrays.hashCode(subjectKeyId);
+
+ if (this.serialNumber != null)
+ {
+ code ^= this.serialNumber.hashCode();
+ }
+
+ if (this.issuer != null)
+ {
+ code ^= this.issuer.hashCode();
+ }
+
+ return code;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof X509CertificateHolderSelector))
+ {
+ return false;
+ }
+
+ X509CertificateHolderSelector id = (X509CertificateHolderSelector)o;
+
+ return Arrays.areEqual(subjectKeyId, id.subjectKeyId)
+ && equalsObj(this.serialNumber, id.serialNumber)
+ && equalsObj(this.issuer, id.issuer);
+ }
+
+ private boolean equalsObj(Object a, Object b)
+ {
+ return (a != null) ? a.equals(b) : b == null;
+ }
+
+ public boolean match(Object obj)
+ {
+ if (obj instanceof X509CertificateHolder)
+ {
+ X509CertificateHolder certHldr = (X509CertificateHolder)obj;
+
+ if (this.getSerialNumber() != null)
+ {
+ IssuerAndSerialNumber iAndS = new IssuerAndSerialNumber(certHldr.toASN1Structure());
+
+ return iAndS.getName().equals(this.issuer)
+ && iAndS.getSerialNumber().getValue().equals(this.serialNumber);
+ }
+ else if (subjectKeyId != null)
+ {
+ Extension ext = certHldr.getExtension(Extension.subjectKeyIdentifier);
+
+ if (ext == null)
+ {
+ return Arrays.areEqual(subjectKeyId, MSOutlookKeyIdCalculator.calculateKeyId(certHldr.getSubjectPublicKeyInfo()));
+ }
+
+ byte[] subKeyID = ASN1OctetString.getInstance(ext.getParsedValue()).getOctets();
+
+ return Arrays.areEqual(subjectKeyId, subKeyID);
+ }
+ }
+ else if (obj instanceof byte[])
+ {
+ return Arrays.areEqual(subjectKeyId, (byte[])obj);
+ }
+
+ return false;
+ }
+
+ public Object clone()
+ {
+ return new X509CertificateHolderSelector(this.issuer, this.serialNumber, this.subjectKeyId);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaSelectorConverter.java b/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaSelectorConverter.java
new file mode 100644
index 00000000..551000b0
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaSelectorConverter.java
@@ -0,0 +1,35 @@
+package org.spongycastle.cert.selector.jcajce;
+
+import java.io.IOException;
+import java.security.cert.X509CertSelector;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.selector.X509CertificateHolderSelector;
+
+public class JcaSelectorConverter
+{
+ public JcaSelectorConverter()
+ {
+
+ }
+
+ public X509CertificateHolderSelector getCertificateHolderSelector(X509CertSelector certSelector)
+ {
+ try
+ {
+ if (certSelector.getSubjectKeyIdentifier() != null)
+ {
+ return new X509CertificateHolderSelector(X500Name.getInstance(certSelector.getIssuerAsBytes()), certSelector.getSerialNumber(), ASN1OctetString.getInstance(certSelector.getSubjectKeyIdentifier()).getOctets());
+ }
+ else
+ {
+ return new X509CertificateHolderSelector(X500Name.getInstance(certSelector.getIssuerAsBytes()), certSelector.getSerialNumber());
+ }
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("unable to convert issuer: " + e.getMessage());
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertSelectorConverter.java b/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertSelectorConverter.java
new file mode 100644
index 00000000..6dbcef43
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertSelectorConverter.java
@@ -0,0 +1,57 @@
+package org.spongycastle.cert.selector.jcajce;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.X509CertSelector;
+
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.cert.selector.X509CertificateHolderSelector;
+
+public class JcaX509CertSelectorConverter
+{
+ public JcaX509CertSelectorConverter()
+ {
+ }
+
+ protected X509CertSelector doConversion(X500Name issuer, BigInteger serialNumber, byte[] subjectKeyIdentifier)
+ {
+ X509CertSelector selector = new X509CertSelector();
+
+ if (issuer != null)
+ {
+ try
+ {
+ selector.setIssuer(issuer.getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("unable to convert issuer: " + e.getMessage());
+ }
+ }
+
+ if (serialNumber != null)
+ {
+ selector.setSerialNumber(serialNumber);
+ }
+
+ if (subjectKeyIdentifier != null)
+ {
+ try
+ {
+ selector.setSubjectKeyIdentifier(new DEROctetString(subjectKeyIdentifier).getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("unable to convert issuer: " + e.getMessage());
+ }
+ }
+
+ return selector;
+ }
+
+ public X509CertSelector getCertSelector(X509CertificateHolderSelector holderSelector)
+ {
+ return doConversion(holderSelector.getIssuer(), holderSelector.getSerialNumber(), holderSelector.getSubjectKeyIdentifier());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertificateHolderSelector.java b/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertificateHolderSelector.java
new file mode 100644
index 00000000..fd73a9fa
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cert/selector/jcajce/JcaX509CertificateHolderSelector.java
@@ -0,0 +1,72 @@
+package org.spongycastle.cert.selector.jcajce;
+
+import java.math.BigInteger;
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.spongycastle.asn1.ASN1OctetString;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.cert.selector.X509CertificateHolderSelector;
+
+public class JcaX509CertificateHolderSelector
+ extends X509CertificateHolderSelector
+{
+ /**
+ * Construct a signer identifier based on the issuer, serial number and subject key identifier (if present) of the passed in
+ * certificate.
+ *
+ * @param certificate certificate providing the issue and serial number and subject key identifier.
+ */
+ public JcaX509CertificateHolderSelector(X509Certificate certificate)
+ {
+ super(convertPrincipal(certificate.getIssuerX500Principal()), certificate.getSerialNumber(), getSubjectKeyId(certificate));
+ }
+
+ /**
+ * Construct a signer identifier based on the provided issuer and serial number..
+ *
+ * @param issuer the issuer to use.
+ * @param serialNumber the serial number to use.
+ */
+ public JcaX509CertificateHolderSelector(X500Principal issuer, BigInteger serialNumber)
+ {
+ super(convertPrincipal(issuer), serialNumber);
+ }
+
+ /**
+ * Construct a signer identifier based on the provided issuer, serial number, and subjectKeyId..
+ *
+ * @param issuer the issuer to use.
+ * @param serialNumber the serial number to use.
+ * @param subjectKeyId the subject key ID to use.
+ */
+ public JcaX509CertificateHolderSelector(X500Principal issuer, BigInteger serialNumber, byte[] subjectKeyId)
+ {
+ super(convertPrincipal(issuer), serialNumber, subjectKeyId);
+ }
+
+ private static X500Name convertPrincipal(X500Principal issuer)
+ {
+ if (issuer == null)
+ {
+ return null;
+ }
+ return X500Name.getInstance(issuer.getEncoded());
+ }
+
+ private static byte[] getSubjectKeyId(X509Certificate cert)
+ {
+ byte[] ext = cert.getExtensionValue(Extension.subjectKeyIdentifier.getId());
+
+ if (ext != null)
+ {
+ return ASN1OctetString.getInstance(ASN1OctetString.getInstance(ext).getOctets()).getOctets();
+ }
+ else
+ {
+ return null;
+ }
+ }
+}