001: package org.bouncycastle.cms;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.ASN1OctetStringParser;
005: import org.bouncycastle.asn1.ASN1OutputStream;
006: import org.bouncycastle.asn1.ASN1SequenceParser;
007: import org.bouncycastle.asn1.ASN1Set;
008: import org.bouncycastle.asn1.ASN1SetParser;
009: import org.bouncycastle.asn1.DEREncodable;
010: import org.bouncycastle.asn1.DERSet;
011: import org.bouncycastle.asn1.DERTags;
012: import org.bouncycastle.asn1.cms.AttributeTable;
013: import org.bouncycastle.asn1.cms.EncryptedContentInfoParser;
014: import org.bouncycastle.asn1.cms.EnvelopedDataParser;
015: import org.bouncycastle.asn1.cms.KEKRecipientInfo;
016: import org.bouncycastle.asn1.cms.KeyAgreeRecipientInfo;
017: import org.bouncycastle.asn1.cms.KeyTransRecipientInfo;
018: import org.bouncycastle.asn1.cms.PasswordRecipientInfo;
019: import org.bouncycastle.asn1.cms.RecipientInfo;
020: import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
021:
022: import java.io.ByteArrayInputStream;
023: import java.io.ByteArrayOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.security.AlgorithmParameters;
027: import java.security.NoSuchProviderException;
028: import java.util.ArrayList;
029: import java.util.Enumeration;
030: import java.util.Iterator;
031: import java.util.List;
032:
033: /**
034: * Parsing class for an CMS Enveloped Data object from an input stream.
035: * <p>
036: * Note: that because we are in a streaming mode only one recipient can be tried and it is important
037: * that the methods on the parser are called in the appropriate order.
038: * </p>
039: * <p>
040: * Example of use - assuming the first recipient matches the private key we have.
041: * <pre>
042: * CMSEnvelopedDataParser ep = new CMSEnvelopedDataParser(inputStream);
043: *
044: * RecipientInformationStore recipients = ep.getRecipientInfos();
045: *
046: * Collection c = recipients.getRecipients();
047: * Iterator it = c.iterator();
048: *
049: * if (it.hasNext())
050: * {
051: * RecipientInformation recipient = (RecipientInformation)it.next();
052: *
053: * CMSTypedStream recData = recipient.getContentStream(privateKey, "BC");
054: *
055: * processDataStream(recData.getContentStream());
056: * }
057: * </pre>
058: * Note: this class does not introduce buffering - if you are processing large files you should create
059: * the parser with:
060: * <pre>
061: * CMSEnvelopedDataParser ep = new CMSEnvelopedDataParser(new BufferedInputStream(inputStream, bufSize));
062: * </pre>
063: * where bufSize is a suitably large buffer size.
064: */
065: public class CMSEnvelopedDataParser extends CMSContentInfoParser {
066: RecipientInformationStore _recipientInfoStore;
067: EnvelopedDataParser _envelopedData;
068:
069: private AlgorithmIdentifier _encAlg;
070: private AttributeTable _unprotectedAttributes;
071: private boolean _attrNotRead;
072:
073: public CMSEnvelopedDataParser(byte[] envelopedData)
074: throws CMSException, IOException {
075: this (new ByteArrayInputStream(envelopedData));
076: }
077:
078: public CMSEnvelopedDataParser(InputStream envelopedData)
079: throws CMSException, IOException {
080: super (envelopedData);
081:
082: this ._attrNotRead = true;
083: this ._envelopedData = new EnvelopedDataParser(
084: (ASN1SequenceParser) _contentInfo
085: .getContent(DERTags.SEQUENCE));
086:
087: //
088: // load the RecepientInfoStore
089: //
090: ASN1SetParser s = _envelopedData.getRecipientInfos();
091: List baseInfos = new ArrayList();
092: ASN1Set set = ASN1Set.getInstance(s.getDERObject());
093:
094: for (Enumeration en = set.getObjects(); en.hasMoreElements();) {
095: baseInfos.add(RecipientInfo.getInstance(en.nextElement()));
096: }
097:
098: //
099: // read the encrypted content info
100: //
101: EncryptedContentInfoParser encInfo = _envelopedData
102: .getEncryptedContentInfo();
103:
104: this ._encAlg = encInfo.getContentEncryptionAlgorithm();
105:
106: //
107: // prime the recepients
108: //
109: List infos = new ArrayList();
110: Iterator it = baseInfos.iterator();
111: InputStream dataStream = ((ASN1OctetStringParser) encInfo
112: .getEncryptedContent(DERTags.OCTET_STRING))
113: .getOctetStream();
114:
115: while (it.hasNext()) {
116: RecipientInfo info = (RecipientInfo) it.next();
117:
118: if (info.getInfo() instanceof KeyTransRecipientInfo) {
119: infos.add(new KeyTransRecipientInformation(
120: (KeyTransRecipientInfo) info.getInfo(),
121: _encAlg, dataStream));
122: } else if (info.getInfo() instanceof KEKRecipientInfo) {
123: infos.add(new KEKRecipientInformation(
124: (KEKRecipientInfo) info.getInfo(), _encAlg,
125: dataStream));
126: } else if (info.getInfo() instanceof KeyAgreeRecipientInfo) {
127: infos.add(new KeyAgreeRecipientInformation(
128: (KeyAgreeRecipientInfo) info.getInfo(),
129: _encAlg, dataStream));
130: } else if (info.getInfo() instanceof PasswordRecipientInfo) {
131: infos.add(new PasswordRecipientInformation(
132: (PasswordRecipientInfo) info.getInfo(),
133: _encAlg, dataStream));
134: }
135: }
136:
137: _recipientInfoStore = new RecipientInformationStore(infos);
138: }
139:
140: /**
141: * return the object identifier for the content encryption algorithm.
142: */
143: public String getEncryptionAlgOID() {
144: return _encAlg.getObjectId().toString();
145: }
146:
147: /**
148: * return the ASN.1 encoded encryption algorithm parameters, or null if
149: * there aren't any.
150: */
151: public byte[] getEncryptionAlgParams() {
152: try {
153: return encodeObj(_encAlg.getParameters());
154: } catch (Exception e) {
155: throw new RuntimeException(
156: "exception getting encryption parameters " + e);
157: }
158: }
159:
160: /**
161: * Return an AlgorithmParameters object giving the encryption parameters
162: * used to encrypt the message content.
163: *
164: * @param provider the provider to generate the parameters for.
165: * @return the parameters object, null if there is not one.
166: * @throws CMSException if the algorithm cannot be found, or the parameters can't be parsed.
167: * @throws NoSuchProviderException if the provider cannot be found.
168: */
169: public AlgorithmParameters getEncryptionAlgorithmParameters(
170: String provider) throws CMSException,
171: NoSuchProviderException {
172: return CMSEnvelopedHelper.INSTANCE
173: .getEncryptionAlgorithmParameters(
174: getEncryptionAlgOID(),
175: getEncryptionAlgParams(), provider);
176: }
177:
178: /**
179: * return a store of the intended recipients for this message
180: */
181: public RecipientInformationStore getRecipientInfos() {
182: return _recipientInfoStore;
183: }
184:
185: /**
186: * return a table of the unprotected attributes indexed by
187: * the OID of the attribute.
188: * @exception IOException
189: */
190: public AttributeTable getUnprotectedAttributes() throws IOException {
191: if (_unprotectedAttributes == null && _attrNotRead) {
192: ASN1SetParser set = _envelopedData.getUnprotectedAttrs();
193:
194: _attrNotRead = false;
195:
196: if (set != null) {
197: ASN1EncodableVector v = new ASN1EncodableVector();
198: DEREncodable o;
199:
200: while ((o = set.readObject()) != null) {
201: ASN1SequenceParser seq = (ASN1SequenceParser) o;
202:
203: v.add(seq.getDERObject());
204: }
205:
206: _unprotectedAttributes = new AttributeTable(new DERSet(
207: v));
208: }
209: }
210:
211: return _unprotectedAttributes;
212: }
213:
214: private byte[] encodeObj(DEREncodable obj) throws IOException {
215: if (obj != null) {
216: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
217: ASN1OutputStream aOut = new ASN1OutputStream(bOut);
218:
219: aOut.writeObject(obj);
220:
221: return bOut.toByteArray();
222: }
223:
224: return null;
225: }
226: }
|