001 // Copyright (C) 2002 IAIK
002 // https://jce.iaik.tugraz.at
003 //
004 // Copyright (C) 2003 - 2025 Stiftung Secure Information and
005 // Communication Technologies SIC
006 // https://sic.tech
007 //
008 // All rights reserved.
009 //
010 // Redistribution and use in source and binary forms, with or without
011 // modification, are permitted provided that the following conditions
012 // are met:
013 // 1. Redistributions of source code must retain the above copyright
014 // notice, this list of conditions and the following disclaimer.
015 // 2. Redistributions in binary form must reproduce the above copyright
016 // notice, this list of conditions and the following disclaimer in the
017 // documentation and/or other materials provided with the distribution.
018 //
019 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
020 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
023 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
025 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
029 // SUCH DAMAGE.
030
031 // Copyright (C) 2002 IAIK
032 // https://sic.tech/
033 //
034 // Copyright (C) 2003 - 2025 Stiftung Secure Information and
035 // Communication Technologies SIC
036 // https://sic.tech/
037 //
038 // All rights reserved.
039 //
040 // This source is provided for inspection purposes and recompilation only,
041 // unless specified differently in a contract with IAIK. This source has to
042 // be kept in strict confidence and must not be disclosed to any third party
043 // under any circumstances. Redistribution in source and binary forms, with
044 // or without modification, are <not> permitted in any case!
045 //
046 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
047 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
048 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
049 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
050 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
051 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
052 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
053 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
054 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
055 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056 // SUCH DAMAGE.
057 //
058 // $Header: /IAIK-CMS/current/src/demo/cms/ecc/EckaEGAuthEnvelopedDataDemo.java 12 12.02.25 17:58 Dbratko $
059 // $Revision: 12 $
060 //
061
062
063 package demo.cms.ecc;
064
065 import iaik.asn1.CodingException;
066 import iaik.asn1.ObjectID;
067 import iaik.asn1.structures.AlgorithmID;
068 import iaik.asn1.structures.Attribute;
069 import iaik.cms.AuthEnvelopedData;
070 import iaik.cms.AuthEnvelopedDataOutputStream;
071 import iaik.cms.AuthEnvelopedDataStream;
072 import iaik.cms.CMSAlgorithmID;
073 import iaik.cms.CMSException;
074 import iaik.cms.CertificateIdentifier;
075 import iaik.cms.CompressedData;
076 import iaik.cms.CompressedDataOutputStream;
077 import iaik.cms.CompressedDataStream;
078 import iaik.cms.ContentInfo;
079 import iaik.cms.ContentInfoOutputStream;
080 import iaik.cms.ContentInfoStream;
081 import iaik.cms.EncryptedContentInfo;
082 import iaik.cms.EncryptedContentInfoStream;
083 import iaik.cms.IssuerAndSerialNumber;
084 import iaik.cms.KeyAgreeRecipientInfo;
085 import iaik.cms.KeyIdentifier;
086 import iaik.cms.RecipientInfo;
087 import iaik.cms.RecipientKeyIdentifier;
088 import iaik.cms.SignedData;
089 import iaik.cms.SignedDataStream;
090 import iaik.cms.SignerInfo;
091 import iaik.cms.attributes.CMSContentType;
092 import iaik.cms.attributes.SigningTime;
093 import iaik.security.random.SecRandom;
094 import iaik.utils.Util;
095 import iaik.x509.X509Certificate;
096
097 import java.io.ByteArrayInputStream;
098 import java.io.ByteArrayOutputStream;
099 import java.io.IOException;
100 import java.io.InputStream;
101 import java.security.InvalidKeyException;
102 import java.security.Key;
103 import java.security.NoSuchAlgorithmException;
104 import java.security.PrivateKey;
105 import java.security.SecureRandom;
106 import java.security.SignatureException;
107
108 import javax.crypto.SecretKey;
109
110 import demo.DemoUtil;
111 import demo.cms.ecc.keystore.CMSEccKeyStore;
112
113
114 /**
115 * Demonstrates the usage of class {@link iaik.cms.SignedDataStream} and
116 * {@link iaik.cms.SignedData}, and {@link iaik.cms.AuthEnvelopedDataStream} and
117 * {@link iaik.cms.AuthEnvelopedData} according to the BSI Technical
118 * Recommendation <a href =
119 * "https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR03109/TR-03109-1_Anlage_CMS.pdf;jsessionid=3DD6E4FBAC90766F8E2AB3F79BB003C5.1_cid341?__blob=publicationFile&v=1" target="_blank">
120 * BSI TR-03109-1</a> for transmitting signed authenticated encrypted
121 * data between Smart-Meter-Gateways and external market participants and the
122 * Smart Meter Gateway Administrator.
123 * <p>
124 * This demo uses AES-GCM as specified by <a href = "http://www.ietf.org/rfc/rfc5084.txt" target="_blank">RFC 5084</a>
125 * and AES-CBC-CMAC as specified by BSI TR-03109-1 for authenticated encryption.
126 * The demo compressed the data, creates an AuthEnvelopedData object, packs it into a SignedData and
127 * subsequently shows several ways that may be used for decrypting the content
128 * for some particular recipient.
129 * <p>
130 * Any keys/certificates required for this demo are read from a keystore
131 * file "cmsecc.keystore" located in your current working directory. If
132 * the keystore file does not exist you can create it by running the
133 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
134 * program.
135 * <p>
136 * Additionally to <code>iaik_cms.jar</code> you also must have
137 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
138 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
139 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
140 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
141 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
142 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
143 * in your classpath.
144 *
145 * @see iaik.cms.AuthEnvelopedDataStream
146 * @see iaik.cms.AuthEnvelopedData
147 * @see iaik.cms.SignedDataStream
148 * @see iaik.cms.SignedData
149 * @see iaik.cms.RecipientInfo
150 * @see iaik.cms.KeyAgreeRecipientInfo
151 *
152 */
153 public class EckaEGAuthEnvelopedDataDemo {
154
155 // certificate of signer
156 X509Certificate[] ecdsa256bitSignerCertChain_;
157 // private key of signer
158 PrivateKey ecdsa256bitSignerPrivateKey_;
159
160 // certificate of signer
161 X509Certificate[] ecdsa384bitSignerCertChain_;
162 // private key of signer
163 PrivateKey ecdsa384bitSignerPrivateKey_;
164
165 //certificate of signer
166 X509Certificate[] ecdsa521bitSignerCertChain_;
167 // private key of signer
168 PrivateKey ecdsa521bitSignerPrivateKey_;
169
170
171 // certificate of recipient 1 (recipient 1 is signer)
172 X509Certificate ecdh256bitRecipient1Cert_;
173 // private key of recipient 1
174 PrivateKey ecdh256bitRecipient1PrivateKey_;
175 // certificate of recipient 2
176 X509Certificate ecdh256bitRecipient2Cert_;
177 // private key of recipient 2
178 PrivateKey ecdh256bitRecipient2PrivateKey_;
179
180 //certificate of recipient 1 (recipient 1 is signer)
181 X509Certificate ecdh384bitRecipient1Cert_;
182 // private key of recipient 1
183 PrivateKey ecdh384bitRecipient1PrivateKey_;
184 // certificate of recipient 2
185 X509Certificate ecdh384bitRecipient2Cert_;
186 // private key of recipient 2
187 PrivateKey ecdh384bitRecipient2PrivateKey_;
188
189 //certificate of recipient 1 (recipient 1 is signer)
190 X509Certificate ecdh521bitRecipient1Cert_;
191 // private key of recipient 1
192 PrivateKey ecdh521bitRecipient1PrivateKey_;
193 // certificate of recipient 2
194 X509Certificate ecdh521bitRecipient2Cert_;
195 // private key of recipient 2
196 PrivateKey ecdh521bitRecipient2PrivateKey_;
197
198 // secure random number generator
199 SecureRandom random;
200
201 /**
202 * Setup the demo certificate chains.
203 *
204 * Keys and certificates are retrieved from the demo KeyStore ("cmsecc.keystore")
205 * file which has to be located in your current working directory and may be
206 * created by running {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore
207 * SetupCMSEccKeyStore}.
208 *
209 * @throws IOException if an file read error occurs
210 */
211 public EckaEGAuthEnvelopedDataDemo() throws IOException {
212
213 System.out.println();
214 System.out.println("**********************************************************************************");
215 System.out.println("* EckaEGAuthEnvelopedDataDemo *");
216 System.out.println("* (CMS AuthEnvelopedData/SignedData for type implementation for BSI TR-03109-1) *");
217 System.out.println("**********************************************************************************");
218 System.out.println();
219
220 ecdsa256bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN);
221 ecdsa256bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN);
222 ecdsa384bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN);
223 ecdsa384bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN);
224 ecdsa521bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN);
225 ecdsa521bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN);
226
227
228 ecdh256bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_1)[0];
229 ecdh256bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_1);
230 ecdh256bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_2)[0];
231 ecdh256bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_2);
232
233 ecdh384bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_1)[0];
234 ecdh384bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_1);
235 ecdh384bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_2)[0];
236 ecdh384bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_2);
237
238 ecdh521bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_1)[0];
239 ecdh521bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_1);
240 ecdh521bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_2)[0];
241 ecdh521bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_2);
242 random = SecRandom.getDefault();
243
244 }
245
246
247 /**
248 * Creates a CMS <code>AuthEnvelopedData</code> message using class <code>AuthEnvelopedDataStream</code>.
249 *
250 * @param message the message to be authenticated-enveloped, as byte representation
251 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
252 * @param recipient1Cert the certificate of the first recipient (sender)
253 * @param recipient2Cert the certificate of the second recipient *
254 *
255 * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
256 *
257 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
258 * be created
259 * @throws IOException if an I/O error occurs
260 */
261 public byte[] createAuthEnvelopedDataStream(byte[] message,
262 AlgorithmID contentAuthEncAlg,
263 X509Certificate recipient1Cert,
264 X509Certificate recipient2Cert)
265 throws CMSException, IOException {
266
267 System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg);
268
269 // we are testing the stream interface
270 ByteArrayInputStream is = new ByteArrayInputStream(message);
271 // create a new AuthEnvelopedData object
272 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(ObjectID.cms_compressedData, is, contentAuthEncAlg);
273
274 // create some authenticated attributes
275 try {
276 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
277 authEnvelopedData.setAuthenticatedAttributes(attributes);
278 } catch (Exception ex) {
279 throw new CMSException("Error creating attribute: " + ex.toString());
280 }
281
282 // create the recipient infos
283 RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert);
284 // specify the recipients of the encrypted message
285 authEnvelopedData.setRecipientInfos(recipients);
286
287 // wrap into ContentInfo
288 ContentInfoStream contentInfo = new ContentInfoStream(authEnvelopedData);
289 ByteArrayOutputStream os = new ByteArrayOutputStream();
290 contentInfo.writeTo(os);
291 return os.toByteArray();
292 }
293
294 /**
295 * Creates a CMS <code>AuthEnvelopedData</code> message using class
296 * <code>AuthEnvelopedDataOutputStream</code>. The content data is
297 * compressed inside this method.
298 *
299 * @param message the message to be authenticated-enveloped, as byte representation
300 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
301 * @param recipient1Cert the certificate of the first recipient (sender)
302 * @param recipient2Cert the certificate of the second recipient
303 *
304 * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created
305 *
306 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
307 * be created
308 * @throws IOException if an I/O error occurs
309 */
310 public byte[] createAuthEnvelopedDataOutputStream(byte[] message,
311 AlgorithmID contentAuthEncAlg,
312 X509Certificate recipient1Cert,
313 X509Certificate recipient2Cert)
314 throws CMSException, IOException {
315
316 System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg);
317
318 // we are testing the stream interface
319 ByteArrayInputStream is = new ByteArrayInputStream(message);
320 // the stream to which to write the AuthEnvelopedData
321 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
322 AuthEnvelopedDataOutputStream authEnvelopedData;
323
324 // wrap AuthEnvelopedData into a ContentInfo
325 ContentInfoOutputStream contentInfoStream =
326 new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream);
327
328 // create a new AuthEnvelopedData object
329 authEnvelopedData = new AuthEnvelopedDataOutputStream(contentInfoStream,
330 contentAuthEncAlg);
331
332 // create the recipient infos
333 RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert);
334 // specify the recipients of the encrypted message
335 authEnvelopedData.setRecipientInfos(recipients);
336
337 // create some authenticated attributes
338 try {
339 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
340 authEnvelopedData.setAuthenticatedAttributes(attributes);
341 } catch (Exception ex) {
342 throw new CMSException("Error creating attribute: " + ex.toString());
343 }
344
345 // compress message
346 CompressedDataOutputStream compressedData =
347 new CompressedDataOutputStream(authEnvelopedData,
348 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone());
349
350 int blockSize = 16; // in real world we would use a block size like 2048
351 // write in the data to be encrypted
352 byte[] buffer = new byte[blockSize];
353 int bytesRead;
354 while ((bytesRead = is.read(buffer)) != -1) {
355 compressedData.write(buffer, 0, bytesRead);
356 }
357
358 // closing the stream finishes encryption and closes the underlying stream
359 compressedData.close();
360 // authEnvelopedData.close();
361 return resultStream.toByteArray();
362 }
363
364
365 /**
366 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
367 * the recipient identified by its index into the recipientInfos field and verifies
368 * the message authentication code.
369 * <p>
370 * This way of decrypting the content may be used for any type of RecipientInfo
371 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
372 * know at what index of the recipientInfo field the RecipientInfo for the
373 * particular recipient in mind can be found. If the recipient in mind uses
374 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
375 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
376 * keys for more than only one recipient; since the recipientInfoIndex only
377 * specifies the RecipientInfo but not the encrypted content encryption key
378 * -- if there are more than only one -- repeated decryption runs may be
379 * required as long as the decryption process completes successfully.
380 *
381 * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array
382 * @param key the key to decrypt the message
383 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
384 * to which the specified key belongs
385 *
386 * @return the recovered message, as byte array
387 * @throws CMSException if the message cannot be recovered or MAC verification fails
388 * @throws IOException if a stream read/write error occurs
389 */
390 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex)
391 throws CMSException, IOException {
392
393 // create the AuthEnvelopedData object from a BER encoded byte array
394 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
395 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
396
397 System.out.println("Information about the authenticated encrypted data:");
398 EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
399 System.out.println("Content type: "+eci.getContentType().getName());
400 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
401
402 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
403 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
404
405 // for demonstration purposes we only look one time for all recipients included:
406 if (recipientInfoIndex == 0) {
407 int k = 0;
408 for (int i=0; i<recipients.length; i++) {
409 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
410 for (int j = 0; j < recipientIDs.length; j++) {
411 System.out.println("Recipient "+(++k)+":");
412 System.out.println(recipientIDs[j]);
413 }
414 }
415 }
416 // decrypt the message for the first recipient and verify mac
417 try {
418 authEnvelopedData.setupCipher(key, recipientInfoIndex);
419 InputStream decrypted = authEnvelopedData.getInputStream();
420 ByteArrayOutputStream os = new ByteArrayOutputStream();
421 Util.copyStream(decrypted, os, null);
422 byte[] content = os.toByteArray();
423
424 // get authenticated attributes
425 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
426 if (contentTypeAttribute != null) {
427 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
428 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
429 }
430
431 return content;
432 } catch (InvalidKeyException ex) {
433 throw new CMSException("Private key error: "+ex.toString());
434 } catch (NoSuchAlgorithmException ex) {
435 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
436 } catch (CodingException ex) {
437 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
438 }
439 }
440
441 /**
442 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for
443 * the recipient identified by recipient identifier and verifies the message
444 * authentication code.
445 * <p>
446 * This way of decrypting the content may be used for any type of RecipientInfo
447 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The
448 * recipient in mind is identified by its recipient identifier.
449 *
450 * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
451 * @param key the key to decrypt the message
452 * @param recipientID the recipient identifier uniquely identifying the key of the
453 * recipient
454 *
455 * @return the recovered message, as byte array
456 * @throws CMSException if the message cannot be recovered
457 * @throws IOException if a stream read/write error occurs
458 */
459 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID)
460 throws CMSException, IOException {
461
462 // create the AuthEnvelopedData object from a DER encoded byte array
463 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
464 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
465
466 System.out.println("Information about the encrypted data:");
467 EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo();
468 System.out.println("Content type: "+eci.getContentType().getName());
469 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
470
471 // get the right RecipientInfo
472 System.out.println("\nSearch for RecipientInfo:");
473 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
474 if (recipient != null) {
475 System.out.println("RecipientInfo: " + recipient);
476 } else {
477 throw new CMSException("No recipient with ID: " + recipientID);
478 }
479 // decrypt the content encryption key and the content; verify mac
480 try {
481 System.out.println("Decrypt encrypted content encryption key...");
482 SecretKey cek = recipient.decryptKey(key, recipientID);
483 System.out.println("Decrypt content with decrypted content encryption key...");
484 authEnvelopedData.setupCipher(cek);
485 InputStream decrypted = authEnvelopedData.getInputStream();
486 ByteArrayOutputStream os = new ByteArrayOutputStream();
487 Util.copyStream(decrypted, os, null);
488 byte[] content = os.toByteArray();
489
490 // get authenticated attributes
491 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
492 if (contentTypeAttribute != null) {
493 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
494 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
495 }
496
497 return content;
498 } catch (InvalidKeyException ex) {
499 throw new CMSException("Private key error: "+ex.toString());
500 } catch (NoSuchAlgorithmException ex) {
501 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
502 } catch (CodingException ex) {
503 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
504 }
505 }
506
507 /**
508 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
509 * the recipient identified by its recipient certificate and verifies the message
510 * authentication code.
511 * <p>
512 *
513 * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array
514 * @param key the key to decrypt the message
515 * @param recipientCert the certificate of the recipient having a RecipientInfo of
516 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo
517 *
518 * @return the recovered message, as byte array
519 * @throws CMSException if the message cannot be recovered
520 * @throws IOException if a stream read/write error occurs
521 */
522 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert)
523 throws CMSException, IOException {
524
525 // create the AuthEnvelopedData object from a BER encoded byte array
526 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
527 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is);
528
529 System.out.println("Information about the encrypted data:");
530 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo();
531 System.out.println("Content type: "+eci.getContentType().getName());
532 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
533
534 // decrypt the content encryption key and the content; verify mac
535 try {
536 System.out.println("Decrypt the content...");
537 authEnvelopedData.setupCipher(key, recipientCert);
538 InputStream decrypted = authEnvelopedData.getInputStream();
539 ByteArrayOutputStream os = new ByteArrayOutputStream();
540 Util.copyStream(decrypted, os, null);
541 byte[] content = os.toByteArray();
542
543 // get authenticated attributes
544 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
545 if (contentTypeAttribute != null) {
546 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
547 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
548 }
549
550 return content;
551 } catch (InvalidKeyException ex) {
552 throw new CMSException("Private key error: "+ex.toString());
553 } catch (NoSuchAlgorithmException ex) {
554 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
555 } catch (CodingException ex) {
556 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
557 }
558 }
559
560
561 // non stream
562
563 /**
564 * Creates a CMS <code>AuthEnvelopedData</code> message.
565 *
566 * @param message the message to be enveloped, as byte representation
567 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
568 * @param recipient1Cert the certificate of the first recipient (sender)
569 * @param recipient2Cert the certificate of the second recipient
570 *
571 * @return the encoded <code>AuthEnvelopedData</code>, as byte array
572 *
573 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot
574 * be created
575 */
576 public byte[] createAuthEnvelopedData(
577 byte[] message,
578 AlgorithmID contentAuthEncAlg,
579 X509Certificate recipient1Cert,
580 X509Certificate recipient2Cert)
581 throws CMSException {
582
583 System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg);
584
585 AuthEnvelopedData authEnvelopedData;
586
587 // create a new AuthEnvelopedData object
588 authEnvelopedData = new AuthEnvelopedData(ObjectID.cms_compressedData, message, contentAuthEncAlg);
589
590 // create some authenticated attributes
591 try {
592 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) };
593 authEnvelopedData.setAuthenticatedAttributes(attributes);
594 } catch (Exception ex) {
595 throw new CMSException("Error creating attribute: " + ex.toString());
596 }
597
598 // set the RecipientInfos
599 RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert);
600 authEnvelopedData.setRecipientInfos(recipients);
601 authEnvelopedData.getEncryptedContentInfo().setBlockSize(2048);
602
603 // wrap into ContentInfo
604 ContentInfo contentInfo = new ContentInfo(authEnvelopedData);
605 // return encoded EnvelopedData
606 return contentInfo.getEncoded();
607 }
608
609
610 /**
611 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
612 * the recipient identified by its index into the recipientInfos field and verifies
613 * the message authentication code.
614 * <p>
615 * This way of decrypting the content may be used for any type of RecipientInfo
616 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to
617 * know at what index of the recipientInfo field the RecipientInfo for the
618 * particular recipient in mind can be found. If the recipient in mind uses
619 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may
620 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption
621 * keys for more than only one recipient; since the recipientInfoIndex only
622 * specifies the RecipientInfo but not the encrypted content encryption key
623 * -- if there are more than only one -- repeated decryption runs may be
624 * required as long as the decryption process completes successfully.
625 *
626 * @param enc the encoded <code>AuthEnvelopedData</code>
627 *
628 * @param key the key to decrypt the message
629 *
630 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
631 * to which the specified key belongs
632 *
633 * @return the recovered message, as byte array
634 *
635 * @throws CMSException if the message cannot be recovered
636 * @throws IOException if an I/O error occurs
637 */
638 public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex)
639 throws CMSException, IOException {
640 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
641 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
642
643 System.out.println("Information about the encrypted data:");
644 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
645 System.out.println("Content type: "+eci.getContentType().getName());
646 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
647
648 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
649 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos();
650
651 // for demonstration purposes we only look one time for all recipients included:
652 if (recipientInfoIndex == 0) {
653 int k = 0;
654 for (int i=0; i<recipients.length; i++) {
655 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers();
656 for (int j = 0; j < recipientIDs.length; j++) {
657 System.out.println("Recipient "+(++k)+":");
658 System.out.println(recipientIDs[j]);
659 }
660 }
661 }
662
663 // decrypt the message and verify the mac
664 try {
665 authEnvelopedData.setupCipher(key, recipientInfoIndex);
666 byte[] content = authEnvelopedData.getContent();
667
668 // get authenticated attributes
669 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
670 if (contentTypeAttribute != null) {
671 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
672 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
673 }
674
675 return content;
676 } catch (InvalidKeyException ex) {
677 throw new CMSException("Private key error: "+ex.toString());
678 } catch (NoSuchAlgorithmException ex) {
679 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
680 } catch (CodingException ex) {
681 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
682 }
683 }
684
685 /**
686 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
687 * the recipient identified by recipient identifier.
688 * <p>
689 * This way of decrypting the content may be used for any type of RecipientInfo
690 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The
691 * recipient in mind is identified by its recipient identifier.
692 *
693 * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
694 * @param key the key to decrypt the message
695 * @param recipientID the recipient identifier uniquely identifying the key of the
696 * recipient
697 *
698 * @return the recovered message, as byte array
699 * @throws CMSException if the message cannot be recovered
700 * @throws IOException if an I/O error occurs
701 */
702 public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID)
703 throws CMSException, IOException {
704 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
705 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
706
707 System.out.println("Information about the encrypted data:");
708 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
709 System.out.println("Content type: "+eci.getContentType().getName());
710 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
711
712 // get the right RecipientInfo
713 System.out.println("\nSearch for RecipientInfo:");
714 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID);
715 if (recipient != null) {
716 System.out.println("RecipientInfo: " + recipient);
717 } else {
718 throw new CMSException("No recipient with ID: " + recipientID);
719 }
720 // decrypt the content encryption key and the content
721 try {
722 byte[] content = null;
723 System.out.println("Decrypt encrypted content encryption key...");
724 SecretKey cek = recipient.decryptKey(key, recipientID);
725 // decrypt content and verify mac
726 System.out.println("Decrypt content with decrypted content encryption key...");
727 authEnvelopedData.setupCipher(cek);
728 content = authEnvelopedData.getContent();
729 // get authenticated attributes
730 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
731 if (contentTypeAttribute != null) {
732 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
733 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
734 }
735
736 return content;
737 } catch (InvalidKeyException ex) {
738 throw new CMSException("Private key error: "+ex.toString());
739 } catch (NoSuchAlgorithmException ex) {
740 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
741 } catch (CodingException ex) {
742 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
743 }
744 }
745
746 /**
747 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for
748 * the recipient identified by its recipient certificate.
749 *
750 * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object
751 * @param key the key to decrypt the message
752 * @param recipientCert the certificate of the recipient having a RecipientInfo of
753 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo
754 *
755 * @return the recovered message, as byte array
756 * @throws CMSException if the message cannot be recovered
757 */
758 public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert)
759 throws CMSException, IOException {
760 ByteArrayInputStream bais = new ByteArrayInputStream(enc);
761 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais);
762
763 System.out.println("Information about the encrypted data:");
764 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo();
765 System.out.println("Content type: "+eci.getContentType().getName());
766 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
767
768 // decrypt the content encryption key and the content
769 try {
770 System.out.println("Decrypt the content and verify mac...");
771 // decrypt content and verify mac
772 authEnvelopedData.setupCipher(key, recipientCert);
773
774 byte[] content = authEnvelopedData.getContent();
775
776 // get authenticated attributes
777 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType);
778 if (contentTypeAttribute != null) {
779 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue();
780 System.out.println("Authenticated content type attribute included: " + contentType.get().getName());
781 }
782
783 return content;
784 } catch (InvalidKeyException ex) {
785 throw new CMSException("Private key error: "+ex.toString());
786 } catch (NoSuchAlgorithmException ex) {
787 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString());
788 } catch (CodingException ex) {
789 throw new CMSException("Error reading authenticated attributes: "+ex.toString());
790 }
791
792 }
793
794 /**
795 * Creates the RecipientInfos.
796 *
797 * @param contentAuthEncAlg the content encryption algorithm
798 * @param recipient1Cert the certificate of the first recipient (sender)
799 * @param recipient2Cert the certificate of the second recipient
800 *
801 * @return the RecipientInfos created, one
802 * KeyAgreeRecipientInfo (for two recipients with same domain
803 * parameters)
804 *
805 * @throws CMSException if an error occurs when creating the recipient infos
806 */
807 public RecipientInfo[] createRecipients(
808 AlgorithmID contentAuthEncAlg,
809 X509Certificate recipient1Cert,
810 X509Certificate recipient2Cert) throws CMSException {
811
812 RecipientInfo[] recipients = new RecipientInfo[1];
813 try {
814
815 // next recipients use key agreement
816 // the key encryption (key agreement) algorithm to use:
817 AlgorithmID keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA256.clone();
818 // the key wrap algorithm to use:
819 AlgorithmID keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone();
820 // the length of the key encryption key to be generated:
821 int kekLength = 128;
822 if ((contentAuthEncAlg.equals(AlgorithmID.aes192_GCM) ||
823 (contentAuthEncAlg.equals(AlgorithmID.aes_CBC_CMAC_192)))) {
824 keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA384.clone();
825 keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone();
826 kekLength = 192;
827 } else if ((contentAuthEncAlg.equals(AlgorithmID.aes256_GCM) ||
828 (contentAuthEncAlg.equals(AlgorithmID.aes_CBC_CMAC_256)))) {
829 keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA512.clone();
830 keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone();
831 kekLength = 256;
832 }
833 recipients[0] = new KeyAgreeRecipientInfo(keyEA, keyWrapAlg, kekLength);
834 // ecdhUser1 is the third receiver (cert identified by IssuerAndSerialNumber)
835 ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(recipient1Cert, CertificateIdentifier.ISSUER_AND_SERIALNUMBER);
836 // ecdhUser2 is the fourth receiver (cert identified by RecipientKeyIdentifier)
837 ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(recipient2Cert, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER);
838
839 } catch (Exception ex) {
840 throw new CMSException("Error adding recipients: " + ex.getMessage());
841 }
842 return recipients;
843 }
844
845 /**
846 * Parses an AuthEnvelopedData and decrypts the content for all test recipients
847 * using the index into the recipientInfos field for identifying the recipient.
848 *
849 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
850 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
851 * @param recipient1PrivateKey the private key of the first recipient (sender)
852 * @param recipient2PrivateKey the private key of the second recipient
853 *
854 * @throws Exception if some error occurs during decoding/decryption
855 */
856 public void parseAuthEnvelopedDataWithRecipientInfoIndex(
857 boolean stream,
858 byte[] encodedAuthEnvelopedData,
859 PrivateKey recipient1PrivateKey,
860 PrivateKey recipient2PrivateKey) throws Exception {
861 byte[] receivedMessage;
862 if (stream) {
863 // ecdhUser1
864 System.out.println("\nDecrypt for ecdhUser1:");
865 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, 0);
866 // decompress
867 receivedMessage = getCompressedDataStream(receivedMessage);
868 System.out.print("\nDecrypted content: ");
869 System.out.println(new String(receivedMessage));
870 // ecdhUser2
871 System.out.println("\nDecrypt for ecdhUser2:");
872 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, 0);
873 // decompress
874 receivedMessage = getCompressedDataStream(receivedMessage);
875 System.out.print("\nDecrypted content: ");
876 System.out.println(new String(receivedMessage));
877
878 } else {
879 // ecdhUser1
880 System.out.println("\nDecrypt for ecdhUser1:");
881 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, 0);
882 // decompress
883 receivedMessage = getCompressedData(receivedMessage);
884 System.out.print("\nDecrypted content: ");
885 System.out.println(new String(receivedMessage));
886 // ecdhUser2
887 System.out.println("\nDecrypt for ecdhUser2:");
888 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, 0);
889 // decompress
890 receivedMessage = getCompressedData(receivedMessage);
891 System.out.print("\nDecrypted content: ");
892 System.out.println(new String(receivedMessage));
893
894 }
895 }
896
897 /**
898 * Parses an AuthEnvelopedData and decrypts the content for all test recipients
899 * using their recipient identifiers for identifying the recipient.
900 *
901 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
902 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
903 * @param recipient1PrivateKey the private key of the first recipient (sender)
904 * @param recipient1Cert the certificate of the first recipient (sender)
905 * @param recipient2PrivateKey the private key of the second recipient
906 * @param recipient2Cert the certificate of the second recipient
907 *
908 * @throws Exception if some error occurs during decoding/decryption
909 */
910 public void parseAuthEnvelopedDataWithRecipientIdentifier(
911 boolean stream,
912 byte[] encodedAuthEnvelopedData,
913 PrivateKey recipient1PrivateKey,
914 X509Certificate recipient1Cert,
915 PrivateKey recipient2PrivateKey,
916 X509Certificate recipient2Cert) throws Exception {
917 byte[] receivedMessage;
918 if (stream) {
919 // ecdhUser1
920 System.out.println("\nDecrypt for ecdhUser1:");
921 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, new IssuerAndSerialNumber(recipient1Cert));
922 // decompress
923 receivedMessage = getCompressedDataStream(receivedMessage);
924 System.out.print("\nDecrypted content: ");
925 System.out.println(new String(receivedMessage));
926 // ecdhUser2
927 System.out.println("\nDecrypt for ecdhUser2:");
928 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, new RecipientKeyIdentifier(recipient2Cert));
929 // decompress
930 receivedMessage = getCompressedDataStream(receivedMessage);
931 System.out.print("\nDecrypted content: ");
932 System.out.println(new String(receivedMessage));
933 } else {
934 // ecdhUser1
935 System.out.println("\nDecrypt for ecdhUser1:");
936 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, new IssuerAndSerialNumber(recipient1Cert));
937 // decompress
938 receivedMessage = getCompressedData(receivedMessage);
939 System.out.print("\nDecrypted content: ");
940 System.out.println(new String(receivedMessage));
941 // ecdhUser2
942 System.out.println("\nDecrypt for ecdhUser2:");
943 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, new RecipientKeyIdentifier(recipient2Cert));
944 // decompress
945 receivedMessage = getCompressedData(receivedMessage);
946 System.out.print("\nDecrypted content: ");
947 System.out.println(new String(receivedMessage));
948 }
949 }
950
951 /**
952 * Parses an AuthEnvelopedData and decrypts the content for all test recipients
953 * using their recipient certificate for identifying the recipient.
954 *
955 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData
956 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object
957 * @param recipient1PrivateKey the private key of the first recipient (sender)
958 * @param recipient1Cert the certificate of the first recipient (sender)
959 * @param recipient2PrivateKey the private key of the second recipient
960 * @param recipient2Cert the certificate of the second recipient
961 *
962 * @throws Exception if some error occurs during decoding/decryption
963 */
964 public void parseAuthEnvelopedDataWithRecipientCert(
965 boolean stream,
966 byte[] encodedAuthEnvelopedData,
967 PrivateKey recipient1PrivateKey,
968 X509Certificate recipient1Cert,
969 PrivateKey recipient2PrivateKey,
970 X509Certificate recipient2Cert) throws Exception {
971 byte[] receivedMessage;
972 if (stream) {
973 // ecdhUser1
974 System.out.println("\nDecrypt for ecdhUser1:");
975 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert);
976 // decompress
977 receivedMessage = getCompressedDataStream(receivedMessage);
978 System.out.print("\nDecrypted content: ");
979 System.out.println(new String(receivedMessage));
980 // ecdhUser2
981 System.out.println("\nDecrypt for ecdhUser2:");
982 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, recipient2Cert);
983 // decompress
984 receivedMessage = getCompressedDataStream(receivedMessage);
985 System.out.print("\nDecrypted content: ");
986 System.out.println(new String(receivedMessage));
987 } else {
988 // ecdhUser1
989 System.out.println("\nDecrypt for ecdhUser1:");
990 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert);
991 // decompress
992 receivedMessage = getCompressedData(receivedMessage);
993 System.out.print("\nDecrypted content: ");
994 System.out.println(new String(receivedMessage));
995 // ecdhUser2
996 System.out.println("\nDecrypt for ecdhUser2:");
997 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, recipient2Cert);
998 // decompress
999 receivedMessage = getCompressedData(receivedMessage);
1000 System.out.print("\nDecrypted content: ");
1001 System.out.println(new String(receivedMessage));
1002 }
1003 }
1004
1005 /**
1006 * Creates an ECDSA signed CMS <code>SignedDataStream</code> object and wraps it by a
1007 * CMS <code>ContentInfoStream</code>.
1008 *
1009 * @param message the message to be signed, as byte representation
1010 * @param mode the transmission mode, either IMPLICIT or EXPLICIT
1011 * @param hashAlgorithm the hash algorithm to be used
1012 * @param signatureAlgorithm the signature algorithm to be used
1013 * @param signerKey the private key of the signer
1014 * @param certificates the certificate chain of the signer
1015 *
1016 * @return the DER encoding of the <code>ContentInfo</code> object just created
1017 *
1018 * @throws CMSException if the <code>SignedData</code>, <code>ContentInfo</code>
1019 * object cannot be created
1020 * @throws IOException if an I/O related error occurs
1021 */
1022 public byte[] createSignedDataStream(byte[] message,
1023 int mode,
1024 AlgorithmID hashAlgorithm,
1025 AlgorithmID signatureAlgorithm,
1026 PrivateKey signerKey,
1027 X509Certificate[] certificates)
1028 throws CMSException, IOException {
1029
1030 System.out.print("Create a new message signed with " + signatureAlgorithm.getName());
1031
1032 // we are testing the stream interface
1033 ByteArrayInputStream is = new ByteArrayInputStream(message);
1034 // create a new SignedData object which includes the data
1035 SignedDataStream signed_data = new SignedDataStream(is, ObjectID.cms_authEnvelopedData, mode);
1036
1037 // SignedData shall include the certificate chain for verifying
1038 signed_data.setCertificates(certificates);
1039
1040 // cert at index 0 is the user certificate
1041 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]);
1042
1043 // create a new SignerInfo
1044 AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone();
1045 // CMS-ECDSA requires to encode the parameter field as NULL (see RFC 3278)
1046 ecdsaSig.encodeAbsentParametersAsNull(true);
1047 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey);
1048
1049 try {
1050 // create some signed attributes
1051 // the message digest attribute is automatically added
1052 Attribute[] attributes = new Attribute[2];
1053 // content type is data
1054 CMSContentType contentType = new CMSContentType(ObjectID.cms_authEnvelopedData);
1055 attributes[0] = new Attribute(contentType);
1056 // signing time is now
1057 SigningTime signingTime = new SigningTime();
1058 attributes[1] = new Attribute(signingTime);
1059
1060 // set the attributes
1061 signer_info.setSignedAttributes(attributes);
1062 } catch (Exception ex) {
1063 throw new CMSException("Error adding attributes: " + ex.toString());
1064 }
1065
1066 // finish the creation of SignerInfo by calling method addSigner
1067 try {
1068 signed_data.addSignerInfo(signer_info);
1069 } catch (NoSuchAlgorithmException ex) {
1070 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
1071 }
1072
1073 // write the data through SignedData to any out-of-band place
1074 if (mode == SignedDataStream.EXPLICIT) {
1075 InputStream data_is = signed_data.getInputStream();
1076 byte[] buf = new byte[1024];
1077 int r;
1078 while ((r = data_is.read(buf)) > 0) {
1079 ; // skip data
1080 }
1081 }
1082
1083 signed_data.setBlockSize(2048);
1084 // create the ContentInfo
1085 ContentInfoStream cis = new ContentInfoStream(signed_data);
1086 // return the SignedData as DER encoded byte array with block size 2048
1087 ByteArrayOutputStream os = new ByteArrayOutputStream();
1088 cis.writeTo(os);
1089 return os.toByteArray();
1090 }
1091
1092
1093 /**
1094 * Parses a CMS <code>ContentInfo</code> object holding a <code>SignedData</code>
1095 * object and verifies the signature.
1096 *
1097 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code>
1098 * object as BER encoded byte array
1099 * @param message the the message which was transmitted out-of-band (explicit signed)
1100 * @param certificates the certificate of the signer (used for alternative signature verification)
1101 *
1102 * @return the inherent message as byte array
1103 *
1104 * @throws CMSException if any signature does not verify
1105 * @throws IOException if an I/O related error occurs
1106 */
1107 public byte[] getSignedDataStream(byte[] signedData, byte[] message, X509Certificate[] certificates)
1108 throws CMSException, IOException {
1109
1110 // we are testing the stream interface
1111 ByteArrayInputStream is = new ByteArrayInputStream(signedData);
1112
1113 SignedDataStream signed_data = new SignedDataStream(is);
1114
1115 if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
1116 // in explicit mode explicitly supply the content for hash computation
1117 signed_data.setInputStream(new ByteArrayInputStream(message));
1118 }
1119
1120 // get an InputStream for reading the signed content and update hash computation
1121 InputStream data = signed_data.getInputStream();
1122 ByteArrayOutputStream os = new ByteArrayOutputStream();
1123 Util.copyStream(data, os, null);
1124
1125 System.out.println("SignedData contains the following signer information:");
1126 SignerInfo[] signer_infos = signed_data.getSignerInfos();
1127
1128 int numberOfSignerInfos = signer_infos.length;
1129 if (numberOfSignerInfos == 0) {
1130 String warning = "Warning: Unsigned message (no SignerInfo included)!";
1131 System.err.println(warning);
1132 throw new CMSException(warning);
1133 } else {
1134 for (int i = 0; i < numberOfSignerInfos; i++) {
1135 try {
1136 // verify the signed data using the SignerInfo at index i
1137 X509Certificate signer_cert = signed_data.verify(i);
1138 // if the signature is OK the certificate of the signer is returned
1139 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
1140 // check for some included attributes
1141 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
1142 if (signingTime != null) {
1143 System.out.println("This message has been signed at " + signingTime.get());
1144 }
1145 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
1146 if (contentType != null) {
1147 System.out.println("The content has CMS content type " + contentType.get().getName());
1148 }
1149 } catch (SignatureException ex) {
1150 // if the signature is not OK a SignatureException is thrown
1151 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
1152 throw new CMSException(ex.toString());
1153 }
1154 }
1155 // now check alternative signature verification
1156 System.out.println("Now check the signature assuming that no certs have been included:");
1157 try {
1158 SignerInfo signer_info = signed_data.verify(certificates[0]);
1159 // if the signature is OK the certificate of the signer is returned
1160 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
1161 } catch (SignatureException ex) {
1162 // if the signature is not OK a SignatureException is thrown
1163 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN());
1164 throw new CMSException(ex.toString());
1165 }
1166 // in practice we also would validate the signer certificate(s)
1167 }
1168 return os.toByteArray();
1169 }
1170
1171
1172 /**
1173 * Creates an ECDSA signed CMS <code>SignedData</code> object and wraps it by a CMS
1174 * <code>ContentInfo</code> object.
1175 * <p>
1176 *
1177 * @param message the message to be signed, as byte representation
1178 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
1179 * @param hashAlgorithm the hash algorithm to be used
1180 * @param signatureAlgorithm the signature algorithm to be used
1181 * @param signerKey the private key of the signer
1182 * @param certificates the certificate chain of the signer
1183 *
1184 * @return the DER encoded <code>SignedData</code>-<code>ContentInfo</code> object
1185 *
1186 * @throws CMSException if the <code>SignedData</code>-<code>ContentInfo</code> object cannot
1187 * be created
1188 * @throws IOException if an I/O related error occurs
1189 */
1190 public byte[] createSignedData(byte[] message,
1191 int mode,
1192 AlgorithmID hashAlgorithm,
1193 AlgorithmID signatureAlgorithm,
1194 PrivateKey signerKey,
1195 X509Certificate[] certificates)
1196 throws CMSException, IOException {
1197
1198 System.out.println("Create a new message signed with " + signatureAlgorithm.getName());
1199
1200 // create a new SignedData object which includes the data
1201 SignedData signed_data = new SignedData(message, ObjectID.cms_authEnvelopedData, mode);
1202
1203 // SignedData shall include the certificate chain for verifying
1204 signed_data.setCertificates(certificates);
1205
1206 // cert at index 0 is the user certificate
1207 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]);
1208
1209 // create a new SignerInfo
1210 AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone();
1211 // CMS-ECC requires that the parameters field is encoded as ASN.1 NULL object (see RFC 3278)
1212 ecdsaSig.encodeAbsentParametersAsNull(true);
1213 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey);
1214
1215 try {
1216 // create some signed attributes
1217 // the message digest attribute is automatically added
1218 Attribute[] attributes = new Attribute[2];
1219 // content type is data
1220 CMSContentType contentType = new CMSContentType(ObjectID.cms_authEnvelopedData);
1221 attributes[0] = new Attribute(contentType);
1222 // signing time is now
1223 SigningTime signingTime = new SigningTime();
1224 attributes[1] = new Attribute(signingTime);
1225
1226 // set the attributes
1227 signer_info.setSignedAttributes(attributes);
1228 } catch (Exception ex) {
1229 throw new CMSException("Error adding attributes: " + ex.toString());
1230 }
1231
1232 // finish the creation of SignerInfo by calling method addSigner
1233 try {
1234 signed_data.addSignerInfo(signer_info);
1235 } catch (NoSuchAlgorithmException ex) {
1236 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
1237 }
1238
1239 ContentInfo ci = new ContentInfo(signed_data);
1240 return ci.getEncoded();
1241 }
1242
1243
1244 /**
1245 * Parses a CMS <code>ContentInfo</code> holding a <code>SignedData</code>
1246 * object and verifies the signature.
1247 *
1248 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code>
1249 * object as DER encoded byte array
1250 * @param message the message which was transmitted out-of-band (explicit signed)
1251 * @param certificates the certificate of the signer (used for alternative signature verification)
1252 *
1253 * @return the inherent message as byte array
1254 *
1255 * @throws CMSException if any signature does not verify
1256 * @throws IOException if an I/O related error occurs
1257 */
1258 public byte[] getSignedData(byte[] signedData, byte[] message, X509Certificate[] certificates)
1259 throws CMSException, IOException {
1260
1261 ByteArrayInputStream is = new ByteArrayInputStream(signedData);
1262 // create the SignedData object
1263 SignedData signed_data = new SignedData(is);
1264
1265 if (signed_data.getMode() == SignedData.EXPLICIT) {
1266 // in explcit mode explictly supply the content data to do the hash calculation
1267 signed_data.setContent(message);
1268 }
1269
1270 System.out.println("SignedData contains the following signer information:");
1271 SignerInfo[] signer_infos = signed_data.getSignerInfos();
1272
1273 int numberOfSignerInfos = signer_infos.length;
1274 if (numberOfSignerInfos == 0) {
1275 String warning = "Warning: Unsigned message (no SignerInfo included)!";
1276 System.err.println(warning);
1277 throw new CMSException(warning);
1278 } else {
1279 for (int i = 0; i < numberOfSignerInfos; i++) {
1280 try {
1281 // verify the signed data using the SignerInfo at index i
1282 X509Certificate signer_cert = signed_data.verify(i);
1283 // if the signature is OK the certificate of the signer is returned
1284 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
1285 // check some attributes
1286 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
1287 if (signingTime != null) {
1288 System.out.println("This message has been signed at " + signingTime.get());
1289 }
1290 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
1291 if (contentType != null) {
1292 System.out.println("The content has CMS content type " + contentType.get().getName());
1293 }
1294 } catch (SignatureException ex) {
1295 // if the signature is not OK a SignatureException is thrown
1296 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
1297 throw new CMSException(ex.toString());
1298 }
1299 }
1300
1301 // now check alternative signature verification
1302 System.out.println("Now check the signature assuming that no certs have been included:");
1303 try {
1304 SignerInfo signer_info = signed_data.verify(certificates[0]);
1305 // if the signature is OK the certificate of the signer is returned
1306 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
1307 } catch (SignatureException ex) {
1308 // if the signature is not OK a SignatureException is thrown
1309 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN());
1310 throw new CMSException(ex.toString());
1311 }
1312 // in practice we also would validate the signer certificate(s)
1313 }
1314 return signed_data.getContent();
1315 }
1316
1317 /**
1318 * Creates a CMS <code>CompressedData</code> object.
1319 * <p>
1320 * @param message the message to be compressed, as byte representation
1321 *
1322 * @return the BER encoding of the <code>CompressedData</code> object just created
1323 *
1324 * @throws CMSException if the <code>CompressedData</code> object cannot
1325 * be created
1326 * @throws IOException if an I/O error occurs
1327 * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1328 */
1329 public byte[] createCompressedDataStream(byte[] message)
1330 throws CMSException, IOException, NoSuchAlgorithmException {
1331
1332 System.out.println("Create a new CompressedData message");
1333
1334 // we are testing the stream interface
1335 ByteArrayInputStream is = new ByteArrayInputStream(message);
1336
1337 // create a new CompressedData object
1338 CompressedDataStream compressedData = new CompressedDataStream(is,
1339 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(),
1340 CompressedDataStream.IMPLICIT);
1341
1342 // for testing return the CompressedData as BER encoded byte array with block size of 4
1343 ByteArrayOutputStream os = new ByteArrayOutputStream();
1344 compressedData.setBlockSize(4);
1345 ContentInfoStream cis = new ContentInfoStream(compressedData);
1346 cis.writeTo(os);
1347 return os.toByteArray();
1348 }
1349
1350 /**
1351 * Parses a CMS <code>CompressedData</code> object.
1352 *
1353 * @param encoding the <code>CompressedData</code> object as BER encoded byte array
1354 *
1355 * @return the decompressed message as byte array
1356 *
1357 * @throws CMSException if the CompressedData cannot be parsed
1358 * @throws IOException if an I/O error occurs
1359 * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1360 */
1361 public byte[] getCompressedDataStream(byte[] encoding)
1362 throws CMSException, IOException, NoSuchAlgorithmException {
1363
1364 System.out.println("Parse CompressedData message.");
1365 // we are testing the stream interface
1366 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
1367 // create the CompressedData object
1368 CompressedDataStream compressedData = new CompressedDataStream(is);
1369 // get an InputStream for reading and decompressing the content
1370 InputStream data = compressedData.getInputStream();
1371 ByteArrayOutputStream os = new ByteArrayOutputStream();
1372 Util.copyStream(data, os, null);
1373
1374 return os.toByteArray();
1375 }
1376
1377
1378 /**
1379 * Creates a CMS <code>CompressedData</code> object.
1380 * <p>
1381 *
1382 * @param message the message to be compressed, as byte representation
1383 *
1384 * @return the DER encoded <code>CompressedData</code>
1385 *
1386 * @throws CMSException if the <code>CompressedData</code> object cannot
1387 * be created
1388 * @throws IOException if an I/O error occurs
1389 * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1390 */
1391 public byte[] createCompressedData(byte[] message)
1392 throws CMSException, IOException, NoSuchAlgorithmException {
1393
1394 System.out.println("Create a new CompressedData message");
1395
1396 // create a new CompressedData object
1397 CompressedData compressedData = new CompressedData(message,
1398 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(),
1399 CompressedData.IMPLICIT);
1400 ContentInfo ci = new ContentInfo(compressedData);
1401 return ci.getEncoded();
1402 }
1403
1404 /**
1405 * Parses a CMS <code>CompressedData</code> object.
1406 *
1407 * @param encoding the DER encoded <code>CompressedData</code> object
1408 *
1409 * @return the decompressed message as byte array
1410 *
1411 * @throws CMSException if the CompressedData cannot be parsed
1412 * @throws IOException if an I/O error occurs
1413 * @throws NoSuchAlgorithmException if the compression algorithm is not supported
1414 */
1415 public byte[] getCompressedData(byte[] encoding)
1416 throws CMSException, IOException, NoSuchAlgorithmException {
1417
1418 System.out.println("Parse CompressedData message.");
1419 ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
1420 // create the CompressedData object
1421 CompressedData compressedData = new CompressedData(encodedStream);
1422 // decompress
1423 return compressedData.getContent();
1424 }
1425
1426 /**
1427 * Starts the test.
1428 */
1429 public void start() {
1430
1431 PrivateKey signerPrivateKey = null;
1432 X509Certificate[] signerCertChain = null;
1433 PrivateKey recipient1PrivateKey = null;
1434 X509Certificate recipient1Cert = null;
1435 PrivateKey recipient2PrivateKey = null;
1436 X509Certificate recipient2Cert = null;
1437
1438 // AES-GCM
1439
1440 // AES128-GCM
1441 AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes128_GCM.clone();
1442 signerPrivateKey = ecdsa256bitSignerPrivateKey_;
1443 signerCertChain = ecdsa256bitSignerCertChain_;
1444 recipient1PrivateKey = ecdh256bitRecipient1PrivateKey_;
1445 recipient1Cert = ecdh256bitRecipient1Cert_;
1446 recipient2PrivateKey = ecdh256bitRecipient2PrivateKey_;
1447 recipient2Cert = ecdh256bitRecipient2Cert_;
1448 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1449 // AES192-GCM
1450 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes192_GCM.clone();
1451 signerPrivateKey = ecdsa384bitSignerPrivateKey_;
1452 signerCertChain = ecdsa384bitSignerCertChain_;
1453 recipient1PrivateKey = ecdh384bitRecipient1PrivateKey_;
1454 recipient1Cert = ecdh384bitRecipient1Cert_;
1455 recipient2PrivateKey = ecdh384bitRecipient2PrivateKey_;
1456 recipient2Cert = ecdh384bitRecipient2Cert_;
1457 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1458 // AES256-GCM
1459 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone();
1460 signerPrivateKey = ecdsa521bitSignerPrivateKey_;
1461 signerCertChain = ecdsa521bitSignerCertChain_;
1462 recipient1PrivateKey = ecdh521bitRecipient1PrivateKey_;
1463 recipient1Cert = ecdh521bitRecipient1Cert_;
1464 recipient2PrivateKey = ecdh521bitRecipient2PrivateKey_;
1465 recipient2Cert = ecdh521bitRecipient2Cert_;
1466 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1467
1468 // AES-CBC-CMAC
1469
1470 // AES-CBC-CMAC-128
1471 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_128.clone();
1472 signerPrivateKey = ecdsa256bitSignerPrivateKey_;
1473 signerCertChain = ecdsa256bitSignerCertChain_;
1474 recipient1PrivateKey = ecdh256bitRecipient1PrivateKey_;
1475 recipient1Cert = ecdh256bitRecipient1Cert_;
1476 recipient2PrivateKey = ecdh256bitRecipient2PrivateKey_;
1477 recipient2Cert = ecdh256bitRecipient2Cert_;
1478 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1479 // AES-CBC-CMAC-192
1480 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_192.clone();
1481 signerPrivateKey = ecdsa384bitSignerPrivateKey_;
1482 signerCertChain = ecdsa384bitSignerCertChain_;
1483 recipient1PrivateKey = ecdh384bitRecipient1PrivateKey_;
1484 recipient1Cert = ecdh384bitRecipient1Cert_;
1485 recipient2PrivateKey = ecdh384bitRecipient2PrivateKey_;
1486 recipient2Cert = ecdh384bitRecipient2Cert_;
1487 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1488 // AES-CBC-CMAC-256
1489 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_256.clone();
1490 signerPrivateKey = ecdsa521bitSignerPrivateKey_;
1491 signerCertChain = ecdsa521bitSignerCertChain_;
1492 recipient1PrivateKey = ecdh521bitRecipient1PrivateKey_;
1493 recipient1Cert = ecdh521bitRecipient1Cert_;
1494 recipient2PrivateKey = ecdh521bitRecipient2PrivateKey_;
1495 recipient2Cert = ecdh521bitRecipient2Cert_;
1496 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1497 }
1498
1499 /**
1500 * Starts the test for the given content-authenticated encryption algorithm.
1501 *
1502 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm
1503 */
1504 public void start(AlgorithmID contentAuthEncAlg,
1505 PrivateKey signerPrivateKey,
1506 X509Certificate[] signerCertChain,
1507 PrivateKey recipient1PrivateKey,
1508 X509Certificate recipient1Cert,
1509 PrivateKey recipient2PrivateKey,
1510 X509Certificate recipient2Cert) {
1511 // the test message
1512 String m = "This is the test message.";
1513 System.out.println("Test message: \""+m+"\"");
1514 System.out.println();
1515 byte[] message = m.getBytes();
1516
1517 try {
1518 byte[] encodedAuthEnvelopedData;
1519 byte[] encodedSignedAuthEnvelopedData;
1520 System.out.println("Stream implementation demos");
1521 System.out.println("===========================");
1522
1523 AlgorithmID hashAlgorithm = AlgorithmID.sha256;
1524 AlgorithmID signatureAlgorithm = AlgorithmID.ecdsa_With_SHA256;
1525
1526
1527 // the stream implementation
1528 System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [create]:\n");
1529 // compress content
1530 byte[] compressedData = createCompressedDataStream(message);
1531 encodedAuthEnvelopedData = createAuthEnvelopedDataStream(compressedData,
1532 (AlgorithmID)contentAuthEncAlg.clone(),
1533 recipient1Cert,
1534 recipient2Cert);
1535 encodedSignedAuthEnvelopedData = createSignedDataStream(encodedAuthEnvelopedData,
1536 SignedDataStream.IMPLICIT,
1537 (AlgorithmID)hashAlgorithm.clone(),
1538 (AlgorithmID)signatureAlgorithm.clone(),
1539 signerPrivateKey,
1540 signerCertChain);
1541
1542 // transmit data
1543 System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [parse]:\n");
1544 // verify signature
1545 encodedAuthEnvelopedData = getSignedDataStream(encodedSignedAuthEnvelopedData, null, signerCertChain);
1546 // parse contents
1547 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1548 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey);
1549 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1550 parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1551 System.out.println("Decrypt for the several recipients using their certificate.");
1552 parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1553
1554 // the non-stream implementation
1555 System.out.println("\nNon-stream implementation demos");
1556 System.out.println("===============================");
1557
1558
1559 System.out.println("\nCMS Signed AuthEnvelopedData demo [create]:\n");
1560 // compress content
1561 compressedData = createCompressedData(message);
1562 encodedAuthEnvelopedData = createAuthEnvelopedData(compressedData,
1563 (AlgorithmID)contentAuthEncAlg.clone(),
1564 recipient1Cert,
1565 recipient2Cert);
1566 encodedSignedAuthEnvelopedData = createSignedData(encodedAuthEnvelopedData,
1567 SignedData.IMPLICIT,
1568 (AlgorithmID)hashAlgorithm.clone(),
1569 (AlgorithmID)signatureAlgorithm.clone(),
1570 signerPrivateKey,
1571 signerCertChain);
1572 // transmit data
1573 System.out.println("\nCMS Signed AuthEnvelopedData demo [parse]:\n");
1574 // verify signature
1575 encodedAuthEnvelopedData = getSignedData(encodedSignedAuthEnvelopedData, null, signerCertChain);
1576 // parse contents
1577 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1578 parseAuthEnvelopedDataWithRecipientInfoIndex(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey);
1579 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1580 parseAuthEnvelopedDataWithRecipientIdentifier(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1581 System.out.println("Decrypt for the several recipients using their certificate.");
1582 parseAuthEnvelopedDataWithRecipientCert(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1583
1584
1585 System.out.println("OutputStream implementation demos");
1586 System.out.println("===========================");
1587
1588
1589 // the stream implementation
1590 System.out.println("\nCMS Signed AuthEnvelopedDataOutputStream demo [create]:\n");
1591 // compression of content is done in method createAuthEnvelopedDataOutputStream
1592 encodedAuthEnvelopedData = createAuthEnvelopedDataOutputStream(message,
1593 (AlgorithmID)contentAuthEncAlg.clone(), recipient1Cert,
1594 recipient2Cert);
1595 encodedSignedAuthEnvelopedData = createSignedDataStream(encodedAuthEnvelopedData,
1596 SignedDataStream.IMPLICIT,
1597 (AlgorithmID)hashAlgorithm.clone(),
1598 (AlgorithmID)signatureAlgorithm.clone(),
1599 signerPrivateKey,
1600 signerCertChain);
1601
1602 // transmit data
1603 System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [parse]:\n");
1604 // verify signature
1605 encodedAuthEnvelopedData = getSignedDataStream(encodedSignedAuthEnvelopedData, null, signerCertChain);
1606 // parse contents
1607 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field.");
1608 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey);
1609 System.out.println("Decrypt for the several recipients using their RecipientIdentifier.");
1610 parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1611 System.out.println("Decrypt for the several recipients using their certificate.");
1612 parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert);
1613
1614
1615 } catch (Exception ex) {
1616 ex.printStackTrace();
1617 throw new RuntimeException(ex.toString());
1618 }
1619 }
1620
1621
1622 /**
1623 * Main method.
1624 *
1625 * @throws IOException
1626 * if an I/O error occurs when reading required keys
1627 * and certificates from files
1628 */
1629 public static void main(String argv[]) throws Exception {
1630
1631 DemoUtil.initDemos();
1632 ECCDemoUtil.installIaikEccProvider();
1633
1634 (new EckaEGAuthEnvelopedDataDemo()).start();
1635 System.out.println("\nReady!");
1636 DemoUtil.waitKey();
1637 }
1638 }