001// Copyright (C) 2002 IAIK 002// https://sic.tech/ 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// This source is provided for inspection purposes and recompilation only, 011// unless specified differently in a contract with IAIK. This source has to 012// be kept in strict confidence and must not be disclosed to any third party 013// under any circumstances. Redistribution in source and binary forms, with 014// or without modification, are <not> permitted in any case! 015// 016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 019// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 026// SUCH DAMAGE. 027// 028// $Header: /IAIK-CMS/current/src/demo/cms/envelopedData/EnvelopedDataDemoAEAD.java 5 12.02.25 17:58 Dbratko $ 029// $Revision: 5 $ 030// 031 032 033package demo.cms.envelopedData; 034 035import java.io.ByteArrayInputStream; 036import java.io.ByteArrayOutputStream; 037import java.io.IOException; 038import java.io.InputStream; 039import java.security.InvalidKeyException; 040import java.security.Key; 041import java.security.NoSuchAlgorithmException; 042import java.security.PrivateKey; 043import java.security.SecureRandom; 044 045import javax.crypto.KeyGenerator; 046import javax.crypto.SecretKey; 047 048import demo.DemoUtil; 049import demo.keystore.CMSKeyStore; 050import iaik.asn1.ObjectID; 051import iaik.asn1.structures.AlgorithmID; 052import iaik.cms.CMSException; 053import iaik.cms.CertificateIdentifier; 054import iaik.cms.ContentInfo; 055import iaik.cms.ContentInfoOutputStream; 056import iaik.cms.ContentInfoStream; 057import iaik.cms.EncryptedContentInfo; 058import iaik.cms.EncryptedContentInfoStream; 059import iaik.cms.EnvelopedData; 060import iaik.cms.EnvelopedDataOutputStream; 061import iaik.cms.EnvelopedDataStream; 062import iaik.cms.IssuerAndSerialNumber; 063import iaik.cms.KEKIdentifier; 064import iaik.cms.KEKRecipientInfo; 065import iaik.cms.KeyAgreeRecipientInfo; 066import iaik.cms.KeyIdentifier; 067import iaik.cms.KeyTransRecipientInfo; 068import iaik.cms.RecipientInfo; 069import iaik.cms.RecipientKeyIdentifier; 070import iaik.cms.SubjectKeyID; 071import iaik.security.random.SecRandom; 072import iaik.utils.CryptoUtils; 073import iaik.utils.Util; 074import iaik.x509.X509Certificate; 075 076 077/** 078 * Demonstrates the usage of class {@link iaik.cms.EnvelopedDataStream}, 079 * {@link iaik.cms.EnvelopedData} and {@link iaik.cms.EnvelopedDataOutputStream} for 080 * AEAD encrypting data using the CMS type EnvelopedData according to 081 * <a href = "http://www.ietf.org/rfc/rfc5652.txt" target="_blank">RFC 5652</a>. 082 * <p> 083 * Usually CMS uses AEAD cipher modes like GCM with the <code>AuthEnvelopedData</code> type. However, 084 * technically -- when appending the mac value to the cipher text -- it is also possible to use 085 * AEAD ciphers with the <code>EnvelopedData</code> type as shown in this demo. 086 * <br> 087 * Since AES-CCM and AES-GCM are not implemented by IAIK-JCE versions prior 3.17, this demo 088 * at least may require IAIK-JCE 3.17 as cryptographic service provider. 089 * ChaCha20-Poly1305 for CMS requires IAIK-JCE version 5.62 or later. 090 * <p> 091 * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore") 092 * which has to be located in your current working directory and may be 093 * created by running the {@link demo.keystore.SetupCMSKeyStore 094 * SetupCMSKeyStore} program. 095 * <p> 096 * This demo requires Java 7 or later. 097 * 098 * @see iaik.cms.EnvelopedDataStream 099 * @see iaik.cms.EnvelopedData 100 * @see iaik.cms.RecipientInfo 101 * @see iaik.cms.KeyTransRecipientInfo 102 * @see iaik.cms.KeyAgreeRecipientInfo 103 * @see iaik.cms.KEKRecipientInfo 104 */ 105public class EnvelopedDataDemoAEAD { 106 107 // certificate of rsaUser 1 108 X509Certificate rsaUser1; 109 // private key of rsaUser 1 110 PrivateKey rsaUser1_pk; 111 // certificate of rsaUser 2 112 X509Certificate rsaUser2; 113 // private key of rsaUser 2 114 PrivateKey rsaUser2_pk; 115 116 // certificate of esdhUser 1 117 X509Certificate esdhUser1; 118 // private key of esdhUser 1 119 PrivateKey esdhUser1_pk; 120 // certificate of esdhUser 2 121 X509Certificate esdhUser2; 122 // private key of esdhUser 2 123 PrivateKey esdhUser2_pk; 124 125 // key encryption key for KEKRecipientInfo 126 SecretKey kek; 127 byte[] kekID; 128 129 // secure random number generator 130 SecureRandom random; 131 132 // the test message 133 byte[] test_message; 134 135 /** 136 * Setup the demo certificate chains. 137 * 138 * Keys and certificates are retrieved from the demo KeyStore ("cms.keystore") 139 * file which has to be located in your current working directory and may be 140 * created by running {@link demo.keystore.SetupCMSKeyStore 141 * SetupCMSKeyStore}. 142 * 143 * @throws IOException if an file read error occurs 144 */ 145 public EnvelopedDataDemoAEAD() throws IOException { 146 147 System.out.println(); 148 System.out.println("*************************************************************************************"); 149 System.out.println("* EnvelopedDataDemoAEAD *"); 150 System.out.println("* (shows the usage of the CMS EnvelopedData type implementation with AEAD ciphers) *"); 151 System.out.println("*************************************************************************************"); 152 System.out.println(); 153 154 // add all certificates to the list 155 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 156 rsaUser1 = certs[0]; 157 rsaUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 158 rsaUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 159 rsaUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 160 161 esdhUser1 = CMSKeyStore.getCertificateChain(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 162 esdhUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_1); 163 esdhUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 164 esdhUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.ESDH, CMSKeyStore.SZ_2048_CRYPT_2); 165 random = SecRandom.getDefault(); 166 // create a secret key encryption key for a KEKRecipientInfo 167 KeyGenerator kg; 168 try { 169 kg = KeyGenerator.getInstance("AES"); 170 } catch (NoSuchAlgorithmException ex) { 171 throw new IOException("KeyGenerator for AES not avcailable!"); 172 } 173 kg.init(random); 174 kek = kg.generateKey(); 175 kekID = new byte[] { 00, 00, 00, 01 }; 176 177 test_message = "This is the test message.".getBytes(); 178 } 179 180 181 /** 182 * Creates a CMS <code>EnvelopedDataStream</code> message. 183 * 184 * @param message the message to be authenticated-enveloped, as byte representation 185 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 186 * 187 * @return the BER encoding of the <code>EnvelopedData</code> object just created 188 * 189 * @throws CMSException if the <code>EnvelopedData</code> object cannot 190 * be created 191 * @throws IOException if an I/O error occurs 192 */ 193 public byte[] createEnvelopedDataStream(byte[] message, 194 AlgorithmID contentAuthEncAlg) 195 throws CMSException, IOException { 196 197 // we are testing the stream interface 198 ByteArrayInputStream is = new ByteArrayInputStream(message); 199 // create a new EnvelopedData object 200 EnvelopedDataStream enveleopedData; 201 try { 202 enveleopedData = new EnvelopedDataStream(is, contentAuthEncAlg); 203 } catch (NoSuchAlgorithmException e) { 204 throw new CMSException(e.getMessage()); 205 } 206 207 // create the recipient infos 208 RecipientInfo[] recipients = createRecipients(); 209 // specify the recipients of the encrypted message 210 enveleopedData.setRecipientInfos(recipients); 211 212 // wrap into ContentInfo 213 ContentInfoStream contentInfo = new ContentInfoStream(enveleopedData); 214 ByteArrayOutputStream os = new ByteArrayOutputStream(); 215 contentInfo.writeTo(os); 216 return os.toByteArray(); 217 } 218 219 /** 220 * Uses the <code>EnvelopedDataOutputStream</code> implementation to create an 221 * EnvelopedData message. 222 * 223 * @param message the message to be authenticated-enveloped, as byte representation 224 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 225 * 226 * @return the BER encoding of the <code>EnvelopedData</code> object just created 227 * 228 * @throws CMSException if the <code>EnvelopedData</code> object cannot 229 * be created 230 * @throws IOException if an I/O error occurs 231 */ 232 public byte[] createEnvelopedDataOutputStream(byte[] message, 233 AlgorithmID contentAuthEncAlg) 234 throws CMSException, IOException { 235 236 // a stream from which to read the data to be encrypted 237 ByteArrayInputStream is = new ByteArrayInputStream(message); 238 239 // the stream to which to write the EnvelopedData 240 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 241 EnvelopedDataOutputStream envelopedData; 242 243 // wrap EnvelopedData into a ContentInfo 244 ContentInfoOutputStream contentInfoStream = 245 new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream); 246 // create a new EnvelopedData object encrypted with AES 247 try { 248 envelopedData = new EnvelopedDataOutputStream(contentInfoStream, 249 contentAuthEncAlg); 250 } catch (NoSuchAlgorithmException ex) { 251 throw new CMSException("No implementation for AES."); 252 } 253 254 // create the recipient infos 255 RecipientInfo[] recipients = createRecipients(); 256 // specify the recipients of the encrypted message 257 envelopedData.setRecipientInfos(recipients); 258 259 int blockSize = 16; // in real world we would use a block size like 2048 260 // write in the data to be encrypted 261 byte[] buffer = new byte[blockSize]; 262 int bytesRead; 263 while ((bytesRead = is.read(buffer)) != -1) { 264 envelopedData.write(buffer, 0, bytesRead); 265 } 266 267 // closing the stream finishes encryption and closes the underlying stream 268 envelopedData.close(); 269 return resultStream.toByteArray(); 270 } 271 272 273 /** 274 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 275 * the recipient identified by its index into the recipientInfos field and verifies 276 * the message authentication code. 277 * <p> 278 * This way of decrypting the content may be used for any type of RecipientInfo 279 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to 280 * know at what index of the recipientInfo field the RecipientInfo for the 281 * particular recipient in mind can be found. If the recipient in mind uses 282 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may 283 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption 284 * keys for more than only one recipient; since the recipientInfoIndex only 285 * specifies the RecipientInfo but not the encrypted content encryption key 286 * -- if there are more than only one -- repeated decryption runs may be 287 * required as long as the decryption process completes successfully. 288 * 289 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 290 * @param key the key to decrypt the message 291 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 292 * to which the specified key belongs 293 * 294 * @return the recovered message, as byte array 295 * @throws CMSException if the message cannot be recovered or MAC verification fails 296 * @throws IOException if a stream read/write error occurs 297 */ 298 public byte[] getEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex) 299 throws CMSException, IOException { 300 301 // create the EnvelopedData object from a BER encoded byte array 302 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 303 EnvelopedDataStream enveleopedData = new EnvelopedDataStream(is); 304 305 System.out.println("Information about the authenticated encrypted data:"); 306 EncryptedContentInfoStream eci = enveleopedData.getEncryptedContentInfo(); 307 System.out.println("Content type: "+eci.getContentType().getName()); 308 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 309 310 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 311 RecipientInfo[] recipients = enveleopedData.getRecipientInfos(); 312 313 // for demonstration purposes we only look one time for all recipients included: 314 if (recipientInfoIndex == 0) { 315 int k = 0; 316 for (int i=0; i<recipients.length; i++) { 317 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 318 for (int j = 0; j < recipientIDs.length; j++) { 319 System.out.println("Recipient "+(++k)+":"); 320 System.out.println(recipientIDs[j]); 321 } 322 } 323 } 324 // decrypt the message for the first recipient and verify mac 325 try { 326 enveleopedData.setupCipher(key, recipientInfoIndex); 327 InputStream decrypted = enveleopedData.getInputStream(); 328 ByteArrayOutputStream os = new ByteArrayOutputStream(); 329 Util.copyStream(decrypted, os, null); 330 byte[] content = os.toByteArray(); 331 332 checkDecryptedMessage(content, test_message); 333 return content; 334 } catch (InvalidKeyException ex) { 335 throw new CMSException("Private key error: "+ex.toString()); 336 } catch (NoSuchAlgorithmException ex) { 337 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 338 } 339 } 340 341 /** 342 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 343 * the recipient identified by recipient identifier and verifies the message 344 * authentication code. 345 * <p> 346 * This way of decrypting the content may be used for any type of RecipientInfo 347 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 348 * recipient in mind is identified by its recipient identifier. 349 * 350 * @param encoding the <code>EnvelopedData</code> object as BER encoded byte array 351 * @param key the key to decrypt the message 352 * @param recipientID the recipient identifier uniquely identifying the key of the 353 * recipient 354 * 355 * @return the recovered message, as byte array 356 * @throws CMSException if the message cannot be recovered 357 * @throws IOException if a stream read/write error occurs 358 */ 359 public byte[] getEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID) 360 throws CMSException, IOException { 361 362 // create the EnvelopedData object from a DER encoded byte array 363 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 364 EnvelopedDataStream enveleopedData = new EnvelopedDataStream(is); 365 366 System.out.println("Information about the encrypted data:"); 367 EncryptedContentInfoStream eci = enveleopedData.getEncryptedContentInfo(); 368 System.out.println("Content type: "+eci.getContentType().getName()); 369 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 370 371 // get the right RecipientInfo 372 System.out.println("\nSearch for RecipientInfo:"); 373 RecipientInfo recipient = enveleopedData.getRecipientInfo(recipientID); 374 if (recipient != null) { 375 System.out.println("RecipientInfo: " + recipient); 376 } else { 377 throw new CMSException("No recipient with ID: " + recipientID); 378 } 379 // decrypt the content encryption key and the content; verify mac 380 try { 381 System.out.println("Decrypt encrypted content encryption key..."); 382 SecretKey cek = recipient.decryptKey(key, recipientID); 383 System.out.println("Decrypt content with decrypted content encryption key..."); 384 enveleopedData.setupCipher(cek); 385 InputStream decrypted = enveleopedData.getInputStream(); 386 ByteArrayOutputStream os = new ByteArrayOutputStream(); 387 Util.copyStream(decrypted, os, null); 388 byte[] content = os.toByteArray(); 389 checkDecryptedMessage(content, test_message); 390 return content; 391 } catch (InvalidKeyException ex) { 392 throw new CMSException("Private key error: "+ex.toString()); 393 } catch (NoSuchAlgorithmException ex) { 394 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 395 } 396 } 397 398 /** 399 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 400 * the recipient identified by its recipient certificate or kekID and verifies the message 401 * authentication code. 402 * <p> 403 * Since recipient certificates only may be used for for RecipientInfos of type 404 * KeyTransRecipientInfo or KeyAgreeRecipientInfo, a key id has to be supplied 405 * for decrypting the content for a recipient using a KEKRecipientInfo. 406 * 407 * @param encoding the <code>EnvelopedData</code> object as BER encoded byte array 408 * @param key the key to decrypt the message 409 * @param recipientCert the certificate of the recipient having a RecipientInfo of 410 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo 411 * @param kekID the kekID identifying the recipient key when using a RecipientInfo 412 * of type KEKRecipientInfo 413 * 414 * @return the recovered message, as byte array 415 * @throws CMSException if the message cannot be recovered 416 * @throws IOException if a stream read/write error occurs 417 */ 418 public byte[] getEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert, byte[] kekID) 419 throws CMSException, IOException { 420 421 // create the EnvelopedData object from a BER encoded byte array 422 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 423 EnvelopedDataStream enveleopedData = new EnvelopedDataStream(is); 424 425 System.out.println("Information about the encrypted data:"); 426 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveleopedData.getEncryptedContentInfo(); 427 System.out.println("Content type: "+eci.getContentType().getName()); 428 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 429 430 // decrypt the content encryption key and the content; verify mac 431 try { 432 System.out.println("Decrypt the content..."); 433 if (recipientCert != null) { 434 enveleopedData.setupCipher(key, recipientCert); 435 } else { 436 // KEKRecipientInfo 437 enveleopedData.setupCipher(key, new KEKIdentifier(kekID)); 438 } 439 InputStream decrypted = enveleopedData.getInputStream(); 440 ByteArrayOutputStream os = new ByteArrayOutputStream(); 441 Util.copyStream(decrypted, os, null); 442 byte[] content = os.toByteArray(); 443 checkDecryptedMessage(content, test_message); 444 return content; 445 } catch (InvalidKeyException ex) { 446 throw new CMSException("Private key error: "+ex.toString()); 447 } catch (NoSuchAlgorithmException ex) { 448 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 449 } 450 } 451 452 453 // non stream 454 455 /** 456 * Creates a CMS <code>EnvelopedData</code> message. 457 * 458 * @param message the message to be enveloped, as byte representation 459 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 460 * 461 * 462 * @return the encoded <code>EnvelopedData</code>, as byte array 463 * 464 * @throws CMSException if the <code>EnvelopedData</code> object cannot 465 * be created 466 */ 467 public byte[] createEnvelopedData(byte[] message, AlgorithmID contentAuthEncAlg) 468 throws CMSException { 469 470 EnvelopedData enveleopedData; 471 472 // create a new EnvelopedData object 473 try { 474 enveleopedData = new EnvelopedData(message, contentAuthEncAlg); 475 } catch (NoSuchAlgorithmException e) { 476 throw new CMSException(e.getMessage()); 477 } 478 479 // set the RecipientInfos 480 RecipientInfo[] recipients = createRecipients(); 481 enveleopedData.setRecipientInfos(recipients); 482 483 // wrap into ContentInfo 484 ContentInfo contentInfo = new ContentInfo(enveleopedData); 485 // return encoded EnvelopedData 486 return contentInfo.getEncoded(); 487 } 488 489 490 /** 491 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 492 * the recipient identified by its index into the recipientInfos field and verifies 493 * the message authentication code. 494 * <p> 495 * This way of decrypting the content may be used for any type of RecipientInfo 496 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to 497 * know at what index of the recipientInfo field the RecipientInfo for the 498 * particular recipient in mind can be found. If the recipient in mind uses 499 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may 500 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption 501 * keys for more than only one recipient; since the recipientInfoIndex only 502 * specifies the RecipientInfo but not the encrypted content encryption key 503 * -- if there are more than only one -- repeated decryption runs may be 504 * required as long as the decryption process completes successfully. 505 * 506 * @param enc the encoded <code>EnvelopedData</code> 507 * 508 * @param key the key to decrypt the message 509 * 510 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 511 * to which the specified key belongs 512 * 513 * @return the recovered message, as byte array 514 * 515 * @throws CMSException if the message cannot be recovered 516 * @throws IOException if an I/O error occurs 517 */ 518 public byte[] getEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 519 throws CMSException, IOException { 520 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 521 EnvelopedData enveleopedData = new EnvelopedData(bais); 522 523 System.out.println("Information about the encrypted data:"); 524 EncryptedContentInfo eci = (EncryptedContentInfo)enveleopedData.getEncryptedContentInfo(); 525 System.out.println("Content type: "+eci.getContentType().getName()); 526 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 527 528 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 529 RecipientInfo[] recipients = enveleopedData.getRecipientInfos(); 530 531 // for demonstration purposes we only look one time for all recipients included: 532 if (recipientInfoIndex == 0) { 533 int k = 0; 534 for (int i=0; i<recipients.length; i++) { 535 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 536 for (int j = 0; j < recipientIDs.length; j++) { 537 System.out.println("Recipient "+(++k)+":"); 538 System.out.println(recipientIDs[j]); 539 } 540 } 541 } 542 543 // decrypt the message and verify the mac 544 try { 545 enveleopedData.setupCipher(key, recipientInfoIndex); 546 byte[] content = enveleopedData.getContent(); 547 checkDecryptedMessage(content, test_message); 548 return content; 549 } catch (InvalidKeyException ex) { 550 throw new CMSException("Private key error: "+ex.toString()); 551 } catch (NoSuchAlgorithmException ex) { 552 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 553 } 554 } 555 /** 556 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 557 * the recipient identified by recipient identifier. 558 * <p> 559 * This way of decrypting the content may be used for any type of RecipientInfo 560 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 561 * recipient in mind is identified by its recipient identifier. 562 * 563 * @param enc the BER encoded <code>EnvelopedData</code> ASN.1 object 564 * @param key the key to decrypt the message 565 * @param recipientID the recipient identifier uniquely identifying the key of the 566 * recipient 567 * 568 * @return the recovered message, as byte array 569 * @throws CMSException if the message cannot be recovered 570 * @throws IOException if an I/O error occurs 571 */ 572 public byte[] getEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 573 throws CMSException, IOException { 574 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 575 EnvelopedData enveleopedData = new EnvelopedData(bais); 576 577 System.out.println("Information about the encrypted data:"); 578 EncryptedContentInfo eci = (EncryptedContentInfo)enveleopedData.getEncryptedContentInfo(); 579 System.out.println("Content type: "+eci.getContentType().getName()); 580 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 581 582 // get the right RecipientInfo 583 System.out.println("\nSearch for RecipientInfo:"); 584 RecipientInfo recipient = enveleopedData.getRecipientInfo(recipientID); 585 if (recipient != null) { 586 System.out.println("RecipientInfo: " + recipient); 587 } else { 588 throw new CMSException("No recipient with ID: " + recipientID); 589 } 590 // decrypt the content encryption key and the content 591 try { 592 System.out.println("Decrypt encrypted content encryption key..."); 593 SecretKey cek = recipient.decryptKey(key, recipientID); 594 System.out.println("Decrypt content with decrypted content encryption key..."); 595 // decrypt content and verify mac 596 enveleopedData.setupCipher(cek); 597 byte[] content = enveleopedData.getContent(); 598 checkDecryptedMessage(content, test_message); 599 return content; 600 } catch (InvalidKeyException ex) { 601 throw new CMSException("Private key error: "+ex.toString()); 602 } catch (NoSuchAlgorithmException ex) { 603 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 604 } 605 } 606 607 /** 608 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 609 * the recipient identified by its recipient certificate or keyID. 610 * <p> 611 * Since recipient certificates only may be used for for RecipientInfos of type 612 * KeyTransRecipientInfo or KeyAgreeRecipientInfo, a key id has to be supplied 613 * for decrypting the content for a recipient using a KEKRecipientInfo. 614 * 615 * @param enc the BER encoded <code>EnvelopedData</code> ASN.1 object 616 * @param key the key to decrypt the message 617 * @param recipientCert the certificate of the recipient having a RecipientInfo of 618 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo 619 * @param kekID the kekID identifying the recipient key when using a RecipientInfo 620 * of type KEKRecipientInfo 621 * 622 * @return the recovered message, as byte array 623 * @throws CMSException if the message cannot be recovered 624 */ 625 public byte[] getEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert, byte[] kekID) 626 throws CMSException, IOException { 627 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 628 EnvelopedData enveleopedData = new EnvelopedData(bais); 629 630 System.out.println("Information about the encrypted data:"); 631 EncryptedContentInfo eci = (EncryptedContentInfo)enveleopedData.getEncryptedContentInfo(); 632 System.out.println("Content type: "+eci.getContentType().getName()); 633 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 634 635 // decrypt the content encryption key and the content 636 try { 637 System.out.println("Decrypt the content and verify mac..."); 638 // decrypt content and verify mac 639 if (recipientCert != null) { 640 enveleopedData.setupCipher(key, recipientCert); 641 } else { 642 // KEKRecipientInfo 643 enveleopedData.setupCipher(key, new KEKIdentifier(kekID)); 644 } 645 byte[] content = enveleopedData.getContent(); 646 checkDecryptedMessage(content, test_message); 647 return content; 648 } catch (InvalidKeyException ex) { 649 throw new CMSException("Private key error: "+ex.toString()); 650 } catch (NoSuchAlgorithmException ex) { 651 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 652 } 653 654 } 655 656 /** 657 * Creates the RecipientInfos. 658 * 659 * @return the RecipientInfos created, two KeyTransRecipientInfos, one 660 * KeyAgreeRecipientInfo (for two recipients with same domain 661 * parameters), and one KEKRecipientInfo 662 * 663 * @throws CMSException if an error occurs when creating the recipient infos 664 */ 665 public RecipientInfo[] createRecipients() throws CMSException { 666 667 RecipientInfo[] recipients = new RecipientInfo[4]; 668 try { 669 // rsaUser1 is the first receiver (cert identified by IssuerAndSerialNumber) 670 recipients[0] = new KeyTransRecipientInfo(rsaUser1, 671 (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 672 // rsaUser2 is the second receiver (cert identifief by SubjectKeyIdentifier) 673 recipients[1] = new KeyTransRecipientInfo(rsaUser2, 674 CertificateIdentifier.SUBJECT_KEY_IDENTIFIER, 675 (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 676 677 // next recipients use key agreement 678 // the key encryption (key agreement) algorithm to use: 679 AlgorithmID keyEA = (AlgorithmID)AlgorithmID.esdhKeyAgreement.clone(); 680 // the key wrap algorithm to use: 681 AlgorithmID keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone(); 682 // the length of the key encryption key to be generated: 683 int kekLength = 256; 684 recipients[2] = new KeyAgreeRecipientInfo(keyEA, keyWrapAlg, kekLength); 685 // esdhUser1 is the third receiver (cert identified by IssuerAndSerialNumber) 686 ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(esdhUser1, CertificateIdentifier.ISSUER_AND_SERIALNUMBER); 687 // esdhUser2 is the fourth receiver (cert identified by RecipientKeyIdentifier) 688 ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(esdhUser2, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER); 689 690 // last receiver uses a symmetric key encryption key 691 AlgorithmID kea = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone(); 692 KEKIdentifier kekIdentifier = new KEKIdentifier(kekID); 693 recipients[3] = new KEKRecipientInfo(kekIdentifier, kea, kek); 694 } catch (Exception ex) { 695 throw new CMSException("Error adding recipients: " + ex.getMessage()); 696 } 697 return recipients; 698 } 699 700 /** 701 * Parses an EnvelopedData and decrypts the content for all test recipients 702 * using the index into the recipientInfos field for identifying the recipient. 703 * 704 * @param stream whether to use EnvelopedDataStream or EnvelopedData 705 * @param encodedEnvelopedData the encoded EnvelopedData object 706 * 707 * @throws Exception if some error occurs during decoding/decryption 708 */ 709 public void parseEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedEnvelopedData) throws Exception { 710 byte[] receivedMessage; 711 if (stream) { 712 // rsaUser1 713 System.out.println("\nDecrypt for rsaUser1:"); 714 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser1_pk, 0); 715 System.out.print("\nDecrypted content: "); 716 System.out.println(new String(receivedMessage)); 717 // rsaUser2 718 System.out.println("\nDecrypt for rsaUser2:"); 719 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser2_pk, 1); 720 System.out.print("\nDecrypted content: "); 721 System.out.println(new String(receivedMessage)); 722 // esdhUser1 723 System.out.println("\nDecrypt for esdhUser1:"); 724 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser1_pk, 2); 725 System.out.print("\nDecrypted content: "); 726 System.out.println(new String(receivedMessage)); 727 // esdhUser2 728 System.out.println("\nDecrypt for esdhUser2:"); 729 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser2_pk, 2); 730 System.out.print("\nDecrypted content: "); 731 System.out.println(new String(receivedMessage)); 732 // kekUser 733 System.out.println("\nDecrypt for kekUser:"); 734 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, kek, 3); 735 System.out.print("\nDecrypted content: "); 736 System.out.println(new String(receivedMessage)); 737 } else { 738 // rsaUser1 739 System.out.println("\nDecrypt for rsaUser1:"); 740 receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser1_pk, 0); 741 System.out.print("\nDecrypted content: "); 742 System.out.println(new String(receivedMessage)); 743 // rsaUser2 744 System.out.println("\nDecrypt for rsaUser2:"); 745 receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser2_pk, 1); 746 System.out.print("\nDecrypted content: "); 747 System.out.println(new String(receivedMessage)); 748 // esdhUser1 749 System.out.println("\nDecrypt for esdhUser1:"); 750 receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser1_pk, 2); 751 System.out.print("\nDecrypted content: "); 752 System.out.println(new String(receivedMessage)); 753 // esdhUser2 754 System.out.println("\nDecrypt for esdhUser2:"); 755 receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser2_pk, 2); 756 System.out.print("\nDecrypted content: "); 757 System.out.println(new String(receivedMessage)); 758 // kekUser 759 System.out.println("\nDecrypt for kekUser:"); 760 receivedMessage = getEnvelopedData(encodedEnvelopedData, kek, 3); 761 System.out.print("\nDecrypted content: "); 762 System.out.println(new String(receivedMessage)); 763 } 764 } 765 766 /** 767 * Parses an EnvelopedData and decrypts the content for all test recipients 768 * using their recipient identifiers for identifying the recipient. 769 * 770 * @param stream whether to use EnvelopedDataStream or EnvelopedData 771 * @param encodedEnvelopedData the encoded EnvelopedData object 772 * 773 * @throws Exception if some error occurs during decoding/decryption 774 */ 775 public void parseEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedEnvelopedData) throws Exception { 776 byte[] receivedMessage; 777 if (stream) { 778 // rsaUser1 779 System.out.println("\nDecrypt for rsaUser1:"); 780 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser1_pk, new IssuerAndSerialNumber(rsaUser1)); 781 System.out.print("\nDecrypted content: "); 782 System.out.println(new String(receivedMessage)); 783 // rsaUser2 784 System.out.println("\nDecrypt for rsaUser2:"); 785 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser2_pk, new SubjectKeyID(rsaUser2)); 786 System.out.print("\nDecrypted content: "); 787 System.out.println(new String(receivedMessage)); 788 // esdhUser1 789 System.out.println("\nDecrypt for esdhUser1:"); 790 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser1_pk, new IssuerAndSerialNumber(esdhUser1)); 791 System.out.print("\nDecrypted content: "); 792 System.out.println(new String(receivedMessage)); 793 // esdhUser2 794 System.out.println("\nDecrypt for esdhUser2:"); 795 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser2_pk, new RecipientKeyIdentifier(esdhUser2)); 796 System.out.print("\nDecrypted content: "); 797 System.out.println(new String(receivedMessage)); 798 // kekUser 799 System.out.println("\nDecrypt for kekUser:"); 800 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, kek, new KEKIdentifier(kekID)); 801 System.out.print("\nDecrypted content: "); 802 System.out.println(new String(receivedMessage)); 803 } else { 804 // rsaUser1 805 System.out.println("\nDecrypt for rsaUser1:"); 806 receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser1_pk, new IssuerAndSerialNumber(rsaUser1)); 807 System.out.print("\nDecrypted content: "); 808 System.out.println(new String(receivedMessage)); 809 // rsaUser2 810 System.out.println("\nDecrypt for rsaUser2:"); 811 receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser2_pk, new SubjectKeyID(rsaUser2)); 812 System.out.print("\nDecrypted content: "); 813 System.out.println(new String(receivedMessage)); 814 // esdhUser1 815 System.out.println("\nDecrypt for esdhUser1:"); 816 receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser1_pk, new IssuerAndSerialNumber(esdhUser1)); 817 System.out.print("\nDecrypted content: "); 818 System.out.println(new String(receivedMessage)); 819 // esdhUser2 820 System.out.println("\nDecrypt for esdhUser2:"); 821 receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser2_pk, new RecipientKeyIdentifier(esdhUser2)); 822 System.out.print("\nDecrypted content: "); 823 System.out.println(new String(receivedMessage)); 824 // kekUser 825 System.out.println("\nDecrypt for kekUser:"); 826 receivedMessage = getEnvelopedData(encodedEnvelopedData, kek, new KEKIdentifier(kekID)); 827 System.out.print("\nDecrypted content: "); 828 System.out.println(new String(receivedMessage)); 829 } 830 } 831 832 /** 833 * Parses an EnvelopedData and decrypts the content for all test recipients 834 * using their recipient certificate (for RecipientInfos of type KeyTransRecipientInfo 835 * or KeyAgreeRecipientInfo) or key id (for RecipientInfos of type KEKRecipientInfo) 836 * for identifying the recipient. 837 * 838 * @param stream whether to use EnvelopedDataStream or EnvelopedData 839 * @param encodedEnvelopedData the encoded EnvelopedData object 840 * 841 * @throws Exception if some error occurs during decoding/decryption 842 */ 843 public void parseEnvelopedDataWithRecipientCertOrKEKId(boolean stream, byte[] encodedEnvelopedData) throws Exception { 844 byte[] receivedMessage; 845 if (stream) { 846 // rsaUser1 847 System.out.println("\nDecrypt for rsaUser1:"); 848 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser1_pk, rsaUser1, null); 849 System.out.print("\nDecrypted content: "); 850 System.out.println(new String(receivedMessage)); 851 // rsaUser2 852 System.out.println("\nDecrypt for rsaUser2:"); 853 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, rsaUser2_pk, rsaUser2, null); 854 System.out.print("\nDecrypted content: "); 855 System.out.println(new String(receivedMessage)); 856 // esdhUser1 857 System.out.println("\nDecrypt for esdhUser1:"); 858 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser1_pk, esdhUser1, null); 859 System.out.print("\nDecrypted content: "); 860 System.out.println(new String(receivedMessage)); 861 // esdhUser2 862 System.out.println("\nDecrypt for esdhUser2:"); 863 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, esdhUser2_pk, esdhUser2, null); 864 System.out.print("\nDecrypted content: "); 865 System.out.println(new String(receivedMessage)); 866 // kekUser 867 System.out.println("\nDecrypt for kekUser:"); 868 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, kek, null, kekID); 869 System.out.print("\nDecrypted content: "); 870 System.out.println(new String(receivedMessage)); 871 } else { 872 // rsaUser1 873 System.out.println("\nDecrypt for rsaUser1:"); 874 receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser1_pk, rsaUser1, null); 875 System.out.print("\nDecrypted content: "); 876 System.out.println(new String(receivedMessage)); 877 // rsaUser2 878 System.out.println("\nDecrypt for rsaUser2:"); 879 receivedMessage = getEnvelopedData(encodedEnvelopedData, rsaUser2_pk, rsaUser2, null); 880 System.out.print("\nDecrypted content: "); 881 System.out.println(new String(receivedMessage)); 882 // esdhUser1 883 System.out.println("\nDecrypt for esdhUser1:"); 884 receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser1_pk, esdhUser1, null); 885 System.out.print("\nDecrypted content: "); 886 System.out.println(new String(receivedMessage)); 887 // esdhUser2 888 System.out.println("\nDecrypt for esdhUser2:"); 889 receivedMessage = getEnvelopedData(encodedEnvelopedData, esdhUser2_pk, esdhUser2, null); 890 System.out.print("\nDecrypted content: "); 891 System.out.println(new String(receivedMessage)); 892 // kekUser 893 System.out.println("\nDecrypt for kekUser:"); 894 receivedMessage = getEnvelopedData(encodedEnvelopedData, kek, null, kekID); 895 System.out.print("\nDecrypted content: "); 896 System.out.println(new String(receivedMessage)); 897 } 898 } 899 900 /** 901 * Checks the decrypted message. 902 * 903 * @param decryptedMessage the decrypted message 904 * @param testMessage the original message 905 * 906 * @throws CMSException if decrypted and original message do not match 907 */ 908 private void checkDecryptedMessage(byte[] decryptedMessage, byte[] testMessage) throws CMSException { 909 if (CryptoUtils.equalsBlock(decryptedMessage, testMessage) == false) { 910 throw new CMSException("Decrypted content not equal to original one!"); 911 } 912 } 913 914 /** 915 * Starts the test. 916 */ 917 public void start() { 918 919 // AES-GCM 920 AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone(); 921 start(contentAuthEncAlg); 922 923 // AES-CCM 924 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_CCM.clone(); 925 start(contentAuthEncAlg); 926 927 // ChaCha20Poly1305 928 contentAuthEncAlg = (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(); 929 start(contentAuthEncAlg); 930 } 931 932 /** 933 * Starts the test for the given content-authenticated encryption algorithm. 934 * 935 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 936 */ 937 public void start(AlgorithmID contentAuthEncAlg) { 938 // the test message 939 byte[] message = test_message; 940 941 try { 942 byte[] encodedEnvelopedData; 943 944 // the stream implementation 945 System.out.println("Stream implementation demos"); 946 System.out.println("==========================="); 947 948 // 949 // test CMS EnvelopedDataStream 950 // 951 System.out.println("\nCMS EnvelopedDataStream demo [create]:\n"); 952 encodedEnvelopedData = createEnvelopedDataStream(message, (AlgorithmID)contentAuthEncAlg.clone()); 953 // transmit data 954 System.out.println("\nCMS EnvelopedDataStream demo [parse]:\n"); 955 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 956 parseEnvelopedDataWithRecipientInfoIndex(true, encodedEnvelopedData); 957 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 958 parseEnvelopedDataWithRecipientIdentifier(true, encodedEnvelopedData); 959 System.out.println("Decrypt for the several recipients using their certificate or symmetric kek."); 960 parseEnvelopedDataWithRecipientCertOrKEKId(true, encodedEnvelopedData); 961 962 // the output stream implementation 963 System.out.println("Output Stream implementation demos"); 964 System.out.println("=================================="); 965 966 // 967 // test CMS EnvelopedDataOutputStream 968 // 969 System.out.println("\nCMS EnvelopedDataOutputStream demo [create]:\n"); 970 encodedEnvelopedData = createEnvelopedDataOutputStream(message, (AlgorithmID)contentAuthEncAlg.clone()); 971 // transmit data 972 System.out.println("\nCMS EnvelopedDataOutputStream demo [parse]:\n"); 973 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 974 parseEnvelopedDataWithRecipientInfoIndex(true, encodedEnvelopedData); 975 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 976 parseEnvelopedDataWithRecipientIdentifier(true, encodedEnvelopedData); 977 System.out.println("Decrypt for the several recipients using their certificate or symmetric kek."); 978 parseEnvelopedDataWithRecipientCertOrKEKId(true, encodedEnvelopedData); 979 980 981 // the non-stream implementation 982 System.out.println("\nNon-stream implementation demos"); 983 System.out.println("==============================="); 984 985 986 // 987 // test CMS EnvelopedData 988 // 989 System.out.println("\nCMS EnvelopedData demo [create]:\n"); 990 encodedEnvelopedData = createEnvelopedData(message, (AlgorithmID)contentAuthEncAlg.clone()); 991 // transmit data 992 System.out.println("\nCMS EnvelopedData demo [parse]:\n"); 993 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 994 parseEnvelopedDataWithRecipientInfoIndex(false, encodedEnvelopedData); 995 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 996 parseEnvelopedDataWithRecipientIdentifier(false, encodedEnvelopedData); 997 System.out.println("Decrypt for the several recipients using their certificate or symmetric kek."); 998 parseEnvelopedDataWithRecipientCertOrKEKId(false, encodedEnvelopedData); 999 1000 1001 } catch (Exception ex) { 1002 ex.printStackTrace(); 1003 throw new RuntimeException(ex.toString()); 1004 } 1005 } 1006 1007 /** 1008 * Main method. 1009 * 1010 * @throws IOException 1011 * if an I/O error occurs when reading required keys 1012 * and certificates from files 1013 */ 1014 public static void main(String argv[]) throws Exception { 1015 1016 DemoUtil.initDemos(); 1017 1018 (new EnvelopedDataDemoAEAD()).start(); 1019 System.out.println("\nReady!"); 1020 DemoUtil.waitKey(); 1021 } 1022}