001: package org.bouncycastle.cms;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.BEROctetStringGenerator;
005: import org.bouncycastle.asn1.BERSequenceGenerator;
006: import org.bouncycastle.asn1.BERSet;
007: import org.bouncycastle.asn1.DERInteger;
008: import org.bouncycastle.asn1.DERSet;
009: import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
010: import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
011: import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
012:
013: import javax.crypto.Cipher;
014: import javax.crypto.CipherOutputStream;
015: import javax.crypto.KeyGenerator;
016: import javax.crypto.NoSuchPaddingException;
017: import javax.crypto.SecretKey;
018: import java.io.IOException;
019: import java.io.OutputStream;
020: import java.security.AlgorithmParameters;
021: import java.security.GeneralSecurityException;
022: import java.security.InvalidAlgorithmParameterException;
023: import java.security.InvalidKeyException;
024: import java.security.NoSuchAlgorithmException;
025: import java.security.NoSuchProviderException;
026: import java.util.Iterator;
027:
028: /**
029: * General class for generating a CMS enveloped-data message stream.
030: * <p>
031: * A simple example of usage.
032: * <pre>
033: * CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
034: *
035: * edGen.addKeyTransRecipient(cert);
036: *
037: * ByteArrayOutputStream bOut = new ByteArrayOutputStream();
038: *
039: * OutputStream out = edGen.open(
040: * bOut, CMSEnvelopedDataGenerator.AES128_CBC, "BC");*
041: * out.write(data);
042: *
043: * out.close();
044: * </pre>
045: */
046: public class CMSEnvelopedDataStreamGenerator extends
047: CMSEnvelopedGenerator {
048: private Object _originatorInfo = null;
049: private Object _unprotectedAttributes = null;
050: private int _bufferSize;
051: private boolean _berEncodeRecipientSet;
052:
053: /**
054: * base constructor
055: */
056: public CMSEnvelopedDataStreamGenerator() {
057: }
058:
059: /**
060: * Set the underlying string size for encapsulated data
061: *
062: * @param bufferSize length of octet strings to buffer the data.
063: */
064: public void setBufferSize(int bufferSize) {
065: _bufferSize = bufferSize;
066: }
067:
068: /**
069: * Use a BER Set to store the recipient information
070: */
071: public void setBEREncodeRecipients(boolean berEncodeRecipientSet) {
072: _berEncodeRecipientSet = berEncodeRecipientSet;
073: }
074:
075: private DERInteger getVersion() {
076: if (_originatorInfo != null || _unprotectedAttributes != null) {
077: return new DERInteger(2);
078: } else {
079: return new DERInteger(0);
080: }
081: }
082:
083: /**
084: * generate an enveloped object that contains an CMS Enveloped Data
085: * object using the given provider and the passed in key generator.
086: * @throws IOException
087: */
088: private OutputStream open(OutputStream out, String encryptionOID,
089: KeyGenerator keyGen, String provider)
090: throws NoSuchAlgorithmException, NoSuchProviderException,
091: CMSException {
092: String encProvider = keyGen.getProvider().getName();
093: SecretKey encKey = keyGen.generateKey();
094: AlgorithmParameters params = generateParameters(encryptionOID,
095: encKey, encProvider);
096:
097: Iterator it = recipientInfs.iterator();
098: ASN1EncodableVector recipientInfos = new ASN1EncodableVector();
099:
100: while (it.hasNext()) {
101: RecipientInf recipient = (RecipientInf) it.next();
102:
103: try {
104: recipientInfos.add(recipient.toRecipientInfo(encKey,
105: provider));
106: } catch (IOException e) {
107: throw new CMSException("encoding error.", e);
108: } catch (InvalidKeyException e) {
109: throw new CMSException(
110: "key inappropriate for algorithm.", e);
111: } catch (GeneralSecurityException e) {
112: throw new CMSException(
113: "error making encrypted content.", e);
114: }
115: }
116:
117: return open(out, encryptionOID, encKey, params, recipientInfos,
118: encProvider);
119: }
120:
121: protected OutputStream open(OutputStream out, String encryptionOID,
122: SecretKey encKey, AlgorithmParameters params,
123: ASN1EncodableVector recipientInfos, String provider)
124: throws NoSuchAlgorithmException, NoSuchProviderException,
125: CMSException {
126: try {
127: //
128: // ContentInfo
129: //
130: BERSequenceGenerator cGen = new BERSequenceGenerator(out);
131:
132: cGen.addObject(CMSObjectIdentifiers.envelopedData);
133:
134: //
135: // Encrypted Data
136: //
137: BERSequenceGenerator envGen = new BERSequenceGenerator(cGen
138: .getRawOutputStream(), 0, true);
139:
140: envGen.addObject(getVersion());
141:
142: if (_berEncodeRecipientSet) {
143: envGen.getRawOutputStream().write(
144: new BERSet(recipientInfos).getEncoded());
145: } else {
146: envGen.getRawOutputStream().write(
147: new DERSet(recipientInfos).getEncoded());
148: }
149:
150: Cipher cipher = CMSEnvelopedHelper.INSTANCE
151: .getSymmetricCipher(encryptionOID, provider);
152:
153: cipher.init(Cipher.ENCRYPT_MODE, encKey, params);
154:
155: BERSequenceGenerator eiGen = new BERSequenceGenerator(
156: envGen.getRawOutputStream());
157:
158: eiGen.addObject(PKCSObjectIdentifiers.data);
159:
160: //
161: // If params are null we try and second guess on them as some providers don't provide
162: // algorithm parameter generation explicity but instead generate them under the hood.
163: //
164: if (params == null) {
165: params = cipher.getParameters();
166: }
167:
168: AlgorithmIdentifier encAlgId = getAlgorithmIdentifier(
169: encryptionOID, params);
170:
171: eiGen.getRawOutputStream().write(encAlgId.getEncoded());
172:
173: BEROctetStringGenerator octGen = new BEROctetStringGenerator(
174: eiGen.getRawOutputStream(), 0, false);
175:
176: CipherOutputStream cOut;
177:
178: if (_bufferSize != 0) {
179: cOut = new CipherOutputStream(octGen
180: .getOctetOutputStream(new byte[_bufferSize]),
181: cipher);
182: } else {
183: cOut = new CipherOutputStream(octGen
184: .getOctetOutputStream(), cipher);
185: }
186:
187: return new CmsEnvelopedDataOutputStream(cOut, cGen, envGen,
188: eiGen);
189: } catch (NoSuchAlgorithmException e) {
190: throw new CMSException("can't find algorithm.", e);
191: } catch (InvalidKeyException e) {
192: throw new CMSException("key invalid in message.", e);
193: } catch (NoSuchPaddingException e) {
194: throw new CMSException("required padding not supported.", e);
195: } catch (InvalidAlgorithmParameterException e) {
196: throw new CMSException("algorithm parameters invalid.", e);
197: } catch (IOException e) {
198: throw new CMSException(
199: "exception decoding algorithm parameters.", e);
200: }
201: }
202:
203: /**
204: * generate an enveloped object that contains an CMS Enveloped Data
205: * object using the given provider.
206: * @throws IOException
207: */
208: public OutputStream open(OutputStream out, String encryptionOID,
209: String provider) throws NoSuchAlgorithmException,
210: NoSuchProviderException, CMSException, IOException {
211: try {
212: KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE
213: .createSymmetricKeyGenerator(encryptionOID,
214: provider);
215:
216: return open(out, encryptionOID, keyGen, provider);
217: } catch (NoSuchAlgorithmException e) {
218: throw new CMSException(
219: "can't find key generation algorithm.", e);
220: }
221: }
222:
223: /**
224: * generate an enveloped object that contains an CMS Enveloped Data
225: * object using the given provider.
226: * @throws IOException
227: */
228: public OutputStream open(OutputStream out, String encryptionOID,
229: int keySize, String provider)
230: throws NoSuchAlgorithmException, NoSuchProviderException,
231: CMSException, IOException {
232: try {
233: KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE
234: .createSymmetricKeyGenerator(encryptionOID,
235: provider);
236:
237: keyGen.init(keySize);
238:
239: return open(out, encryptionOID, keyGen, provider);
240: } catch (NoSuchAlgorithmException e) {
241: throw new CMSException(
242: "can't find key generation algorithm.", e);
243: }
244: }
245:
246: private class CmsEnvelopedDataOutputStream extends OutputStream {
247: private CipherOutputStream _out;
248: private BERSequenceGenerator _cGen;
249: private BERSequenceGenerator _envGen;
250: private BERSequenceGenerator _eiGen;
251:
252: public CmsEnvelopedDataOutputStream(CipherOutputStream out,
253: BERSequenceGenerator cGen, BERSequenceGenerator envGen,
254: BERSequenceGenerator eiGen) {
255: _out = out;
256: _cGen = cGen;
257: _envGen = envGen;
258: _eiGen = eiGen;
259: }
260:
261: public void write(int b) throws IOException {
262: _out.write(b);
263: }
264:
265: public void write(byte[] bytes, int off, int len)
266: throws IOException {
267: _out.write(bytes, off, len);
268: }
269:
270: public void write(byte[] bytes) throws IOException {
271: _out.write(bytes);
272: }
273:
274: public void close() throws IOException {
275: _out.close();
276: _eiGen.close();
277:
278: // [TODO] unprotected attributes go here
279:
280: _envGen.close();
281: _cGen.close();
282: }
283: }
284: }
|