diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-11-26 11:57:38 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-11-26 11:57:38 +0400 |
commit | e1304ccb8fe1ea47c0fc907c65277ae47c06b4c7 (patch) | |
tree | 35e1fdb382f4f9975d7619ccc049bdc8f5d030a2 /pkix/src | |
parent | 43882cb7727d0b2344ddfaf4a3d7633d299e8b58 (diff) |
pkix updates
Diffstat (limited to 'pkix/src')
3 files changed, 391 insertions, 319 deletions
diff --git a/pkix/src/main/j2me/org/bouncycastle/cms/CMSTypedStream.java b/pkix/src/main/j2me/org/bouncycastle/cms/CMSTypedStream.java deleted file mode 100644 index c05c5952..00000000 --- a/pkix/src/main/j2me/org/bouncycastle/cms/CMSTypedStream.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.bouncycastle.cms; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.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; - } - } -} diff --git a/pkix/src/main/j2me/org/bouncycastle/tsp/TSPUtil.java b/pkix/src/main/j2me/org/bouncycastle/tsp/TSPUtil.java deleted file mode 100644 index 5a8278bf..00000000 --- a/pkix/src/main/j2me/org/bouncycastle/tsp/TSPUtil.java +++ /dev/null @@ -1,234 +0,0 @@ -package org.bouncycastle.tsp; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.OutputStream; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Set; -import org.bouncycastle.asn1.cms.Attribute; -import org.bouncycastle.asn1.cms.AttributeTable; -import org.bouncycastle.asn1.cms.ContentInfo; -import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; -import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; -import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; -import org.bouncycastle.asn1.x509.ExtendedKeyUsage; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.Extensions; -import org.bouncycastle.asn1.x509.ExtensionsGenerator; -import org.bouncycastle.asn1.x509.KeyPurposeId; -import org.bouncycastle.asn1.x509.X509Extensions; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cms.SignerInformation; -import org.bouncycastle.operator.DigestCalculator; -import org.bouncycastle.operator.DigestCalculatorProvider; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Integers; - -public class TSPUtil -{ - private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet()); - private static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList()); - - private static final Map digestLengths = new HashMap(); - private static final Map digestNames = new HashMap(); - - static - { - digestLengths.put(PKCSObjectIdentifiers.md5.getId(), Integers.valueOf(16)); - digestLengths.put(OIWObjectIdentifiers.idSHA1.getId(), Integers.valueOf(20)); - digestLengths.put(NISTObjectIdentifiers.id_sha224.getId(), Integers.valueOf(28)); - digestLengths.put(NISTObjectIdentifiers.id_sha256.getId(), Integers.valueOf(32)); - digestLengths.put(NISTObjectIdentifiers.id_sha384.getId(), Integers.valueOf(48)); - digestLengths.put(NISTObjectIdentifiers.id_sha512.getId(), Integers.valueOf(64)); - digestLengths.put(TeleTrusTObjectIdentifiers.ripemd128.getId(), Integers.valueOf(16)); - digestLengths.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), Integers.valueOf(20)); - digestLengths.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), Integers.valueOf(32)); - digestLengths.put(CryptoProObjectIdentifiers.gostR3411.getId(), Integers.valueOf(32)); - - digestNames.put(PKCSObjectIdentifiers.md5.getId(), "MD5"); - digestNames.put(OIWObjectIdentifiers.idSHA1.getId(), "SHA1"); - digestNames.put(NISTObjectIdentifiers.id_sha224.getId(), "SHA224"); - digestNames.put(NISTObjectIdentifiers.id_sha256.getId(), "SHA256"); - digestNames.put(NISTObjectIdentifiers.id_sha384.getId(), "SHA384"); - digestNames.put(NISTObjectIdentifiers.id_sha512.getId(), "SHA512"); - digestNames.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1"); - digestNames.put(PKCSObjectIdentifiers.sha224WithRSAEncryption.getId(), "SHA224"); - digestNames.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256"); - digestNames.put(PKCSObjectIdentifiers.sha384WithRSAEncryption.getId(), "SHA384"); - digestNames.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512"); - digestNames.put(TeleTrusTObjectIdentifiers.ripemd128.getId(), "RIPEMD128"); - digestNames.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), "RIPEMD160"); - digestNames.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), "RIPEMD256"); - digestNames.put(CryptoProObjectIdentifiers.gostR3411.getId(), "GOST3411"); - } - - /** - * Fetches the signature time-stamp attributes from a SignerInformation object. - * Checks that the MessageImprint for each time-stamp matches the signature field. - * (see RFC 3161 Appendix A). - * - * @param signerInfo a SignerInformation to search for time-stamps - * @param digCalcProvider provider for digest calculators - * @return a collection of TimeStampToken objects - * @throws TSPValidationException - */ - public static Collection getSignatureTimestamps(SignerInformation signerInfo, DigestCalculatorProvider digCalcProvider) - throws TSPValidationException - { - List timestamps = new ArrayList(); - - AttributeTable unsignedAttrs = signerInfo.getUnsignedAttributes(); - if (unsignedAttrs != null) - { - ASN1EncodableVector allTSAttrs = unsignedAttrs.getAll( - PKCSObjectIdentifiers.id_aa_signatureTimeStampToken); - for (int i = 0; i < allTSAttrs.size(); ++i) - { - Attribute tsAttr = (Attribute)allTSAttrs.get(i); - ASN1Set tsAttrValues = tsAttr.getAttrValues(); - for (int j = 0; j < tsAttrValues.size(); ++j) - { - try - { - ContentInfo contentInfo = ContentInfo.getInstance(tsAttrValues.getObjectAt(j)); - TimeStampToken timeStampToken = new TimeStampToken(contentInfo); - TimeStampTokenInfo tstInfo = timeStampToken.getTimeStampInfo(); - - DigestCalculator digCalc = digCalcProvider.get(tstInfo.getHashAlgorithm()); - - OutputStream dOut = digCalc.getOutputStream(); - - dOut.write(signerInfo.getSignature()); - dOut.close(); - - byte[] expectedDigest = digCalc.getDigest(); - - if (!Arrays.constantTimeAreEqual(expectedDigest, tstInfo.getMessageImprintDigest())) - { - throw new TSPValidationException("Incorrect digest in message imprint"); - } - - timestamps.add(timeStampToken); - } - catch (OperatorCreationException e) - { - throw new TSPValidationException("Unknown hash algorithm specified in timestamp"); - } - catch (Exception e) - { - throw new TSPValidationException("Timestamp could not be parsed"); - } - } - } - } - - return timestamps; - } - - /** - * Validate the passed in certificate as being of the correct type to be used - * for time stamping. To be valid it must have an ExtendedKeyUsage extension - * which has a key purpose identifier of id-kp-timeStamping. - * - * @param cert the certificate of interest. - * @throws TSPValidationException if the certicate fails on one of the check points. - */ - public static void validateCertificate( - X509CertificateHolder cert) - throws TSPValidationException - { - if (cert.toASN1Structure().getVersionNumber() != 3) - { - throw new IllegalArgumentException("Certificate must have an ExtendedKeyUsage extension."); - } - - Extension ext = cert.getExtension(Extension.extendedKeyUsage); - if (ext == null) - { - throw new TSPValidationException("Certificate must have an ExtendedKeyUsage extension."); - } - - if (!ext.isCritical()) - { - throw new TSPValidationException("Certificate must have an ExtendedKeyUsage extension marked as critical."); - } - - ExtendedKeyUsage extKey = ExtendedKeyUsage.getInstance(ext.getParsedValue()); - - if (!extKey.hasKeyPurposeId(KeyPurposeId.id_kp_timeStamping) || extKey.size() != 1) - { - throw new TSPValidationException("ExtendedKeyUsage not solely time stamping."); - } - } - - /* - * Return the digest algorithm using one of the standard JCA string - * representations rather than the algorithm identifier (if possible). - */ - static String getDigestAlgName( - String digestAlgOID) - { - String digestName = (String)digestNames.get(digestAlgOID); - - if (digestName != null) - { - return digestName; - } - - return digestAlgOID; - } - - static int getDigestLength( - String digestAlgOID) - throws TSPException - { - Integer length = (Integer)digestLengths.get(digestAlgOID); - - if (length != null) - { - return length.intValue(); - } - - throw new TSPException("digest algorithm cannot be found."); - } - - static List getExtensionOIDs(Extensions extensions) - { - if (extensions == null) - { - return EMPTY_LIST; - } - - return Collections.unmodifiableList(java.util.Arrays.asList(extensions.getExtensionOIDs())); - } - - static void addExtension(ExtensionsGenerator extGenerator, ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value) - throws TSPIOException - { - try - { - extGenerator.addExtension(oid, isCritical, value); - } - catch (IOException e) - { - throw new TSPIOException("cannot encode extension: " + e.getMessage(), e); - } - } -} diff --git a/pkix/src/main/j2me/org/bouncycastle/tsp/TimeStampToken.java b/pkix/src/main/j2me/org/bouncycastle/tsp/TimeStampToken.java new file mode 100644 index 00000000..3cd7f0a9 --- /dev/null +++ b/pkix/src/main/j2me/org/bouncycastle/tsp/TimeStampToken.java @@ -0,0 +1,391 @@ +package org.bouncycastle.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.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.cms.Attribute; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.ess.ESSCertID; +import org.bouncycastle.asn1.ess.ESSCertIDv2; +import org.bouncycastle.asn1.ess.SigningCertificate; +import org.bouncycastle.asn1.ess.SigningCertificateV2; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.tsp.TSTInfo; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.IssuerSerial; +import org.bouncycastle.asn1.x509.X509Name; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSProcessable; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.SignerId; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.SignerInformationVerifier; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.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(); + } + } + } +} |