package org.bouncycastle.cms;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1OctetStringParser;
import org.bouncycastle.asn1.ASN1SequenceParser;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1SetParser;
import org.bouncycastle.asn1.BERTags;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.AuthenticatedDataParser;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.ContentInfoParser;
import org.bouncycastle.asn1.cms.OriginatorInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.util.Arrays;

Parsing class for an CMS Authenticated Data object from an input stream.

Note: that because we are in a streaming mode only one recipient can be tried and it is important that the methods on the parser are called in the appropriate order.

Example of use - assuming the first recipient matches the private key we have.

     CMSAuthenticatedDataParser     ad = new CMSAuthenticatedDataParser(inputStream);
     RecipientInformationStore  recipients = ad.getRecipientInfos();
     Collection  c = recipients.getRecipients();
     Iterator    it = c.iterator();
     if (it.hasNext())
     {
         RecipientInformation   recipient = (RecipientInformation)it.next();
         CMSTypedStream recData = recipient.getContentStream(new JceKeyTransAuthenticatedRecipient(privateKey).setProvider("BC"));
         processDataStream(recData.getContentStream());
         if (!Arrays.equals(ad.getMac(), recipient.getMac())
         {
             System.err.println("Data corrupted!!!!");
         }
     }
 
Note: this class does not introduce buffering - if you are processing large files you should create the parser with:
         CMSAuthenticatedDataParser     ep = new CMSAuthenticatedDataParser(new BufferedInputStream(inputStream, bufSize));
 
where bufSize is a suitably large buffer size.
/** * Parsing class for an CMS Authenticated Data object from an input stream. * <p> * Note: that because we are in a streaming mode only one recipient can be tried and it is important * that the methods on the parser are called in the appropriate order. * </p> * <p> * Example of use - assuming the first recipient matches the private key we have. * <pre> * CMSAuthenticatedDataParser ad = new CMSAuthenticatedDataParser(inputStream); * * RecipientInformationStore recipients = ad.getRecipientInfos(); * * Collection c = recipients.getRecipients(); * Iterator it = c.iterator(); * * if (it.hasNext()) * { * RecipientInformation recipient = (RecipientInformation)it.next(); * * CMSTypedStream recData = recipient.getContentStream(new JceKeyTransAuthenticatedRecipient(privateKey).setProvider("BC")); * * processDataStream(recData.getContentStream()); * * if (!Arrays.equals(ad.getMac(), recipient.getMac()) * { * System.err.println("Data corrupted!!!!"); * } * } * </pre> * Note: this class does not introduce buffering - if you are processing large files you should create * the parser with: * <pre> * CMSAuthenticatedDataParser ep = new CMSAuthenticatedDataParser(new BufferedInputStream(inputStream, bufSize)); * </pre> * where bufSize is a suitably large buffer size. */
public class CMSAuthenticatedDataParser extends CMSContentInfoParser { RecipientInformationStore recipientInfoStore; AuthenticatedDataParser authData; private AlgorithmIdentifier macAlg; private byte[] mac; private AttributeTable authAttrs; private ASN1Set authAttrSet; private AttributeTable unauthAttrs; private boolean authAttrNotRead; private boolean unauthAttrNotRead; private OriginatorInformation originatorInfo; public CMSAuthenticatedDataParser( byte[] envelopedData) throws CMSException, IOException { this(new ByteArrayInputStream(envelopedData)); } public CMSAuthenticatedDataParser( byte[] envelopedData, DigestCalculatorProvider digestCalculatorProvider) throws CMSException, IOException { this(new ByteArrayInputStream(envelopedData), digestCalculatorProvider); } public CMSAuthenticatedDataParser( InputStream envelopedData) throws CMSException, IOException { this(envelopedData, null); } public CMSAuthenticatedDataParser( InputStream envelopedData, DigestCalculatorProvider digestCalculatorProvider) throws CMSException, IOException { super(envelopedData); this.authAttrNotRead = true; this.authData = new AuthenticatedDataParser((ASN1SequenceParser)_contentInfo.getContent(BERTags.SEQUENCE)); // TODO Validate version? //ASN1Integer version = this.authData.getVersion(); OriginatorInfo info = authData.getOriginatorInfo(); if (info != null) { this.originatorInfo = new OriginatorInformation(info); } // // read the recipients // ASN1Set recipientInfos = ASN1Set.getInstance(authData.getRecipientInfos().toASN1Primitive()); this.macAlg = authData.getMacAlgorithm(); // // build the RecipientInformationStore // AlgorithmIdentifier digestAlgorithm = authData.getDigestAlgorithm(); if (digestAlgorithm != null) { if (digestCalculatorProvider == null) { throw new CMSException("a digest calculator provider is required if authenticated attributes are present"); } // // read the authenticated content info // ContentInfoParser data = authData.getEncapsulatedContentInfo(); CMSReadable readable = new CMSProcessableInputStream( ((ASN1OctetStringParser)data.getContent(BERTags.OCTET_STRING)).getOctetStream()); try { CMSSecureReadable secureReadable = new CMSEnvelopedHelper.CMSDigestAuthenticatedSecureReadable(digestCalculatorProvider.get(digestAlgorithm), readable); this.recipientInfoStore = CMSEnvelopedHelper.buildRecipientInformationStore(recipientInfos, this.macAlg, secureReadable, new AuthAttributesProvider() { public ASN1Set getAuthAttributes() { try { return getAuthAttrSet(); } catch (IOException e) { throw new IllegalStateException("can't parse authenticated attributes!"); } } }); } catch (OperatorCreationException e) { throw new CMSException("unable to create digest calculator: " + e.getMessage(), e); } } else { // // read the authenticated content info // ContentInfoParser data = authData.getEncapsulatedContentInfo(); CMSReadable readable = new CMSProcessableInputStream( ((ASN1OctetStringParser)data.getContent(BERTags.OCTET_STRING)).getOctetStream()); CMSSecureReadable secureReadable = new CMSEnvelopedHelper.CMSAuthenticatedSecureReadable(this.macAlg, readable); this.recipientInfoStore = CMSEnvelopedHelper.buildRecipientInformationStore(recipientInfos, this.macAlg, secureReadable); } }
Return the originator information associated with this message if present.
Returns:OriginatorInformation, null if not present.
/** * Return the originator information associated with this message if present. * * @return OriginatorInformation, null if not present. */
public OriginatorInformation getOriginatorInfo() { return originatorInfo; }
Return the MAC algorithm details for the MAC associated with the data in this object.
Returns:AlgorithmIdentifier representing the MAC algorithm.
/** * Return the MAC algorithm details for the MAC associated with the data in this object. * * @return AlgorithmIdentifier representing the MAC algorithm. */
public AlgorithmIdentifier getMacAlgorithm() { return macAlg; }
return the object identifier for the mac algorithm.
/** * return the object identifier for the mac algorithm. */
public String getMacAlgOID() { return macAlg.getAlgorithm().toString(); }
return the ASN.1 encoded encryption algorithm parameters, or null if there aren't any.
/** * return the ASN.1 encoded encryption algorithm parameters, or null if * there aren't any. */
public byte[] getMacAlgParams() { try { return encodeObj(macAlg.getParameters()); } catch (Exception e) { throw new RuntimeException("exception getting encryption parameters " + e); } }
return a store of the intended recipients for this message
/** * return a store of the intended recipients for this message */
public RecipientInformationStore getRecipientInfos() { return recipientInfoStore; } public byte[] getMac() throws IOException { if (mac == null) { getAuthAttrs(); mac = authData.getMac().getOctets(); } return Arrays.clone(mac); } private ASN1Set getAuthAttrSet() throws IOException { if (authAttrs == null && authAttrNotRead) { ASN1SetParser set = authData.getAuthAttrs(); if (set != null) { authAttrSet = (ASN1Set)set.toASN1Primitive(); } authAttrNotRead = false; } return authAttrSet; }
return a table of the unauthenticated attributes indexed by the OID of the attribute.
Throws:
  • IOException –
/** * return a table of the unauthenticated attributes indexed by * the OID of the attribute. * @exception java.io.IOException */
public AttributeTable getAuthAttrs() throws IOException { if (authAttrs == null && authAttrNotRead) { ASN1Set set = getAuthAttrSet(); if (set != null) { authAttrs = new AttributeTable(set); } } return authAttrs; }
return a table of the unauthenticated attributes indexed by the OID of the attribute.
Throws:
  • IOException –
/** * return a table of the unauthenticated attributes indexed by * the OID of the attribute. * @exception java.io.IOException */
public AttributeTable getUnauthAttrs() throws IOException { if (unauthAttrs == null && unauthAttrNotRead) { ASN1SetParser set = authData.getUnauthAttrs(); unauthAttrNotRead = false; if (set != null) { ASN1EncodableVector v = new ASN1EncodableVector(); ASN1Encodable o; while ((o = set.readObject()) != null) { ASN1SequenceParser seq = (ASN1SequenceParser)o; v.add(seq.toASN1Primitive()); } unauthAttrs = new AttributeTable(new DERSet(v)); } } return unauthAttrs; } private byte[] encodeObj( ASN1Encodable obj) throws IOException { if (obj != null) { return obj.toASN1Primitive().getEncoded(); } return null; }
This will only be valid after the content has been read.
Returns:the contents of the messageDigest attribute, if available. Null if not present.
/** * This will only be valid after the content has been read. * * @return the contents of the messageDigest attribute, if available. Null if not present. */
public byte[] getContentDigest() { if (authAttrs != null) { return ASN1OctetString.getInstance(authAttrs.get(CMSAttributes.messageDigest).getAttrValues().getObjectAt(0)).getOctets(); } return null; } }