Add spongy castle sources to libraries folder
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.spongycastle.cert.ocsp;
|
||||
|
||||
public interface CertificateStatus
|
||||
{
|
||||
public static final CertificateStatus GOOD = null;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user