Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/cms/CMSSignedDataParser.java')
-rw-r--r--pkix/src/main/java/org/spongycastle/cms/CMSSignedDataParser.java624
1 files changed, 624 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/cms/CMSSignedDataParser.java b/pkix/src/main/java/org/spongycastle/cms/CMSSignedDataParser.java
new file mode 100644
index 00000000..89edf873
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/cms/CMSSignedDataParser.java
@@ -0,0 +1,624 @@
+package org.spongycastle.cms;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.spongycastle.asn1.ASN1Encodable;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Generator;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1OctetStringParser;
+import org.spongycastle.asn1.ASN1SequenceParser;
+import org.spongycastle.asn1.ASN1Set;
+import org.spongycastle.asn1.ASN1SetParser;
+import org.spongycastle.asn1.ASN1StreamParser;
+import org.spongycastle.asn1.BERSequenceGenerator;
+import org.spongycastle.asn1.BERSetParser;
+import org.spongycastle.asn1.BERTaggedObject;
+import org.spongycastle.asn1.BERTags;
+import org.spongycastle.asn1.DERSet;
+import org.spongycastle.asn1.DERTaggedObject;
+import org.spongycastle.asn1.cms.CMSObjectIdentifiers;
+import org.spongycastle.asn1.cms.ContentInfoParser;
+import org.spongycastle.asn1.cms.SignedDataParser;
+import org.spongycastle.asn1.cms.SignerInfo;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.operator.DigestCalculator;
+import org.spongycastle.operator.DigestCalculatorProvider;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.util.Store;
+import org.spongycastle.util.io.Streams;
+
+/**
+ * Parsing class for an CMS Signed Data object from an input stream.
+ * <p>
+ * Note: that because we are in a streaming mode only one signer can be tried and it is important
+ * that the methods on the parser are called in the appropriate order.
+ * </p>
+ * <p>
+ * A simple example of usage for an encapsulated signature.
+ * </p>
+ * <p>
+ * Two notes: first, in the example below the validity of
+ * the certificate isn't verified, just the fact that one of the certs
+ * matches the given signer, and, second, because we are in a streaming
+ * mode the order of the operations is important.
+ * </p>
+ * <pre>
+ * CMSSignedDataParser sp = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider("SC").build(), encapSigData);
+ *
+ * sp.getSignedContent().drain();
+ *
+ * Store certStore = sp.getCertificates();
+ * SignerInformationStore signers = sp.getSignerInfos();
+ *
+ * Collection c = signers.getSigners();
+ * Iterator it = c.iterator();
+ *
+ * while (it.hasNext())
+ * {
+ * SignerInformation signer = (SignerInformation)it.next();
+ * Collection certCollection = certStore.getMatches(signer.getSID());
+ *
+ * Iterator certIt = certCollection.iterator();
+ * X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
+ *
+ * System.out.println("verify returns: " + signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("SC").build(cert)));
+ * }
+ * </pre>
+ * Note also: this class does not introduce buffering - if you are processing large files you should create
+ * the parser with:
+ * <pre>
+ * CMSSignedDataParser ep = new CMSSignedDataParser(new BufferedInputStream(encapSigData, bufSize));
+ * </pre>
+ * where bufSize is a suitably large buffer size.
+ */
+public class CMSSignedDataParser
+ extends CMSContentInfoParser
+{
+ private static final CMSSignedHelper HELPER = CMSSignedHelper.INSTANCE;
+
+ private SignedDataParser _signedData;
+ private ASN1ObjectIdentifier _signedContentType;
+ private CMSTypedStream _signedContent;
+ private Map digests;
+
+ private SignerInformationStore _signerInfoStore;
+ private ASN1Set _certSet, _crlSet;
+ private boolean _isCertCrlParsed;
+
+ public CMSSignedDataParser(
+ DigestCalculatorProvider digestCalculatorProvider,
+ byte[] sigBlock)
+ throws CMSException
+ {
+ this(digestCalculatorProvider, new ByteArrayInputStream(sigBlock));
+ }
+
+ public CMSSignedDataParser(
+ DigestCalculatorProvider digestCalculatorProvider,
+ CMSTypedStream signedContent,
+ byte[] sigBlock)
+ throws CMSException
+ {
+ this(digestCalculatorProvider, signedContent, new ByteArrayInputStream(sigBlock));
+ }
+
+ /**
+ * base constructor - with encapsulated content
+ */
+ public CMSSignedDataParser(
+ DigestCalculatorProvider digestCalculatorProvider,
+ InputStream sigData)
+ throws CMSException
+ {
+ this(digestCalculatorProvider, null, sigData);
+ }
+
+ /**
+ * base constructor
+ *
+ * @param digestCalculatorProvider for generating accumulating digests
+ * @param signedContent the content that was signed.
+ * @param sigData the signature object stream.
+ */
+ public CMSSignedDataParser(
+ DigestCalculatorProvider digestCalculatorProvider,
+ CMSTypedStream signedContent,
+ InputStream sigData)
+ throws CMSException
+ {
+ super(sigData);
+
+ try
+ {
+ _signedContent = signedContent;
+ _signedData = SignedDataParser.getInstance(_contentInfo.getContent(BERTags.SEQUENCE));
+ digests = new HashMap();
+
+ ASN1SetParser digAlgs = _signedData.getDigestAlgorithms();
+ ASN1Encodable o;
+
+ while ((o = digAlgs.readObject()) != null)
+ {
+ AlgorithmIdentifier algId = AlgorithmIdentifier.getInstance(o);
+ try
+ {
+ DigestCalculator calculator = digestCalculatorProvider.get(algId);
+
+ if (calculator != null)
+ {
+ this.digests.put(algId.getAlgorithm(), calculator);
+ }
+ }
+ catch (OperatorCreationException e)
+ {
+ // ignore
+ }
+ }
+
+ //
+ // If the message is simply a certificate chain message getContent() may return null.
+ //
+ ContentInfoParser cont = _signedData.getEncapContentInfo();
+ ASN1OctetStringParser octs = (ASN1OctetStringParser)
+ cont.getContent(BERTags.OCTET_STRING);
+
+ if (octs != null)
+ {
+ CMSTypedStream ctStr = new CMSTypedStream(
+ cont.getContentType().getId(), octs.getOctetStream());
+
+ if (_signedContent == null)
+ {
+ _signedContent = ctStr;
+ }
+ else
+ {
+ //
+ // content passed in, need to read past empty encapsulated content info object if present
+ //
+ ctStr.drain();
+ }
+ }
+
+ if (signedContent == null)
+ {
+ _signedContentType = cont.getContentType();
+ }
+ else
+ {
+ _signedContentType = _signedContent.getContentType();
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CMSException("io exception: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Return the version number for the SignedData object
+ *
+ * @return the version number
+ */
+ public int getVersion()
+ {
+ return _signedData.getVersion().getValue().intValue();
+ }
+
+ /**
+ * return the collection of signers that are associated with the
+ * signatures for the message.
+ * @throws CMSException
+ */
+ public SignerInformationStore getSignerInfos()
+ throws CMSException
+ {
+ if (_signerInfoStore == null)
+ {
+ populateCertCrlSets();
+
+ List signerInfos = new ArrayList();
+ Map hashes = new HashMap();
+
+ Iterator it = digests.keySet().iterator();
+ while (it.hasNext())
+ {
+ Object digestKey = it.next();
+
+ hashes.put(digestKey, ((DigestCalculator)digests.get(digestKey)).getDigest());
+ }
+
+ try
+ {
+ ASN1SetParser s = _signedData.getSignerInfos();
+ ASN1Encodable o;
+
+ while ((o = s.readObject()) != null)
+ {
+ SignerInfo info = SignerInfo.getInstance(o.toASN1Primitive());
+
+ byte[] hash = (byte[])hashes.get(info.getDigestAlgorithm().getAlgorithm());
+
+ signerInfos.add(new SignerInformation(info, _signedContentType, null, hash));
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CMSException("io exception: " + e.getMessage(), e);
+ }
+
+ _signerInfoStore = new SignerInformationStore(signerInfos);
+ }
+
+ return _signerInfoStore;
+ }
+
+ /**
+ * Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
+ *
+ * @return a Store of X509CertificateHolder objects.
+ */
+ public Store getCertificates()
+ throws CMSException
+ {
+ populateCertCrlSets();
+
+ return HELPER.getCertificates(_certSet);
+ }
+
+ /**
+ * Return any X.509 CRL objects in this SignedData structure as a Store of X509CRLHolder objects.
+ *
+ * @return a Store of X509CRLHolder objects.
+ */
+ public Store getCRLs()
+ throws CMSException
+ {
+ populateCertCrlSets();
+
+ return HELPER.getCRLs(_crlSet);
+ }
+
+ /**
+ * Return any X.509 attribute certificate objects in this SignedData structure as a Store of X509AttributeCertificateHolder objects.
+ *
+ * @return a Store of X509AttributeCertificateHolder objects.
+ */
+ public Store getAttributeCertificates()
+ throws CMSException
+ {
+ populateCertCrlSets();
+
+ return HELPER.getAttributeCertificates(_certSet);
+ }
+
+ /**
+ * Return any OtherRevocationInfo OtherRevInfo objects of the type indicated by otherRevocationInfoFormat in
+ * this SignedData structure.
+ *
+ * @param otherRevocationInfoFormat OID of the format type been looked for.
+ *
+ * @return a Store of ASN1Encodable objects representing any objects of otherRevocationInfoFormat found.
+ */
+ public Store getOtherRevocationInfo(ASN1ObjectIdentifier otherRevocationInfoFormat)
+ throws CMSException
+ {
+ populateCertCrlSets();
+
+ return HELPER.getOtherRevocationInfo(otherRevocationInfoFormat, _crlSet);
+ }
+
+ private void populateCertCrlSets()
+ throws CMSException
+ {
+ if (_isCertCrlParsed)
+ {
+ return;
+ }
+
+ _isCertCrlParsed = true;
+
+ try
+ {
+ // care! Streaming - these must be done in exactly this order.
+ _certSet = getASN1Set(_signedData.getCertificates());
+ _crlSet = getASN1Set(_signedData.getCrls());
+ }
+ catch (IOException e)
+ {
+ throw new CMSException("problem parsing cert/crl sets", e);
+ }
+ }
+
+ /**
+ * Return the a string representation of the OID associated with the
+ * encapsulated content info structure carried in the signed data.
+ *
+ * @return the OID for the content type.
+ */
+ public String getSignedContentTypeOID()
+ {
+ return _signedContentType.getId();
+ }
+
+ public CMSTypedStream getSignedContent()
+ {
+ if (_signedContent == null)
+ {
+ return null;
+ }
+
+ InputStream digStream = CMSUtils.attachDigestsToInputStream(
+ digests.values(), _signedContent.getContentStream());
+
+ return new CMSTypedStream(_signedContent.getContentType(), digStream);
+ }
+
+ /**
+ * Replace the signerinformation store associated with the passed
+ * in message contained in the stream original with the new one passed in.
+ * You would probably only want to do this if you wanted to change the unsigned
+ * attributes associated with a signer, or perhaps delete one.
+ * <p>
+ * The output stream is returned unclosed.
+ * </p>
+ * @param original the signed data stream to be used as a base.
+ * @param signerInformationStore the new signer information store to use.
+ * @param out the stream to write the new signed data object to.
+ * @return out.
+ */
+ public static OutputStream replaceSigners(
+ InputStream original,
+ SignerInformationStore signerInformationStore,
+ OutputStream out)
+ throws CMSException, IOException
+ {
+ ASN1StreamParser in = new ASN1StreamParser(original);
+ ContentInfoParser contentInfo = new ContentInfoParser((ASN1SequenceParser)in.readObject());
+ SignedDataParser signedData = SignedDataParser.getInstance(contentInfo.getContent(BERTags.SEQUENCE));
+
+ BERSequenceGenerator sGen = new BERSequenceGenerator(out);
+
+ sGen.addObject(CMSObjectIdentifiers.signedData);
+
+ BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);
+
+ // version number
+ sigGen.addObject(signedData.getVersion());
+
+ // digests
+ signedData.getDigestAlgorithms().toASN1Primitive(); // skip old ones
+
+ ASN1EncodableVector digestAlgs = new ASN1EncodableVector();
+
+ for (Iterator it = signerInformationStore.getSigners().iterator(); it.hasNext();)
+ {
+ SignerInformation signer = (SignerInformation)it.next();
+ digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+ }
+
+ sigGen.getRawOutputStream().write(new DERSet(digestAlgs).getEncoded());
+
+ // encap content info
+ ContentInfoParser encapContentInfo = signedData.getEncapContentInfo();
+
+ BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen.getRawOutputStream());
+
+ eiGen.addObject(encapContentInfo.getContentType());
+
+ pipeEncapsulatedOctetString(encapContentInfo, eiGen.getRawOutputStream());
+
+ eiGen.close();
+
+
+ writeSetToGeneratorTagged(sigGen, signedData.getCertificates(), 0);
+ writeSetToGeneratorTagged(sigGen, signedData.getCrls(), 1);
+
+
+ ASN1EncodableVector signerInfos = new ASN1EncodableVector();
+ for (Iterator it = signerInformationStore.getSigners().iterator(); it.hasNext();)
+ {
+ SignerInformation signer = (SignerInformation)it.next();
+
+ signerInfos.add(signer.toASN1Structure());
+ }
+
+ sigGen.getRawOutputStream().write(new DERSet(signerInfos).getEncoded());
+
+ sigGen.close();
+
+ sGen.close();
+
+ return out;
+ }
+
+ /**
+ * Replace the certificate and CRL information associated with this
+ * CMSSignedData object with the new one passed in.
+ * <p>
+ * The output stream is returned unclosed.
+ * </p>
+ * @param original the signed data stream to be used as a base.
+ * @param certs new certificates to be used, if any.
+ * @param crls new CRLs to be used, if any.
+ * @param attrCerts new attribute certificates to be used, if any.
+ * @param out the stream to write the new signed data object to.
+ * @return out.
+ * @exception CMSException if there is an error processing the CertStore
+ */
+ public static OutputStream replaceCertificatesAndCRLs(
+ InputStream original,
+ Store certs,
+ Store crls,
+ Store attrCerts,
+ OutputStream out)
+ throws CMSException, IOException
+ {
+ ASN1StreamParser in = new ASN1StreamParser(original);
+ ContentInfoParser contentInfo = new ContentInfoParser((ASN1SequenceParser)in.readObject());
+ SignedDataParser signedData = SignedDataParser.getInstance(contentInfo.getContent(BERTags.SEQUENCE));
+
+ BERSequenceGenerator sGen = new BERSequenceGenerator(out);
+
+ sGen.addObject(CMSObjectIdentifiers.signedData);
+
+ BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);
+
+ // version number
+ sigGen.addObject(signedData.getVersion());
+
+ // digests
+ sigGen.getRawOutputStream().write(signedData.getDigestAlgorithms().toASN1Primitive().getEncoded());
+
+ // encap content info
+ ContentInfoParser encapContentInfo = signedData.getEncapContentInfo();
+
+ BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen.getRawOutputStream());
+
+ eiGen.addObject(encapContentInfo.getContentType());
+
+ pipeEncapsulatedOctetString(encapContentInfo, eiGen.getRawOutputStream());
+
+ eiGen.close();
+
+ //
+ // skip existing certs and CRLs
+ //
+ getASN1Set(signedData.getCertificates());
+ getASN1Set(signedData.getCrls());
+
+ //
+ // replace the certs and crls in the SignedData object
+ //
+ if (certs != null || attrCerts != null)
+ {
+ List certificates = new ArrayList();
+
+ if (certs != null)
+ {
+ certificates.addAll(CMSUtils.getCertificatesFromStore(certs));
+ }
+ if (attrCerts != null)
+ {
+ certificates.addAll(CMSUtils.getAttributeCertificatesFromStore(attrCerts));
+ }
+
+ ASN1Set asn1Certs = CMSUtils.createBerSetFromList(certificates);
+
+ if (asn1Certs.size() > 0)
+ {
+ sigGen.getRawOutputStream().write(new DERTaggedObject(false, 0, asn1Certs).getEncoded());
+ }
+ }
+
+ if (crls != null)
+ {
+ ASN1Set asn1Crls = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(crls));
+
+ if (asn1Crls.size() > 0)
+ {
+ sigGen.getRawOutputStream().write(new DERTaggedObject(false, 1, asn1Crls).getEncoded());
+ }
+ }
+
+ sigGen.getRawOutputStream().write(signedData.getSignerInfos().toASN1Primitive().getEncoded());
+
+ sigGen.close();
+
+ sGen.close();
+
+ return out;
+ }
+
+ private static void writeSetToGeneratorTagged(
+ ASN1Generator asn1Gen,
+ ASN1SetParser asn1SetParser,
+ int tagNo)
+ throws IOException
+ {
+ ASN1Set asn1Set = getASN1Set(asn1SetParser);
+
+ if (asn1Set != null)
+ {
+ if (asn1SetParser instanceof BERSetParser)
+ {
+ asn1Gen.getRawOutputStream().write(new BERTaggedObject(false, tagNo, asn1Set).getEncoded());
+ }
+ else
+ {
+ asn1Gen.getRawOutputStream().write(new DERTaggedObject(false, tagNo, asn1Set).getEncoded());
+ }
+ }
+ }
+
+ private static ASN1Set getASN1Set(
+ ASN1SetParser asn1SetParser)
+ {
+ return asn1SetParser == null
+ ? null
+ : ASN1Set.getInstance(asn1SetParser.toASN1Primitive());
+ }
+
+ private static void pipeEncapsulatedOctetString(ContentInfoParser encapContentInfo,
+ OutputStream rawOutputStream) throws IOException
+ {
+ ASN1OctetStringParser octs = (ASN1OctetStringParser)
+ encapContentInfo.getContent(BERTags.OCTET_STRING);
+
+ if (octs != null)
+ {
+ pipeOctetString(octs, rawOutputStream);
+ }
+
+// BERTaggedObjectParser contentObject = (BERTaggedObjectParser)encapContentInfo.getContentObject();
+// if (contentObject != null)
+// {
+// // Handle IndefiniteLengthInputStream safely
+// InputStream input = ASN1StreamParser.getSafeRawInputStream(contentObject.getContentStream(true));
+//
+// // TODO BerTaggedObjectGenerator?
+// BEROutputStream berOut = new BEROutputStream(rawOutputStream);
+// berOut.write(DERTags.CONSTRUCTED | DERTags.TAGGED | 0);
+// berOut.write(0x80);
+//
+// pipeRawOctetString(input, rawOutputStream);
+//
+// berOut.write(0x00);
+// berOut.write(0x00);
+//
+// input.close();
+// }
+ }
+
+ private static void pipeOctetString(
+ ASN1OctetStringParser octs,
+ OutputStream output)
+ throws IOException
+ {
+ // TODO Allow specification of a specific fragment size?
+ OutputStream outOctets = CMSUtils.createBEROctetOutputStream(
+ output, 0, true, 0);
+ Streams.pipeAll(octs.getOctetStream(), outOctets);
+ outOctets.close();
+ }
+
+// private static void pipeRawOctetString(
+// InputStream rawInput,
+// OutputStream rawOutput)
+// throws IOException
+// {
+// InputStream tee = new TeeInputStream(rawInput, rawOutput);
+// ASN1StreamParser sp = new ASN1StreamParser(tee);
+// ASN1OctetStringParser octs = (ASN1OctetStringParser)sp.readObject();
+// Streams.drain(octs.getOctetStream());
+// }
+}