blob: 11c90c67bda7282ba4f3e41c506f6b32990d7f5d [file] [log] [blame]
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.
* <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.
*
* @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.
*
* @return AlgorithmIdentifier representing the MAC algorithm.
*/
public AlgorithmIdentifier getMacAlgorithm()
{
return macAlg;
}
/**
* 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.
*/
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
*/
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.
* @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.
* @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.
*
* @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;
}
}