diff options
Diffstat (limited to 'pkix/src/main/j2me/org/spongycastle/tsp')
5 files changed, 996 insertions, 0 deletions
diff --git a/pkix/src/main/j2me/org/spongycastle/tsp/TimeStampToken.java b/pkix/src/main/j2me/org/spongycastle/tsp/TimeStampToken.java new file mode 100644 index 00000000..d51a07fe --- /dev/null +++ b/pkix/src/main/j2me/org/spongycastle/tsp/TimeStampToken.java @@ -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(); + } + } + } +} diff --git a/pkix/src/main/j2me/org/spongycastle/tsp/TimeStampTokenInfo.java b/pkix/src/main/j2me/org/spongycastle/tsp/TimeStampTokenInfo.java new file mode 100644 index 00000000..0b3b3352 --- /dev/null +++ b/pkix/src/main/j2me/org/spongycastle/tsp/TimeStampTokenInfo.java @@ -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; + } +} diff --git a/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedData.java b/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedData.java new file mode 100644 index 00000000..4d9c1653 --- /dev/null +++ b/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedData.java @@ -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(); + } +} diff --git a/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedDataParser.java b/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedDataParser.java new file mode 100644 index 00000000..f6439633 --- /dev/null +++ b/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedDataParser.java @@ -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); + } + } +} diff --git a/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedGenerator.java b/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedGenerator.java new file mode 100644 index 00000000..ca6190de --- /dev/null +++ b/pkix/src/main/j2me/org/spongycastle/tsp/cms/CMSTimeStampedGenerator.java @@ -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); + } +} |