001: package org.bouncycastle.mail.smime;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
005: import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
006: import org.bouncycastle.cms.CMSException;
007:
008: import javax.activation.CommandMap;
009: import javax.activation.MailcapCommandMap;
010: import javax.crypto.SecretKey;
011: import javax.mail.MessagingException;
012: import javax.mail.internet.MimeBodyPart;
013: import javax.mail.internet.MimeMessage;
014: import java.io.IOException;
015: import java.io.OutputStream;
016: import java.security.AlgorithmParameters;
017: import java.security.InvalidKeyException;
018: import java.security.NoSuchAlgorithmException;
019: import java.security.NoSuchProviderException;
020: import java.security.PrivateKey;
021: import java.security.PublicKey;
022: import java.security.cert.X509Certificate;
023:
024: /**
025: * General class for generating a pkcs7-mime message.
026: *
027: * A simple example of usage.
028: *
029: * <pre>
030: * SMIMEEnvelopedGenerator fact = new SMIMEEnvelopedGenerator();
031: *
032: * fact.addKeyTransRecipient(cert);
033: *
034: * MimeBodyPart smime = fact.generate(content, algorithm, "BC");
035: * </pre>
036: *
037: * <b>Note:<b> Most clients expect the MimeBodyPart to be in a MimeMultipart
038: * when it's sent.
039: */
040: public class SMIMEEnvelopedGenerator extends SMIMEGenerator {
041: public static final String DES_EDE3_CBC = CMSEnvelopedDataGenerator.DES_EDE3_CBC;
042: public static final String RC2_CBC = CMSEnvelopedDataGenerator.RC2_CBC;
043: public static final String IDEA_CBC = CMSEnvelopedDataGenerator.IDEA_CBC;
044: public static final String CAST5_CBC = CMSEnvelopedDataGenerator.CAST5_CBC;
045:
046: public static final String AES128_CBC = CMSEnvelopedDataGenerator.AES128_CBC;
047: public static final String AES192_CBC = CMSEnvelopedDataGenerator.AES192_CBC;
048: public static final String AES256_CBC = CMSEnvelopedDataGenerator.AES256_CBC;
049:
050: public static final String CAMELLIA128_CBC = CMSEnvelopedDataGenerator.CAMELLIA128_CBC;
051: public static final String CAMELLIA192_CBC = CMSEnvelopedDataGenerator.CAMELLIA192_CBC;
052: public static final String CAMELLIA256_CBC = CMSEnvelopedDataGenerator.CAMELLIA256_CBC;
053:
054: public static final String SEED_CBC = CMSEnvelopedDataGenerator.SEED_CBC;
055:
056: public static final String DES_EDE3_WRAP = CMSEnvelopedDataGenerator.DES_EDE3_WRAP;
057: public static final String AES128_WRAP = CMSEnvelopedDataGenerator.AES128_WRAP;
058: public static final String AES256_WRAP = CMSEnvelopedDataGenerator.AES256_WRAP;
059: public static final String CAMELLIA128_WRAP = CMSEnvelopedDataGenerator.CAMELLIA128_WRAP;
060: public static final String CAMELLIA192_WRAP = CMSEnvelopedDataGenerator.CAMELLIA192_WRAP;
061: public static final String CAMELLIA256_WRAP = CMSEnvelopedDataGenerator.CAMELLIA256_WRAP;
062: public static final String SEED_WRAP = CMSEnvelopedDataGenerator.SEED_WRAP;
063:
064: public static final String ECDH_SHA1KDF = CMSEnvelopedDataGenerator.ECDH_SHA1KDF;
065:
066: private static final String ENCRYPTED_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data";
067:
068: private EnvelopedGenerator fact;
069:
070: static {
071: MailcapCommandMap mc = (MailcapCommandMap) CommandMap
072: .getDefaultCommandMap();
073:
074: mc
075: .addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
076: mc
077: .addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
078:
079: CommandMap.setDefaultCommandMap(mc);
080: }
081:
082: /**
083: * base constructor
084: */
085: public SMIMEEnvelopedGenerator() {
086: fact = new EnvelopedGenerator();
087: }
088:
089: /**
090: * add a recipient.
091: */
092: public void addKeyTransRecipient(X509Certificate cert)
093: throws IllegalArgumentException {
094: fact.addKeyTransRecipient(cert);
095: }
096:
097: /**
098: * add a recipient - note: this will only work on V3 and later clients.
099: *
100: * @param key the recipient's public key
101: * @param subKeyId the subject key id for the recipient's public key
102: */
103: public void addKeyTransRecipient(PublicKey key, byte[] subKeyId)
104: throws IllegalArgumentException {
105: fact.addKeyTransRecipient(key, subKeyId);
106: }
107:
108: /**
109: * add a KEK recipient.
110: */
111: public void addKEKRecipient(SecretKey key, byte[] keyIdentifier)
112: throws IllegalArgumentException {
113: fact.addKEKRecipient(key, keyIdentifier);
114: }
115:
116: /**
117: * Add a key agreement based recipient.
118: *
119: * @param senderPrivateKey private key to initialise sender side of agreement with.
120: * @param senderPublicKey sender public key to include with message.
121: * @param recipientCert recipient's public key certificate.
122: * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
123: * @param provider provider to use for the agreement calculation.
124: */
125: public void addKeyAgreementRecipient(String agreementAlgorithm,
126: PrivateKey senderPrivateKey, PublicKey senderPublicKey,
127: X509Certificate recipientCert, String cekWrapAlgorithm,
128: String provider) throws NoSuchProviderException,
129: NoSuchAlgorithmException, InvalidKeyException {
130: fact.addKeyAgreementRecipient(agreementAlgorithm,
131: senderPrivateKey, senderPublicKey, recipientCert,
132: cekWrapAlgorithm, provider);
133: }
134:
135: /**
136: * Use a BER Set to store the recipient information
137: */
138: public void setBerEncodeRecipients(boolean berEncodeRecipientSet) {
139: fact.setBEREncodeRecipients(berEncodeRecipientSet);
140: }
141:
142: /**
143: * if we get here we expect the Mime body part to be well defined.
144: */
145: private MimeBodyPart make(MimeBodyPart content,
146: String encryptionOID, int keySize, String provider)
147: throws NoSuchAlgorithmException, NoSuchProviderException,
148: SMIMEException {
149: //
150: // check the base algorithm and provider is available
151: //
152: createSymmetricKeyGenerator(encryptionOID, provider);
153:
154: try {
155: MimeBodyPart data = new MimeBodyPart();
156:
157: data.setContent(new ContentEncryptor(content,
158: encryptionOID, keySize, provider),
159: ENCRYPTED_CONTENT_TYPE);
160: data.addHeader("Content-Type", ENCRYPTED_CONTENT_TYPE);
161: data.addHeader("Content-Disposition",
162: "attachment; filename=\"smime.p7m\"");
163: data.addHeader("Content-Description",
164: "S/MIME Encrypted Message");
165: data.addHeader("Content-Transfer-Encoding", encoding);
166:
167: return data;
168: } catch (MessagingException e) {
169: throw new SMIMEException(
170: "exception putting multi-part together.", e);
171: }
172: }
173:
174: /**
175: * generate an enveloped object that contains an SMIME Enveloped
176: * object using the given provider.
177: */
178: public MimeBodyPart generate(MimeBodyPart content,
179: String encryptionOID, String provider)
180: throws NoSuchAlgorithmException, NoSuchProviderException,
181: SMIMEException {
182: return make(makeContentBodyPart(content), encryptionOID, 0,
183: provider);
184: }
185:
186: /**
187: * generate an enveloped object that contains an SMIME Enveloped
188: * object using the given provider from the contents of the passed in
189: * message
190: */
191: public MimeBodyPart generate(MimeMessage message,
192: String encryptionOID, String provider)
193: throws NoSuchAlgorithmException, NoSuchProviderException,
194: SMIMEException {
195: try {
196: message.saveChanges(); // make sure we're up to date.
197: } catch (MessagingException e) {
198: throw new SMIMEException("unable to save message", e);
199: }
200:
201: return make(makeContentBodyPart(message), encryptionOID, 0,
202: provider);
203: }
204:
205: /**
206: * generate an enveloped object that contains an SMIME Enveloped
207: * object using the given provider. The size of the encryption key
208: * is determined by keysize.
209: */
210: public MimeBodyPart generate(MimeBodyPart content,
211: String encryptionOID, int keySize, String provider)
212: throws NoSuchAlgorithmException, NoSuchProviderException,
213: SMIMEException {
214: return make(makeContentBodyPart(content), encryptionOID,
215: keySize, provider);
216: }
217:
218: /**
219: * generate an enveloped object that contains an SMIME Enveloped
220: * object using the given provider from the contents of the passed in
221: * message. The size of the encryption key used to protect the message
222: * is determined by keysize.
223: */
224: public MimeBodyPart generate(MimeMessage message,
225: String encryptionOID, int keySize, String provider)
226: throws NoSuchAlgorithmException, NoSuchProviderException,
227: SMIMEException {
228: try {
229: message.saveChanges(); // make sure we're up to date.
230: } catch (MessagingException e) {
231: throw new SMIMEException("unable to save message", e);
232: }
233:
234: return make(makeContentBodyPart(message), encryptionOID,
235: keySize, provider);
236: }
237:
238: private class ContentEncryptor implements SMIMEStreamingProcessor {
239: private final MimeBodyPart _content;
240: private final String _encryptionOid;
241: private final int _keySize;
242: private final String _provider;
243:
244: private boolean _firstTime = true;
245:
246: ContentEncryptor(MimeBodyPart content, String encryptionOid,
247: int keySize, String provider) {
248: _content = content;
249: _encryptionOid = encryptionOid;
250: _keySize = keySize;
251: _provider = provider;
252: }
253:
254: public void write(OutputStream out) throws IOException {
255: OutputStream encrypted;
256:
257: try {
258: if (_firstTime) {
259: if (_keySize == 0) // use the default
260: {
261: encrypted = fact.open(out, _encryptionOid,
262: _provider);
263: } else {
264: encrypted = fact.open(out, _encryptionOid,
265: _keySize, _provider);
266: }
267:
268: _firstTime = false;
269: } else {
270: encrypted = fact.regenerate(out, _provider);
271: }
272:
273: _content.writeTo(encrypted);
274:
275: encrypted.close();
276: } catch (MessagingException e) {
277: throw new WrappingIOException(e.toString(), e);
278: } catch (NoSuchAlgorithmException e) {
279: throw new WrappingIOException(e.toString(), e);
280: } catch (NoSuchProviderException e) {
281: throw new WrappingIOException(e.toString(), e);
282: } catch (CMSException e) {
283: throw new WrappingIOException(e.toString(), e);
284: }
285: }
286: }
287:
288: private class EnvelopedGenerator extends
289: CMSEnvelopedDataStreamGenerator {
290: private String _encryptionOID;
291: private SecretKey _encKey;
292: private AlgorithmParameters _params;
293: private ASN1EncodableVector _recipientInfos;
294:
295: protected OutputStream open(OutputStream out,
296: String encryptionOID, SecretKey encKey,
297: AlgorithmParameters params,
298: ASN1EncodableVector recepientInfos, String provider)
299: throws NoSuchAlgorithmException,
300: NoSuchProviderException, CMSException {
301: _encryptionOID = encryptionOID;
302: _encKey = encKey;
303: _params = params;
304: _recipientInfos = recepientInfos;
305:
306: return super .open(out, encryptionOID, encKey, params,
307: recepientInfos, provider);
308: }
309:
310: OutputStream regenerate(OutputStream out, String provider)
311: throws NoSuchAlgorithmException,
312: NoSuchProviderException, CMSException {
313: return super .open(out, _encryptionOID, _encKey, _params,
314: _recipientInfos, provider);
315: }
316: }
317:
318: private static class WrappingIOException extends IOException {
319: private Throwable cause;
320:
321: WrappingIOException(String msg, Throwable cause) {
322: super (msg);
323:
324: this .cause = cause;
325: }
326:
327: public Throwable getCause() {
328: return cause;
329: }
330: }
331: }
|