Add spongy castle sources to libraries folder

This commit is contained in:
Dominik Schürmann
2014-01-27 14:00:22 +01:00
parent 8ca42b9bf9
commit 5aec25ac05
4258 changed files with 848014 additions and 0 deletions

View File

@@ -0,0 +1,246 @@
package org.spongycastle.cert;
import java.io.IOException;
import java.io.OutputStream;
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.ASN1ObjectIdentifier;
import org.spongycastle.asn1.DERBitString;
import org.spongycastle.asn1.DERGeneralizedTime;
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(DERGeneralizedTime time)
{
return time.getDate();
}
static boolean dateBefore(Date d1, Date d2)
{
return d1.getTime() < d2.getTime();
}
static boolean dateAfter(Date d1, Date d2)
{
return d1.getTime() > d2.getTime();
}
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());
}
}

View File

@@ -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 !CertUtils.dateBefore(date, CertUtils.recoverDate(certValidityPeriod.getNotBeforeTime())) && !CertUtils.dateAfter(date, 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();
}
}

View File

@@ -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 !CertUtils.dateBefore(date, x509Certificate.getStartDate().getDate()) && !CertUtils.dateAfter(date, 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();
}
}

View File

@@ -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 IllegalStateException("unsupported operation");
}
if (value.getValueHint() != null)
{
throw new IllegalStateException("unsupported operation");
}
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();
}
}

View File

@@ -0,0 +1,120 @@
package org.spongycastle.cert.crmf;
import java.security.SecureRandom;
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 FixedLengthMGF1Padder
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 FixedLengthMGF1Padder(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 FixedLengthMGF1Padder(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() & 0xff));
}
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;
}
}

View File

@@ -0,0 +1,85 @@
package org.spongycastle.cms;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.spongycastle.util.io.Streams;
public class CMSTypedStream
{
private static final int BUF_SIZ = 32 * 1024;
private final ASN1ObjectIdentifier _oid;
private final InputStream _in;
public CMSTypedStream(
InputStream in)
{
this(PKCSObjectIdentifiers.data.getId(), in, BUF_SIZ);
}
public CMSTypedStream(
String oid,
InputStream in)
{
this(new ASN1ObjectIdentifier(oid), in, BUF_SIZ);
}
public CMSTypedStream(
String oid,
InputStream in,
int bufSize)
{
this(new ASN1ObjectIdentifier(oid), in, bufSize);
}
public CMSTypedStream(
ASN1ObjectIdentifier oid,
InputStream in)
{
this(oid, in, BUF_SIZ);
}
public CMSTypedStream(
ASN1ObjectIdentifier oid,
InputStream in,
int bufSize)
{
_oid = oid;
_in = new FullReaderStream(in);
}
public ASN1ObjectIdentifier getContentType()
{
return _oid;
}
public InputStream getContentStream()
{
return _in;
}
public void drain()
throws IOException
{
Streams.drain(_in);
_in.close();
}
private static class FullReaderStream extends FilterInputStream
{
FullReaderStream(InputStream in)
{
super(in);
}
public int read(byte[] buf, int off, int len) throws IOException
{
int totalRead = Streams.readFully(super.in, buf, off, len);
return totalRead > 0 ? totalRead : -1;
}
}
}

View File

@@ -0,0 +1,16 @@
package org.spongycastle.operator.bc;
import org.spongycastle.operator.GenericKey;
class OperatorUtils
{
static byte[] getKeyBytes(GenericKey key)
{
if (key.getRepresentation() instanceof byte[])
{
return (byte[])key.getRepresentation();
}
throw new IllegalArgumentException("unknown generic key type");
}
}

View File

@@ -0,0 +1,391 @@
package org.spongycastle.tsp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Date;
import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.cms.Attribute;
import org.spongycastle.asn1.cms.AttributeTable;
import org.spongycastle.asn1.cms.ContentInfo;
import org.spongycastle.asn1.cms.IssuerAndSerialNumber;
import org.spongycastle.asn1.ess.ESSCertID;
import org.spongycastle.asn1.ess.ESSCertIDv2;
import org.spongycastle.asn1.ess.SigningCertificate;
import org.spongycastle.asn1.ess.SigningCertificateV2;
import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.spongycastle.asn1.tsp.TSTInfo;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
import org.spongycastle.asn1.x509.GeneralName;
import org.spongycastle.asn1.x509.IssuerSerial;
import org.spongycastle.asn1.x509.X509Name;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cms.CMSException;
import org.spongycastle.cms.CMSProcessable;
import org.spongycastle.cms.CMSSignedData;
import org.spongycastle.cms.SignerId;
import org.spongycastle.cms.SignerInformation;
import org.spongycastle.cms.SignerInformationVerifier;
import org.spongycastle.operator.DigestCalculator;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.util.Arrays;
import org.spongycastle.util.Store;
public class TimeStampToken
{
CMSSignedData tsToken;
SignerInformation tsaSignerInfo;
Date genTime;
TimeStampTokenInfo tstInfo;
CertID certID;
public TimeStampToken(ContentInfo contentInfo)
throws TSPException, IOException
{
this(getSignedData(contentInfo));
}
private static CMSSignedData getSignedData(ContentInfo contentInfo)
throws TSPException
{
try
{
return new CMSSignedData(contentInfo);
}
catch (CMSException e)
{
throw new TSPException("TSP parsing error: " + e.getMessage(), e.getCause());
}
}
public TimeStampToken(CMSSignedData signedData)
throws TSPException, IOException
{
this.tsToken = signedData;
if (!this.tsToken.getSignedContentTypeOID().equals(PKCSObjectIdentifiers.id_ct_TSTInfo.getId()))
{
throw new TSPValidationException("ContentInfo object not for a time stamp.");
}
Collection signers = tsToken.getSignerInfos().getSigners();
if (signers.size() != 1)
{
throw new IllegalArgumentException("Time-stamp token signed by "
+ signers.size()
+ " signers, but it must contain just the TSA signature.");
}
tsaSignerInfo = (SignerInformation)signers.iterator().next();
try
{
CMSProcessable content = tsToken.getSignedContent();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
content.write(bOut);
ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bOut.toByteArray()));
this.tstInfo = new TimeStampTokenInfo(TSTInfo.getInstance(aIn.readObject()));
Attribute attr = tsaSignerInfo.getSignedAttributes().get(PKCSObjectIdentifiers.id_aa_signingCertificate);
if (attr != null)
{
SigningCertificate signCert = SigningCertificate.getInstance(attr.getAttrValues().getObjectAt(0));
this.certID = new CertID(ESSCertID.getInstance(signCert.getCerts()[0]));
}
else
{
attr = tsaSignerInfo.getSignedAttributes().get(PKCSObjectIdentifiers.id_aa_signingCertificateV2);
if (attr == null)
{
throw new TSPValidationException("no signing certificate attribute found, time stamp invalid.");
}
SigningCertificateV2 signCertV2 = SigningCertificateV2.getInstance(attr.getAttrValues().getObjectAt(0));
this.certID = new CertID(ESSCertIDv2.getInstance(signCertV2.getCerts()[0]));
}
}
catch (CMSException e)
{
throw new TSPException(e.getMessage(), e.getUnderlyingException());
}
}
public TimeStampTokenInfo getTimeStampInfo()
{
return tstInfo;
}
public SignerId getSID()
{
return tsaSignerInfo.getSID();
}
public AttributeTable getSignedAttributes()
{
return tsaSignerInfo.getSignedAttributes();
}
public AttributeTable getUnsignedAttributes()
{
return tsaSignerInfo.getUnsignedAttributes();
}
public Store getCertificates()
{
return tsToken.getCertificates();
}
public Store getCRLs()
{
return tsToken.getCRLs();
}
public Store getAttributeCertificates()
{
return tsToken.getAttributeCertificates();
}
/**
* Validate the time stamp token.
* <p>
* To be valid the token must be signed by the passed in certificate and
* the certificate must be the one referred to by the SigningCertificate
* attribute included in the hashed attributes of the token. The
* certificate must also have the ExtendedKeyUsageExtension with only
* KeyPurposeId.id_kp_timeStamping and have been valid at the time the
* timestamp was created.
* </p>
* <p>
* A successful call to validate means all the above are true.
* </p>
*
* @param sigVerifier the content verifier create the objects required to verify the CMS object in the timestamp.
* @throws TSPException if an exception occurs in processing the token.
* @throws TSPValidationException if the certificate or signature fail to be valid.
* @throws IllegalArgumentException if the sigVerifierProvider has no associated certificate.
*/
public void validate(
SignerInformationVerifier sigVerifier)
throws TSPException, TSPValidationException
{
if (!sigVerifier.hasAssociatedCertificate())
{
throw new IllegalArgumentException("verifier provider needs an associated certificate");
}
try
{
X509CertificateHolder certHolder = sigVerifier.getAssociatedCertificate();
DigestCalculator calc = sigVerifier.getDigestCalculator(certID.getHashAlgorithm());
OutputStream cOut = calc.getOutputStream();
cOut.write(certHolder.getEncoded());
cOut.close();
if (!Arrays.constantTimeAreEqual(certID.getCertHash(), calc.getDigest()))
{
throw new TSPValidationException("certificate hash does not match certID hash.");
}
if (certID.getIssuerSerial() != null)
{
IssuerAndSerialNumber issuerSerial = new IssuerAndSerialNumber(certHolder.toASN1Structure());
if (!certID.getIssuerSerial().getSerial().equals(issuerSerial.getSerialNumber()))
{
throw new TSPValidationException("certificate serial number does not match certID for signature.");
}
GeneralName[] names = certID.getIssuerSerial().getIssuer().getNames();
boolean found = false;
for (int i = 0; i != names.length; i++)
{
if (names[i].getTagNo() == 4 && X500Name.getInstance(names[i].getName()).equals(X500Name.getInstance(issuerSerial.getName())))
{
found = true;
break;
}
}
if (!found)
{
throw new TSPValidationException("certificate name does not match certID for signature. ");
}
}
TSPUtil.validateCertificate(certHolder);
if (!certHolder.isValidOn(tstInfo.getGenTime()))
{
throw new TSPValidationException("certificate not valid when time stamp created.");
}
if (!tsaSignerInfo.verify(sigVerifier))
{
throw new TSPValidationException("signature not created by certificate.");
}
}
catch (CMSException e)
{
if (e.getUnderlyingException() != null)
{
throw new TSPException(e.getMessage(), e.getUnderlyingException());
}
else
{
throw new TSPException("CMS exception: " + e, e);
}
}
catch (IOException e)
{
throw new TSPException("problem processing certificate: " + e, e);
}
catch (OperatorCreationException e)
{
throw new TSPException("unable to create digest: " + e.getMessage(), e);
}
}
/**
* Return true if the signature on time stamp token is valid.
* <p>
* Note: this is a much weaker proof of correctness than calling validate().
* </p>
*
* @param sigVerifier the content verifier create the objects required to verify the CMS object in the timestamp.
* @return true if the signature matches, false otherwise.
* @throws TSPException if the signature cannot be processed or the provider cannot match the algorithm.
*/
public boolean isSignatureValid(
SignerInformationVerifier sigVerifier)
throws TSPException
{
try
{
return tsaSignerInfo.verify(sigVerifier);
}
catch (CMSException e)
{
if (e.getUnderlyingException() != null)
{
throw new TSPException(e.getMessage(), e.getUnderlyingException());
}
else
{
throw new TSPException("CMS exception: " + e, e);
}
}
}
/**
* Return the underlying CMSSignedData object.
*
* @return the underlying CMS structure.
*/
public CMSSignedData toCMSSignedData()
{
return tsToken;
}
/**
* Return a ASN.1 encoded byte stream representing the encoded object.
*
* @throws IOException if encoding fails.
*/
public byte[] getEncoded()
throws IOException
{
return tsToken.getEncoded();
}
// perhaps this should be done using an interface on the ASN.1 classes...
private class CertID
{
private ESSCertID certID;
private ESSCertIDv2 certIDv2;
CertID(ESSCertID certID)
{
this.certID = certID;
this.certIDv2 = null;
}
CertID(ESSCertIDv2 certID)
{
this.certIDv2 = certID;
this.certID = null;
}
public String getHashAlgorithmName()
{
if (certID != null)
{
return "SHA-1";
}
else
{
if (NISTObjectIdentifiers.id_sha256.equals(certIDv2.getHashAlgorithm().getAlgorithm()))
{
return "SHA-256";
}
return certIDv2.getHashAlgorithm().getAlgorithm().getId();
}
}
public AlgorithmIdentifier getHashAlgorithm()
{
if (certID != null)
{
return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1);
}
else
{
return certIDv2.getHashAlgorithm();
}
}
public byte[] getCertHash()
{
if (certID != null)
{
return certID.getCertHash();
}
else
{
return certIDv2.getCertHash();
}
}
public IssuerSerial getIssuerSerial()
{
if (certID != null)
{
return certID.getIssuerSerial();
}
else
{
return certIDv2.getIssuerSerial();
}
}
}
}

View File

@@ -0,0 +1,112 @@
package org.spongycastle.tsp;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Date;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.tsp.Accuracy;
import org.spongycastle.asn1.tsp.TSTInfo;
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
import org.spongycastle.asn1.x509.GeneralName;
public class TimeStampTokenInfo
{
TSTInfo tstInfo;
Date genTime;
TimeStampTokenInfo(TSTInfo tstInfo)
throws TSPException, IOException
{
this.tstInfo = tstInfo;
this.genTime = tstInfo.getGenTime().getDate();
}
public boolean isOrdered()
{
return tstInfo.getOrdering().isTrue();
}
public Accuracy getAccuracy()
{
return tstInfo.getAccuracy();
}
public Date getGenTime()
{
return genTime;
}
public GenTimeAccuracy getGenTimeAccuracy()
{
if (this.getAccuracy() != null)
{
return new GenTimeAccuracy(this.getAccuracy());
}
return null;
}
public ASN1ObjectIdentifier getPolicy()
{
return tstInfo.getPolicy();
}
public BigInteger getSerialNumber()
{
return tstInfo.getSerialNumber().getValue();
}
public GeneralName getTsa()
{
return tstInfo.getTsa();
}
/**
* @return the nonce value, null if there isn't one.
*/
public BigInteger getNonce()
{
if (tstInfo.getNonce() != null)
{
return tstInfo.getNonce().getValue();
}
return null;
}
public AlgorithmIdentifier getHashAlgorithm()
{
return tstInfo.getMessageImprint().getHashAlgorithm();
}
public ASN1ObjectIdentifier getMessageImprintAlgOID()
{
return tstInfo.getMessageImprint().getHashAlgorithm().getAlgorithm();
}
public byte[] getMessageImprintDigest()
{
return tstInfo.getMessageImprint().getHashedMessage();
}
public byte[] getEncoded()
throws IOException
{
return tstInfo.getEncoded();
}
/**
* @deprecated use toASN1Structure
* @return
*/
public TSTInfo toTSTInfo()
{
return tstInfo;
}
public TSTInfo toASN1Structure()
{
return tstInfo;
}
}

View File

@@ -0,0 +1,201 @@
package org.spongycastle.tsp.cms;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.DERIA5String;
import org.spongycastle.asn1.cms.AttributeTable;
import org.spongycastle.asn1.cms.CMSObjectIdentifiers;
import org.spongycastle.asn1.cms.ContentInfo;
import org.spongycastle.asn1.cms.Evidence;
import org.spongycastle.asn1.cms.TimeStampAndCRL;
import org.spongycastle.asn1.cms.TimeStampTokenEvidence;
import org.spongycastle.asn1.cms.TimeStampedData;
import org.spongycastle.cms.CMSException;
import org.spongycastle.operator.DigestCalculator;
import org.spongycastle.operator.DigestCalculatorProvider;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.tsp.TimeStampToken;
public class CMSTimeStampedData
{
private TimeStampedData timeStampedData;
private ContentInfo contentInfo;
private TimeStampDataUtil util;
public CMSTimeStampedData(ContentInfo contentInfo)
{
this.initialize(contentInfo);
}
public CMSTimeStampedData(InputStream in)
throws IOException
{
try
{
initialize(ContentInfo.getInstance(new ASN1InputStream(in).readObject()));
}
catch (ClassCastException e)
{
throw new IOException("Malformed content: " + e);
}
catch (IllegalArgumentException e)
{
throw new IOException("Malformed content: " + e);
}
}
public CMSTimeStampedData(byte[] baseData)
throws IOException
{
this(new ByteArrayInputStream(baseData));
}
private void initialize(ContentInfo contentInfo)
{
this.contentInfo = contentInfo;
if (CMSObjectIdentifiers.timestampedData.equals(contentInfo.getContentType()))
{
this.timeStampedData = TimeStampedData.getInstance(contentInfo.getContent());
}
else
{
throw new IllegalArgumentException("Malformed content - type must be " + CMSObjectIdentifiers.timestampedData.getId());
}
util = new TimeStampDataUtil(this.timeStampedData);
}
public byte[] calculateNextHash(DigestCalculator calculator)
throws CMSException
{
return util.calculateNextHash(calculator);
}
/**
* Return a new timeStampedData object with the additional token attached.
*
* @throws CMSException
*/
public CMSTimeStampedData addTimeStamp(TimeStampToken token)
throws CMSException
{
TimeStampAndCRL[] timeStamps = util.getTimeStamps();
TimeStampAndCRL[] newTimeStamps = new TimeStampAndCRL[timeStamps.length + 1];
System.arraycopy(timeStamps, 0, newTimeStamps, 0, timeStamps.length);
newTimeStamps[timeStamps.length] = new TimeStampAndCRL(token.toCMSSignedData().toASN1Structure());
return new CMSTimeStampedData(new ContentInfo(CMSObjectIdentifiers.timestampedData, new TimeStampedData(timeStampedData.getDataUri(), timeStampedData.getMetaData(), timeStampedData.getContent(), new Evidence(new TimeStampTokenEvidence(newTimeStamps)))));
}
public byte[] getContent()
{
if (timeStampedData.getContent() != null)
{
return timeStampedData.getContent().getOctets();
}
return null;
}
public String getDataUri()
{
DERIA5String dataURI = this.timeStampedData.getDataUri();
if (dataURI != null)
{
return dataURI.getString();
}
return null;
}
public String getFileName()
{
return util.getFileName();
}
public String getMediaType()
{
return util.getMediaType();
}
public AttributeTable getOtherMetaData()
{
return util.getOtherMetaData();
}
public TimeStampToken[] getTimeStampTokens()
throws CMSException
{
return util.getTimeStampTokens();
}
/**
* Initialise the passed in calculator with the MetaData for this message, if it is
* required as part of the initial message imprint calculation.
*
* @param calculator the digest calculator to be initialised.
* @throws CMSException if the MetaData is required and cannot be processed
*/
public void initialiseMessageImprintDigestCalculator(DigestCalculator calculator)
throws CMSException
{
util.initialiseMessageImprintDigestCalculator(calculator);
}
/**
* Returns an appropriately initialised digest calculator based on the message imprint algorithm
* described in the first time stamp in the TemporalData for this message. If the metadata is required
* to be included in the digest calculation, the returned calculator will be pre-initialised.
*
* @param calculatorProvider a provider of DigestCalculator objects.
* @return an initialised digest calculator.
* @throws OperatorCreationException if the provider is unable to create the calculator.
*/
public DigestCalculator getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider)
throws OperatorCreationException
{
return util.getMessageImprintDigestCalculator(calculatorProvider);
}
/**
* Validate the digests present in the TimeStampTokens contained in the CMSTimeStampedData.
*
* @param calculatorProvider provider for digest calculators
* @param dataDigest the calculated data digest for the message
* @throws ImprintDigestInvalidException if an imprint digest fails to compare
* @throws CMSException if an exception occurs processing the message.
*/
public void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest)
throws ImprintDigestInvalidException, CMSException
{
util.validate(calculatorProvider, dataDigest);
}
/**
* Validate the passed in timestamp token against the tokens and data present in the message.
*
* @param calculatorProvider provider for digest calculators
* @param dataDigest the calculated data digest for the message.
* @param timeStampToken the timestamp token of interest.
* @throws ImprintDigestInvalidException if the token is not present in the message, or an imprint digest fails to compare.
* @throws CMSException if an exception occurs processing the message.
*/
public void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken)
throws ImprintDigestInvalidException, CMSException
{
util.validate(calculatorProvider, dataDigest, timeStampToken);
}
public byte[] getEncoded()
throws IOException
{
return contentInfo.getEncoded();
}
}

View File

@@ -0,0 +1,204 @@
package org.spongycastle.tsp.cms;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.spongycastle.asn1.BERTags;
import org.spongycastle.asn1.DERIA5String;
import org.spongycastle.asn1.cms.AttributeTable;
import org.spongycastle.asn1.cms.CMSObjectIdentifiers;
import org.spongycastle.asn1.cms.ContentInfoParser;
import org.spongycastle.asn1.cms.TimeStampedDataParser;
import org.spongycastle.cms.CMSContentInfoParser;
import org.spongycastle.cms.CMSException;
import org.spongycastle.operator.DigestCalculator;
import org.spongycastle.operator.DigestCalculatorProvider;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.tsp.TimeStampToken;
import org.spongycastle.util.io.Streams;
public class CMSTimeStampedDataParser
extends CMSContentInfoParser
{
private TimeStampedDataParser timeStampedData;
private TimeStampDataUtil util;
public CMSTimeStampedDataParser(InputStream in)
throws CMSException
{
super(in);
initialize(_contentInfo);
}
public CMSTimeStampedDataParser(byte[] baseData)
throws CMSException
{
this(new ByteArrayInputStream(baseData));
}
private void initialize(ContentInfoParser contentInfo)
throws CMSException
{
try
{
if (CMSObjectIdentifiers.timestampedData.equals(contentInfo.getContentType()))
{
this.timeStampedData = TimeStampedDataParser.getInstance(contentInfo.getContent(BERTags.SEQUENCE));
}
else
{
throw new IllegalArgumentException("Malformed content - type must be " + CMSObjectIdentifiers.timestampedData.getId());
}
}
catch (IOException e)
{
throw new CMSException("parsing exception: " + e.getMessage(), e);
}
}
public byte[] calculateNextHash(DigestCalculator calculator)
throws CMSException
{
return util.calculateNextHash(calculator);
}
public InputStream getContent()
{
if (timeStampedData.getContent() != null)
{
return timeStampedData.getContent().getOctetStream();
}
return null;
}
public String getDataUri()
{
DERIA5String dataURI = this.timeStampedData.getDataUri();
if (dataURI != null)
{
return dataURI.getString();
}
return null;
}
public String getFileName()
{
return util.getFileName();
}
public String getMediaType()
{
return util.getMediaType();
}
public AttributeTable getOtherMetaData()
{
return util.getOtherMetaData();
}
/**
* Initialise the passed in calculator with the MetaData for this message, if it is
* required as part of the initial message imprint calculation.
*
* @param calculator the digest calculator to be initialised.
* @throws CMSException if the MetaData is required and cannot be processed
*/
public void initialiseMessageImprintDigestCalculator(DigestCalculator calculator)
throws CMSException
{
util.initialiseMessageImprintDigestCalculator(calculator);
}
/**
* Returns an appropriately initialised digest calculator based on the message imprint algorithm
* described in the first time stamp in the TemporalData for this message. If the metadata is required
* to be included in the digest calculation, the returned calculator will be pre-initialised.
*
* @param calculatorProvider a provider of DigestCalculator objects.
* @return an initialised digest calculator.
* @throws OperatorCreationException if the provider is unable to create the calculator.
*/
public DigestCalculator getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider)
throws OperatorCreationException
{
try
{
parseTimeStamps();
}
catch (CMSException e)
{
throw new OperatorCreationException("unable to extract algorithm ID: " + e.getMessage(), e);
}
return util.getMessageImprintDigestCalculator(calculatorProvider);
}
public TimeStampToken[] getTimeStampTokens()
throws CMSException
{
parseTimeStamps();
return util.getTimeStampTokens();
}
/**
* Validate the digests present in the TimeStampTokens contained in the CMSTimeStampedData.
*
* @param calculatorProvider provider for digest calculators
* @param dataDigest the calculated data digest for the message
* @throws ImprintDigestInvalidException if an imprint digest fails to compare
* @throws CMSException if an exception occurs processing the message.
*/
public void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest)
throws ImprintDigestInvalidException, CMSException
{
parseTimeStamps();
util.validate(calculatorProvider, dataDigest);
}
/**
* Validate the passed in timestamp token against the tokens and data present in the message.
*
* @param calculatorProvider provider for digest calculators
* @param dataDigest the calculated data digest for the message.
* @param timeStampToken the timestamp token of interest.
* @throws ImprintDigestInvalidException if the token is not present in the message, or an imprint digest fails to compare.
* @throws CMSException if an exception occurs processing the message.
*/
public void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken)
throws ImprintDigestInvalidException, CMSException
{
parseTimeStamps();
util.validate(calculatorProvider, dataDigest, timeStampToken);
}
private void parseTimeStamps()
throws CMSException
{
try
{
if (util == null)
{
InputStream cont = this.getContent();
if (cont != null)
{
Streams.drain(cont);
}
util = new TimeStampDataUtil(timeStampedData);
}
}
catch (IOException e)
{
throw new CMSException("unable to parse evidence block: " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,88 @@
package org.spongycastle.tsp.cms;
import org.spongycastle.asn1.ASN1Boolean;
import org.spongycastle.asn1.DERBoolean;
import org.spongycastle.asn1.DERIA5String;
import org.spongycastle.asn1.DERUTF8String;
import org.spongycastle.asn1.cms.Attributes;
import org.spongycastle.asn1.cms.MetaData;
import org.spongycastle.cms.CMSException;
import org.spongycastle.operator.DigestCalculator;
import org.spongycastle.util.Integers;
public class CMSTimeStampedGenerator
{
protected MetaData metaData;
protected String dataUri;
/**
* Set the dataURI to be included in message.
*
* @param dataUri URI for the data the initial message imprint digest is based on.
*/
public void setDataUri(String dataUri)
{
this.dataUri = dataUri;
}
/**
* Set the MetaData for the generated message.
*
* @param hashProtected true if the MetaData should be included in first imprint calculation, false otherwise.
* @param fileName optional file name, may be null.
* @param mediaType optional media type, may be null.
*/
public void setMetaData(boolean hashProtected, String fileName, String mediaType)
{
setMetaData(hashProtected, fileName, mediaType, null);
}
/**
* Set the MetaData for the generated message.
*
* @param hashProtected true if the MetaData should be included in first imprint calculation, false otherwise.
* @param fileName optional file name, may be null.
* @param mediaType optional media type, may be null.
* @param attributes optional attributes, may be null.
*/
public void setMetaData(boolean hashProtected, String fileName, String mediaType, Attributes attributes)
{
DERUTF8String asn1FileName = null;
if (fileName != null)
{
asn1FileName = new DERUTF8String(fileName);
}
DERIA5String asn1MediaType = null;
if (mediaType != null)
{
asn1MediaType = new DERIA5String(mediaType);
}
setMetaData(hashProtected, asn1FileName, asn1MediaType, attributes);
}
private void setMetaData(boolean hashProtected, DERUTF8String fileName, DERIA5String mediaType, Attributes attributes)
{
this.metaData = new MetaData(ASN1Boolean.getInstance(hashProtected), fileName, mediaType, attributes);
}
/**
* Initialise the passed in calculator with the MetaData for this message, if it is
* required as part of the initial message imprint calculation. After initialisation the
* calculator can then be used to calculate the initial message imprint digest for the first
* timestamp.
*
* @param calculator the digest calculator to be initialised.
* @throws CMSException if the MetaData is required and cannot be processed
*/
public void initialiseMessageImprintDigestCalculator(DigestCalculator calculator)
throws CMSException
{
MetaDataUtil util = new MetaDataUtil(metaData);
util.initialiseMessageImprintDigestCalculator(calculator);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,126 @@
package org.spongycastle.cert;
import java.io.IOException;
import java.io.OutputStream;
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));
}
/**
* 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();
}
}

View File

@@ -0,0 +1,66 @@
package org.spongycastle.cert;
import java.math.BigInteger;
import java.util.Date;
import org.spongycastle.asn1.ASN1Integer;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
import org.spongycastle.asn1.x509.Time;
import org.spongycastle.asn1.x509.V1TBSCertificateGenerator;
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)
{
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(new Time(notBefore));
tbsGen.setEndDate(new Time(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());
}
}

View File

@@ -0,0 +1,109 @@
package org.spongycastle.cert;
import java.math.BigInteger;
import java.util.Date;
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;
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));
}
/**
* 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;
}
/**
* 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());
}
}

View File

@@ -0,0 +1,182 @@
package org.spongycastle.cert;
import java.math.BigInteger;
import java.util.Date;
import java.util.Enumeration;
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));
}
/**
* 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)
{
tbsGen.setNextUpdate(new Time(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;
}
/**
* 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());
}
}

View File

@@ -0,0 +1,142 @@
package org.spongycastle.cert;
import java.math.BigInteger;
import java.util.Date;
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)
{
tbsGen = new V3TBSCertificateGenerator();
tbsGen.setSerialNumber(new ASN1Integer(serial));
tbsGen.setIssuer(issuer);
tbsGen.setStartDate(new Time(notBefore));
tbsGen.setEndDate(new Time(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)
* 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());
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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)));
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,120 @@
package org.spongycastle.cert.crmf;
import java.security.SecureRandom;
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 FixedLengthMGF1Padder
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 FixedLengthMGF1Padder(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 FixedLengthMGF1Padder(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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)));
}
}

View File

@@ -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);
}
}
};
}
}

View File

@@ -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()));
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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.JcaJceHelper;
import org.spongycastle.jcajce.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;
}
}

View File

@@ -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.DefaultJcaJceHelper;
import org.spongycastle.jcajce.NamedJcaJceHelper;
import org.spongycastle.jcajce.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;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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.DefaultJcaJceHelper;
import org.spongycastle.jcajce.NamedJcaJceHelper;
import org.spongycastle.jcajce.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);
}
};
}
}

View File

@@ -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.DefaultJcaJceHelper;
import org.spongycastle.jcajce.NamedJcaJceHelper;
import org.spongycastle.jcajce.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);
}
}
}

View File

@@ -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.DefaultJcaJceHelper;
import org.spongycastle.jcajce.NamedJcaJceHelper;
import org.spongycastle.jcajce.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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,129 @@
package org.spongycastle.cert.jcajce;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import org.spongycastle.asn1.ASN1OctetString;
import org.spongycastle.asn1.ASN1Primitive;
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.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()));
}
/**
* 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;
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,103 @@
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.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 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
DERGeneralizedTime thisUpdate;
DERGeneralizedTime 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));
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,6 @@
package org.spongycastle.cert.ocsp;
public interface CertificateStatus
{
public static final CertificateStatus GOOD = null;
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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()));
}
}

View File

@@ -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();
}
}

Some files were not shown because too many files have changed in this diff Show More