001: package org.bouncycastle.cms;
002:
003: import org.bouncycastle.asn1.ASN1EncodableVector;
004: import org.bouncycastle.asn1.ASN1InputStream;
005: import org.bouncycastle.asn1.ASN1Object;
006: import org.bouncycastle.asn1.ASN1OctetString;
007: import org.bouncycastle.asn1.ASN1Sequence;
008: import org.bouncycastle.asn1.DEREncodable;
009: import org.bouncycastle.asn1.DERInteger;
010: import org.bouncycastle.asn1.DERNull;
011: import org.bouncycastle.asn1.DERObjectIdentifier;
012: import org.bouncycastle.asn1.DEROctetString;
013: import org.bouncycastle.asn1.DERSequence;
014: import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
015: import org.bouncycastle.asn1.cms.KEKIdentifier;
016: import org.bouncycastle.asn1.cms.KEKRecipientInfo;
017: import org.bouncycastle.asn1.cms.KeyAgreeRecipientIdentifier;
018: import org.bouncycastle.asn1.cms.KeyAgreeRecipientInfo;
019: import org.bouncycastle.asn1.cms.KeyTransRecipientInfo;
020: import org.bouncycastle.asn1.cms.OriginatorIdentifierOrKey;
021: import org.bouncycastle.asn1.cms.OriginatorPublicKey;
022: import org.bouncycastle.asn1.cms.PasswordRecipientInfo;
023: import org.bouncycastle.asn1.cms.RecipientEncryptedKey;
024: import org.bouncycastle.asn1.cms.RecipientIdentifier;
025: import org.bouncycastle.asn1.cms.RecipientInfo;
026: import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
027: import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
028: import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
029: import org.bouncycastle.asn1.pkcs.PBKDF2Params;
030: import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
031: import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
032: import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
033: import org.bouncycastle.asn1.x509.TBSCertificateStructure;
034: import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
035: import org.bouncycastle.jce.PrincipalUtil;
036:
037: import javax.crypto.Cipher;
038: import javax.crypto.KeyAgreement;
039: import javax.crypto.SecretKey;
040: import javax.crypto.spec.RC2ParameterSpec;
041: import javax.crypto.spec.SecretKeySpec;
042: import java.io.IOException;
043: import java.security.AlgorithmParameterGenerator;
044: import java.security.AlgorithmParameters;
045: import java.security.GeneralSecurityException;
046: import java.security.InvalidAlgorithmParameterException;
047: import java.security.InvalidKeyException;
048: import java.security.NoSuchAlgorithmException;
049: import java.security.NoSuchProviderException;
050: import java.security.PrivateKey;
051: import java.security.PublicKey;
052: import java.security.SecureRandom;
053: import java.security.cert.CertificateEncodingException;
054: import java.security.cert.X509Certificate;
055: import java.util.ArrayList;
056: import java.util.List;
057:
058: /**
059: * General class for generating a CMS enveloped-data message.
060: *
061: * A simple example of usage.
062: *
063: * <pre>
064: * CMSEnvelopedDataGenerator fact = new CMSEnvelopedDataGenerator();
065: *
066: * fact.addKeyTransRecipient(cert);
067: *
068: * CMSEnvelopedData data = fact.generate(content, algorithm, "BC");
069: * </pre>
070: */
071: public class CMSEnvelopedGenerator {
072: public static final String DES_EDE3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC
073: .getId();
074: public static final String RC2_CBC = PKCSObjectIdentifiers.RC2_CBC
075: .getId();
076: public static final String IDEA_CBC = "1.3.6.1.4.1.188.7.1.1.2";
077: public static final String CAST5_CBC = "1.2.840.113533.7.66.10";
078: public static final String AES128_CBC = NISTObjectIdentifiers.id_aes128_CBC
079: .getId();
080: public static final String AES192_CBC = NISTObjectIdentifiers.id_aes192_CBC
081: .getId();
082: public static final String AES256_CBC = NISTObjectIdentifiers.id_aes256_CBC
083: .getId();
084: public static final String CAMELLIA128_CBC = NTTObjectIdentifiers.id_camellia128_cbc
085: .getId();
086: public static final String CAMELLIA192_CBC = NTTObjectIdentifiers.id_camellia192_cbc
087: .getId();
088: public static final String CAMELLIA256_CBC = NTTObjectIdentifiers.id_camellia256_cbc
089: .getId();
090: public static final String SEED_CBC = KISAObjectIdentifiers.id_seedCBC
091: .getId();
092:
093: public static final String DES_EDE3_WRAP = PKCSObjectIdentifiers.id_alg_CMS3DESwrap
094: .getId();
095: public static final String AES128_WRAP = NISTObjectIdentifiers.id_aes128_wrap
096: .getId();
097: public static final String AES192_WRAP = NISTObjectIdentifiers.id_aes192_wrap
098: .getId();
099: public static final String AES256_WRAP = NISTObjectIdentifiers.id_aes256_wrap
100: .getId();
101: public static final String CAMELLIA128_WRAP = NTTObjectIdentifiers.id_camellia128_wrap
102: .getId();
103: public static final String CAMELLIA192_WRAP = NTTObjectIdentifiers.id_camellia192_wrap
104: .getId();
105: public static final String CAMELLIA256_WRAP = NTTObjectIdentifiers.id_camellia256_wrap
106: .getId();
107: public static final String SEED_WRAP = KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap
108: .getId();
109:
110: public static final String ECDH_SHA1KDF = X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme
111: .getId();
112:
113: private static final CMSEnvelopedHelper HELPER = CMSEnvelopedHelper.INSTANCE;
114:
115: List recipientInfs = new ArrayList();
116: SecureRandom rand = new SecureRandom();
117:
118: protected class RecipientInf {
119: X509Certificate cert;
120: AlgorithmIdentifier keyEncAlg;
121: PublicKey pubKey;
122: ASN1OctetString subKeyId;
123:
124: SecretKey secKey;
125: KEKIdentifier secKeyId;
126:
127: OriginatorIdentifierOrKey originator;
128: ASN1OctetString ukm;
129:
130: AlgorithmIdentifier derivationAlg;
131:
132: RecipientInf(X509Certificate cert) {
133: this .cert = cert;
134: this .pubKey = cert.getPublicKey();
135:
136: try {
137: TBSCertificateStructure tbs = TBSCertificateStructure
138: .getInstance(ASN1Object.fromByteArray(cert
139: .getTBSCertificate()));
140: SubjectPublicKeyInfo info = tbs
141: .getSubjectPublicKeyInfo();
142:
143: keyEncAlg = info.getAlgorithmId();
144: } catch (IOException e) {
145: throw new IllegalArgumentException(
146: "can't extract key algorithm from this cert");
147: } catch (CertificateEncodingException e) {
148: throw new IllegalArgumentException(
149: "can't extract tbs structure from this cert");
150: }
151: }
152:
153: RecipientInf(PublicKey pubKey, ASN1OctetString subKeyId) {
154: this .pubKey = pubKey;
155: this .subKeyId = subKeyId;
156:
157: try {
158: SubjectPublicKeyInfo info = SubjectPublicKeyInfo
159: .getInstance(ASN1Object.fromByteArray(pubKey
160: .getEncoded()));
161:
162: keyEncAlg = info.getAlgorithmId();
163: } catch (IOException e) {
164: throw new IllegalArgumentException(
165: "can't extract key algorithm from this key");
166: }
167: }
168:
169: RecipientInf(SecretKey secKey, KEKIdentifier secKeyId) {
170: this .secKey = secKey;
171: this .secKeyId = secKeyId;
172:
173: if (secKey.getAlgorithm().startsWith("DES")) {
174: keyEncAlg = new AlgorithmIdentifier(
175: new DERObjectIdentifier(
176: "1.2.840.113549.1.9.16.3.6"),
177: new DERNull());
178: } else if (secKey.getAlgorithm().startsWith("RC2")) {
179: keyEncAlg = new AlgorithmIdentifier(
180: new DERObjectIdentifier(
181: "1.2.840.113549.1.9.16.3.7"),
182: new DERInteger(58));
183: } else if (secKey.getAlgorithm().startsWith("AES")) {
184: int length = secKey.getEncoded().length * 8;
185: DERObjectIdentifier wrapOid;
186:
187: if (length == 128) {
188: wrapOid = NISTObjectIdentifiers.id_aes128_wrap;
189: } else if (length == 192) {
190: wrapOid = NISTObjectIdentifiers.id_aes192_wrap;
191: } else if (length == 256) {
192: wrapOid = NISTObjectIdentifiers.id_aes256_wrap;
193: } else {
194: throw new IllegalArgumentException(
195: "illegal keysize in AES");
196: }
197:
198: keyEncAlg = new AlgorithmIdentifier(wrapOid); // parameters absent
199: } else if (secKey.getAlgorithm().startsWith("SEED")) {
200: // parameters absent
201: keyEncAlg = new AlgorithmIdentifier(
202: KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap);
203: } else if (secKey.getAlgorithm().startsWith("Camellia")) {
204: int length = secKey.getEncoded().length * 8;
205: DERObjectIdentifier wrapOid;
206:
207: if (length == 128) {
208: wrapOid = NTTObjectIdentifiers.id_camellia128_wrap;
209: } else if (length == 192) {
210: wrapOid = NTTObjectIdentifiers.id_camellia192_wrap;
211: } else if (length == 256) {
212: wrapOid = NTTObjectIdentifiers.id_camellia256_wrap;
213: } else {
214: throw new IllegalArgumentException(
215: "illegal keysize in Camellia");
216: }
217:
218: keyEncAlg = new AlgorithmIdentifier(wrapOid); // parameters must be absent
219: } else {
220: throw new IllegalArgumentException("unknown algorithm");
221: }
222: }
223:
224: public RecipientInf(SecretKey secretKey, String algorithm,
225: String wrapOid, OriginatorIdentifierOrKey originator,
226: X509Certificate cert) {
227: ASN1EncodableVector params = new ASN1EncodableVector();
228:
229: params.add(new DERObjectIdentifier(wrapOid));
230: params.add(DERNull.INSTANCE);
231:
232: this .secKey = secretKey;
233: this .keyEncAlg = new AlgorithmIdentifier(
234: new DERObjectIdentifier(algorithm),
235: new DERSequence(params));
236: this .originator = originator;
237: this .cert = cert;
238: }
239:
240: public RecipientInf(SecretKey secretKey,
241: AlgorithmIdentifier derivationAlg) {
242: this .secKey = secretKey;
243: this .derivationAlg = derivationAlg;
244: }
245:
246: RecipientInfo toRecipientInfo(SecretKey key, String prov)
247: throws IOException, GeneralSecurityException {
248: if (pubKey != null) {
249: ASN1OctetString encKey;
250:
251: Cipher keyCipher = HELPER.createAsymmetricCipher(
252: keyEncAlg.getObjectId().getId(), prov);
253: try {
254: keyCipher.init(Cipher.WRAP_MODE, pubKey);
255:
256: encKey = new DEROctetString(keyCipher.wrap(key));
257: } catch (GeneralSecurityException e) // some providers do not support wrap
258: {
259: keyCipher.init(Cipher.ENCRYPT_MODE, pubKey);
260:
261: encKey = new DEROctetString(keyCipher.doFinal(key
262: .getEncoded()));
263: } catch (IllegalStateException e) // some providers do not support wrap
264: {
265: keyCipher.init(Cipher.ENCRYPT_MODE, pubKey);
266:
267: encKey = new DEROctetString(keyCipher.doFinal(key
268: .getEncoded()));
269: } catch (UnsupportedOperationException e) // some providers do not support UNWRAP
270: {
271: keyCipher.init(Cipher.ENCRYPT_MODE, key);
272:
273: encKey = new DEROctetString(keyCipher.doFinal(key
274: .getEncoded()));
275: }
276:
277: if (cert != null) {
278: ASN1InputStream aIn = new ASN1InputStream(cert
279: .getTBSCertificate());
280: TBSCertificateStructure tbs = TBSCertificateStructure
281: .getInstance(aIn.readObject());
282: IssuerAndSerialNumber encSid = new IssuerAndSerialNumber(
283: tbs.getIssuer(), tbs.getSerialNumber()
284: .getValue());
285:
286: return new RecipientInfo(new KeyTransRecipientInfo(
287: new RecipientIdentifier(encSid), keyEncAlg,
288: encKey));
289: } else {
290: return new RecipientInfo(new KeyTransRecipientInfo(
291: new RecipientIdentifier(subKeyId),
292: keyEncAlg, encKey));
293: }
294: } else if (originator != null) {
295: Cipher keyCipher = HELPER.createAsymmetricCipher(
296: DERObjectIdentifier.getInstance(
297: ASN1Sequence.getInstance(
298: keyEncAlg.getParameters())
299: .getObjectAt(0)).getId(), prov);
300:
301: keyCipher.init(Cipher.WRAP_MODE, secKey);
302:
303: ASN1OctetString encKey = new DEROctetString(keyCipher
304: .wrap(key));
305:
306: RecipientEncryptedKey rKey = new RecipientEncryptedKey(
307: new KeyAgreeRecipientIdentifier(
308: new IssuerAndSerialNumber(PrincipalUtil
309: .getIssuerX509Principal(cert),
310: cert.getSerialNumber())),
311: encKey);
312:
313: return new RecipientInfo(new KeyAgreeRecipientInfo(
314: originator, ukm, keyEncAlg, new DERSequence(
315: rKey)));
316: } else if (derivationAlg != null) {
317: Cipher keyCipher = HELPER.createAsymmetricCipher(HELPER
318: .getRFC3211WrapperName(secKey.getAlgorithm()),
319: prov);
320:
321: keyCipher.init(Cipher.WRAP_MODE, secKey);
322:
323: ASN1OctetString encKey = new DEROctetString(keyCipher
324: .wrap(key));
325:
326: ASN1EncodableVector v = new ASN1EncodableVector();
327:
328: v.add(new DERObjectIdentifier(secKey.getAlgorithm()));
329: v.add(new DEROctetString(keyCipher.getIV()));
330:
331: keyEncAlg = new AlgorithmIdentifier(
332: PKCSObjectIdentifiers.id_alg_PWRI_KEK,
333: new DERSequence(v));
334:
335: return new RecipientInfo(new PasswordRecipientInfo(
336: derivationAlg, keyEncAlg, encKey));
337: } else {
338: Cipher keyCipher = HELPER.createAsymmetricCipher(
339: keyEncAlg.getObjectId().getId(), prov);
340:
341: keyCipher.init(Cipher.WRAP_MODE, secKey);
342:
343: ASN1OctetString encKey = new DEROctetString(keyCipher
344: .wrap(key));
345:
346: return new RecipientInfo(new KEKRecipientInfo(secKeyId,
347: keyEncAlg, encKey));
348: }
349: }
350: }
351:
352: /**
353: * base constructor
354: */
355: public CMSEnvelopedGenerator() {
356: }
357:
358: /**
359: * add a recipient.
360: *
361: * @param cert recipient's public key certificate
362: * @exception IllegalArgumentException if there is a problem with the certificate
363: */
364: public void addKeyTransRecipient(X509Certificate cert)
365: throws IllegalArgumentException {
366: recipientInfs.add(new RecipientInf(cert));
367: }
368:
369: /**
370: * add a recipient
371: *
372: * @param key the public key used by the recipient
373: * @param subKeyId the identifier for the recipient's public key
374: * @exception IllegalArgumentException if there is a problem with the key
375: */
376: public void addKeyTransRecipient(PublicKey key, byte[] subKeyId)
377: throws IllegalArgumentException {
378: recipientInfs.add(new CMSEnvelopedGenerator.RecipientInf(key,
379: new DEROctetString(subKeyId)));
380: }
381:
382: /**
383: * add a KEK recipient.
384: * @param key the secret key to use for wrapping
385: * @param keyIdentifier the byte string that identifies the key
386: */
387: public void addKEKRecipient(SecretKey key, byte[] keyIdentifier) {
388: recipientInfs.add(new RecipientInf(key, new KEKIdentifier(
389: keyIdentifier, null, null)));
390: }
391:
392: public void addPasswordRecipient(CMSPBEKey pbeKey,
393: String kekAlgorithmOid) {
394: PBKDF2Params params = new PBKDF2Params(pbeKey.getSalt(), pbeKey
395: .getIterationCount());
396:
397: recipientInfs.add(new RecipientInf(new SecretKeySpec(pbeKey
398: .getEncoded(kekAlgorithmOid), kekAlgorithmOid),
399: new AlgorithmIdentifier(
400: PKCSObjectIdentifiers.id_PBKDF2, params)));
401: }
402:
403: /**
404: * Add a key agreement based recipient.
405: *
406: * @param agreementAlgorithm key agreement algorithm to use.
407: * @param senderPrivateKey private key to initialise sender side of agreement with.
408: * @param senderPublicKey sender public key to include with message.
409: * @param recipientCert recipient's public key certificate.
410: * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
411: * @param provider provider to use for the agreement calculation.
412: * @exception NoSuchProviderException if the specified provider cannot be found
413: * @exception NoSuchAlgorithmException if the algorithm requested cannot be found
414: * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
415: */
416: public void addKeyAgreementRecipient(String agreementAlgorithm,
417: PrivateKey senderPrivateKey, PublicKey senderPublicKey,
418: X509Certificate recipientCert, String cekWrapAlgorithm,
419: String provider) throws NoSuchProviderException,
420: NoSuchAlgorithmException, InvalidKeyException {
421: KeyAgreement agreement = KeyAgreement.getInstance(
422: agreementAlgorithm, provider);
423:
424: agreement.init(senderPrivateKey);
425:
426: agreement.doPhase(recipientCert.getPublicKey(), true);
427:
428: try {
429: SubjectPublicKeyInfo oPubKeyInfo = SubjectPublicKeyInfo
430: .getInstance(ASN1Object
431: .fromByteArray(senderPublicKey.getEncoded()));
432: OriginatorIdentifierOrKey originator = new OriginatorIdentifierOrKey(
433: new OriginatorPublicKey(new AlgorithmIdentifier(
434: oPubKeyInfo.getAlgorithmId().getObjectId(),
435: new DERNull()), oPubKeyInfo
436: .getPublicKeyData().getBytes()));
437:
438: recipientInfs.add(new RecipientInf(agreement
439: .generateSecret(cekWrapAlgorithm),
440: agreementAlgorithm, cekWrapAlgorithm, originator,
441: recipientCert));
442: } catch (IOException e) {
443: throw new InvalidKeyException(
444: "cannot extract originator public key: " + e);
445: }
446: }
447:
448: protected AlgorithmIdentifier getAlgorithmIdentifier(
449: String encryptionOID, AlgorithmParameters params)
450: throws IOException {
451: DEREncodable asn1Params;
452: if (params != null) {
453: ASN1InputStream aIn = new ASN1InputStream(params
454: .getEncoded("ASN.1"));
455:
456: asn1Params = aIn.readObject();
457: } else {
458: asn1Params = new DERNull();
459: }
460:
461: AlgorithmIdentifier encAlgId = new AlgorithmIdentifier(
462: new DERObjectIdentifier(encryptionOID), asn1Params);
463: return encAlgId;
464: }
465:
466: protected AlgorithmParameters generateParameters(
467: String encryptionOID, SecretKey encKey, String encProvider)
468: throws NoSuchProviderException, CMSException {
469: try {
470: AlgorithmParameterGenerator pGen = AlgorithmParameterGenerator
471: .getInstance(encryptionOID, encProvider);
472:
473: if (encryptionOID.equals(RC2_CBC)) {
474: byte[] iv = new byte[8];
475:
476: //
477: // mix in a bit extra...
478: //
479: rand.setSeed(System.currentTimeMillis());
480:
481: rand.nextBytes(iv);
482:
483: try {
484: pGen.init(new RC2ParameterSpec(
485: encKey.getEncoded().length * 8, iv));
486: } catch (InvalidAlgorithmParameterException e) {
487: throw new CMSException(
488: "parameters generation error: " + e, e);
489: }
490: }
491:
492: return pGen.generateParameters();
493: } catch (NoSuchAlgorithmException e) {
494: return null;
495: }
496: }
497: }
|