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/ecc/ECDHAuthEnvelopedDataDemo.java 5 12.02.25 17:58 Dbratko $ 029// $Revision: 5 $ 030// 031 032 033package demo.cms.ecc; 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; 042 043import javax.crypto.SecretKey; 044 045import demo.DemoUtil; 046import iaik.asn1.ObjectID; 047import iaik.asn1.structures.AlgorithmID; 048import iaik.asn1.structures.Attribute; 049import iaik.cms.AuthEnvelopedData; 050import iaik.cms.AuthEnvelopedDataOutputStream; 051import iaik.cms.AuthEnvelopedDataStream; 052import iaik.cms.CMSException; 053import iaik.cms.ContentInfo; 054import iaik.cms.ContentInfoOutputStream; 055import iaik.cms.ContentInfoStream; 056import iaik.cms.EncryptedContentInfo; 057import iaik.cms.EncryptedContentInfoStream; 058import iaik.cms.IssuerAndSerialNumber; 059import iaik.cms.KeyIdentifier; 060import iaik.cms.OriginatorInfo; 061import iaik.cms.RecipientInfo; 062import iaik.cms.RecipientKeyIdentifier; 063import iaik.cms.attributes.CMSContentType; 064import iaik.utils.Util; 065import iaik.x509.X509Certificate; 066 067/** 068 * Demonstrates the usage of class {@link iaik.cms.AuthEnvelopedDataStream}, 069 * {@link iaik.cms.AuthEnvelopedData} and {@link iaik.cms.AuthEnvelopedDataOutputStream} 070 * for authenticated encrypting data using the CMS type 071 * AuthEnvelopedData by using Static-Static ECDH according to <a href = 072 * "http://www.ietf.org/rfc/rfc6278.txt" target="_blank">6278</a> as 073 * key agreement method. 074 * <p> 075 * Any keys/certificates required for this demo are read from a keystore 076 * file "cmsecc.keystore" located in your current working directory. If 077 * the keystore file does not exist you can create it by running the 078 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore} 079 * program. 080 * <p> 081 * Additionally to <code>iaik_cms.jar</code> you also must have 082 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href = 083 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank"> 084 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>), 085 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href = 086 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank"> 087 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>) 088 * in your classpath. 089 * 090 * @see iaik.cms.AuthEnvelopedDataStream 091 * @see iaik.cms.AuthEnvelopedData 092 * @see iaik.cms.AuthEnvelopedDataOutputStream 093 * @see iaik.cms.RecipientInfo 094 * @see iaik.cms.KeyAgreeRecipientInfo 095 * @see demo.cms.ecc.keystore.SetupCMSEccKeyStore 096 */ 097public class ECDHAuthEnvelopedDataDemo extends StaticStaticECDHDemo { 098 099 100 101 /** 102 * Setup the demo certificate chains. 103 * 104 * Keys and certificates are retrieved from the demo keyStore file 105 * "cmsecc.keystore" located in your current working directory. If 106 * the keystore file does not exist you can create it by running the 107 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore} 108 * program. 109 * 110 * @throws IOException if keys/certificates cannot be read from the keystore 111 */ 112 public ECDHAuthEnvelopedDataDemo() throws IOException { 113 super(); 114 System.out.println(); 115 System.out.println("**********************************************************************************"); 116 System.out.println("* ECDH AuthEnvelopedData demo *"); 117 System.out.println("* (shows the usage of the CMS AuthEnvelopedData type implementation for ECDH) *"); 118 System.out.println("**********************************************************************************"); 119 System.out.println(); 120 121 122 } 123 124 /** 125 * Creates a CMS <code>AuthEnvelopedDataStream</code> message. 126 * 127 * @param message the message to be authenticated-enveloped, as byte representation 128 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 129 * 130 * @return the encoding of the <code>AuthEnvelopedData</code> object just created 131 * 132 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot 133 * be created 134 * @throws IOException if an I/O error occurs 135 */ 136 public byte[] createAuthEnvelopedDataStream(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException, IOException { 137 138 // we are testing the stream interface 139 ByteArrayInputStream is = new ByteArrayInputStream(message); 140 141 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is, contentAuthEncAlg); 142 if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 143 contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) || 144 contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) { 145 // for aes-ccm we need to know the data input length in advance 146 authEnvelopedData.setInputLength(message.length); 147 } 148 149 // static-static mode: set OriginatorInfo 150 OriginatorInfo originator = new OriginatorInfo(); 151 originator.setCertificates(ecdhOriginatorCerts_); 152 authEnvelopedData.setOriginatorInfo(originator); 153 154 // create some authenticated attributes 155 try { 156 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) }; 157 authEnvelopedData.setAuthenticatedAttributes(attributes); 158 } catch (Exception ex) { 159 throw new CMSException("Error creating attribute: " + ex.toString()); 160 } 161 162 // create the recipient infos 163 RecipientInfo[] recipients = createRecipients(); 164 // specify the recipients of the encrypted message 165 authEnvelopedData.setRecipientInfos(recipients); 166 167 // return the EnvelopedDate as DER encoded byte array with block size 4 168 // (just for testing; in real application we will use a proper blocksize, 169 // e.g. 2048, 4096,..) 170 authEnvelopedData.setBlockSize(4); 171 ByteArrayOutputStream os = new ByteArrayOutputStream(); 172 ContentInfoStream cis = new ContentInfoStream(authEnvelopedData); 173 cis.writeTo(os); 174 return os.toByteArray(); 175 } 176 177 /** 178 * Creates a CMS <code>AuthEnvelopedData</code> message using the 179 * {@link iaik.cms.AuthEnvelopedDataOutputStream AuthEnvelopedDataOutputStream} 180 * class. 181 * 182 * @param message the message to be authenticated-enveloped, as byte representation 183 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 184 * 185 * @return the encoding of the <code>AuthEnvelopedData</code> object just created 186 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot 187 * be created 188 * @throws IOException if an I/O error occurs 189 */ 190 public byte[] createAuthEnvelopedDataOutputStream(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException, IOException { 191 192 // a stream from which to read the data to be encrypted 193 ByteArrayInputStream is = new ByteArrayInputStream(message); 194 195 // the stream to which to write the AuthEnvelopedData 196 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 197 198 199 // wrap AuthEnvelopedData into a ContentInfo 200 ContentInfoOutputStream contentInfoStream = 201 new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream); 202 // create a new AuthEnvelopedData object encrypted with AES 203 204 AuthEnvelopedDataOutputStream authEnvelopedData = 205 new AuthEnvelopedDataOutputStream(contentInfoStream, 206 contentAuthEncAlg); 207 208 if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 209 contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) || 210 contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) { 211 // for aes-ccm we need to know the data input length in advance 212 authEnvelopedData.setInputLength(message.length); 213 } 214 // static-static mode: set OriginatorInfo 215 OriginatorInfo originator = new OriginatorInfo(); 216 originator.setCertificates(ecdhOriginatorCerts_); 217 authEnvelopedData.setOriginatorInfo(originator); 218 219 // create some authenticated attributes 220 try { 221 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) }; 222 authEnvelopedData.setAuthenticatedAttributes(attributes); 223 } catch (Exception ex) { 224 throw new CMSException("Error creating attribute: " + ex.toString()); 225 } 226 227 // create the recipient infos 228 RecipientInfo[] recipients = createRecipients(); 229 /// specify the recipients of the encrypted message 230 authEnvelopedData.setRecipientInfos(recipients); 231 232 int blockSize = 4; // in real world we would use a block size like 2048 233 // write in the data to be encrypted 234 byte[] buffer = new byte[blockSize]; 235 int bytesRead; 236 while ((bytesRead = is.read(buffer)) != -1) { 237 authEnvelopedData.write(buffer, 0, bytesRead); 238 } 239 240 // closing the stream finishes encryption and closes the underlying stream 241 authEnvelopedData.close(); 242 return resultStream.toByteArray(); 243 } 244 245 /** 246 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 247 * the recipient identified by its index into the recipientInfos field. 248 * 249 * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array 250 * @param key the key to decrypt the message 251 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 252 * to which the specified key belongs 253 * 254 * @return the recovered message, as byte array 255 * @throws CMSException if the message cannot be recovered 256 * @throws IOException if a stream read/write error occurs 257 */ 258 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex) 259 throws CMSException, IOException { 260 261 // create the AuthEnvelopedData object from a DER encoded byte array 262 // we are testing the stream interface 263 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 264 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is); 265 266 System.out.println("Information about the encrypted data:"); 267 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo(); 268 System.out.println("Content type: "+eci.getContentType().getName()); 269 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 270 271 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 272 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos(); 273 274 // for demonstration purposes we only look one time for all recipients included: 275 if (recipientInfoIndex == 0) { 276 int k = 0; 277 for (int i=0; i<recipients.length; i++) { 278 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 279 for (int j = 0; j < recipientIDs.length; j++) { 280 System.out.println("Recipient "+(++k)+":"); 281 System.out.println(recipientIDs[j]); 282 } 283 } 284 } 285 // decrypt the message for the first recipient 286 try { 287 authEnvelopedData.setupCipher(key, recipientInfoIndex); 288 InputStream decrypted = authEnvelopedData.getInputStream(); 289 ByteArrayOutputStream os = new ByteArrayOutputStream(); 290 Util.copyStream(decrypted, os, null); 291 292 return os.toByteArray(); 293 294 } catch (InvalidKeyException ex) { 295 throw new CMSException("Private key error: "+ex.getMessage()); 296 } catch (NoSuchAlgorithmException ex) { 297 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage()); 298 } 299 } 300 301 /** 302 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 303 * the recipient identified by recipient identifier. 304 * 305 * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array 306 * @param key the key to decrypt the message 307 * @param recipientID the recipient identifier uniquely identifying the key of the 308 * recipient 309 * 310 * @return the recovered message, as byte array 311 * @throws CMSException if the message cannot be recovered 312 * @throws IOException if a stream read/write error occurs 313 */ 314 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID) 315 throws CMSException, IOException { 316 317 // create the AuthEnvelopedData object from a DER encoded byte array 318 // we are testing the stream interface 319 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 320 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is); 321 322 System.out.println("Information about the encrypted data:"); 323 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo(); 324 System.out.println("Content type: "+eci.getContentType().getName()); 325 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 326 327 // get the right RecipientInfo 328 System.out.println("\nSearch for RecipientInfo:"); 329 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID); 330 if (recipient != null) { 331 System.out.println("RecipientInfo: " + recipient); 332 } else { 333 throw new CMSException("No recipient with ID: " + recipientID); 334 } 335 // decrypt the content encryption key and the content 336 try { 337 System.out.println("Decrypt encrypted content encryption key..."); 338 SecretKey cek = recipient.decryptKey(key, recipientID); 339 System.out.println("Decrypt content with decrypted content encryption key..."); 340 authEnvelopedData.setupCipher(cek); 341 InputStream decrypted = authEnvelopedData.getInputStream(); 342 ByteArrayOutputStream os = new ByteArrayOutputStream(); 343 Util.copyStream(decrypted, os, null); 344 345 return os.toByteArray(); 346 347 } catch (InvalidKeyException ex) { 348 throw new CMSException("Private key error: "+ex.getMessage()); 349 } catch (NoSuchAlgorithmException ex) { 350 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage()); 351 } 352 } 353 354 // non stream 355 356 /** 357 * Creates a CMS <code>AuthEnvelopedData</code> message. 358 * 359 * @param message the message to be authenticated-enveloped, as byte representation 360 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 361 * 362 * @return the encoded <code>AuthEnvelopedData</code>, as byte array 363 * 364 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot 365 * be created 366 */ 367 public byte[] createAuthEnvelopedData(byte[] message, AlgorithmID contentAuthEncAlg) throws CMSException { 368 369 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(message, contentAuthEncAlg); 370 371 if (contentAuthEncAlg.equals(AlgorithmID.aes128_CCM) || 372 contentAuthEncAlg.equals(AlgorithmID.aes192_CCM) || 373 contentAuthEncAlg.equals(AlgorithmID.aes256_CCM)) { 374 // for aes-ccm we need to know the data input length in advance 375 authEnvelopedData.setInputLength(message.length); 376 } 377 378 // static-static mode: set OriginatorInfo 379 OriginatorInfo originator = new OriginatorInfo(); 380 originator.setCertificates(ecdhOriginatorCerts_); 381 authEnvelopedData.setOriginatorInfo(originator); 382 383 // create some authenticated attributes 384 try { 385 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) }; 386 authEnvelopedData.setAuthenticatedAttributes(attributes); 387 } catch (Exception ex) { 388 throw new CMSException("Error creating attribute: " + ex.toString()); 389 } 390 391 // set the RecipientInfos 392 RecipientInfo[] recipients = createRecipients(); 393 authEnvelopedData.setRecipientInfos(recipients); 394 395 // return encoded AuthEnvelopedData 396 // wrap into contentInfo 397 ContentInfo ci = new ContentInfo(authEnvelopedData); 398 return ci.getEncoded(); 399 } 400 401 402 /** 403 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 404 * the recipient identified by its index into the recipientInfos field. 405 * 406 * @param enc the encoded <code>AuthEnvelopedData</code> 407 * @param key the key to decrypt the message 408 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 409 * to which the specified key belongs 410 * 411 * @return the recovered message, as byte array 412 * 413 * @throws CMSException if the message cannot be recovered 414 * @throws IOException if an I/O error occurs 415 */ 416 public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 417 throws CMSException, IOException { 418 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 419 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais); 420 421 System.out.println("Information about the encrypted data:"); 422 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo(); 423 System.out.println("Content type: "+eci.getContentType().getName()); 424 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 425 426 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 427 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos(); 428 429 // for demonstration purposes we only look one time for all recipients included: 430 if (recipientInfoIndex == 0) { 431 int k = 0; 432 for (int i=0; i<recipients.length; i++) { 433 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 434 for (int j = 0; j < recipientIDs.length; j++) { 435 System.out.println("Recipient "+(++k)+":"); 436 System.out.println(recipientIDs[j]); 437 } 438 } 439 } 440 441 // decrypt the message 442 try { 443 authEnvelopedData.setupCipher(key, recipientInfoIndex); 444 return authEnvelopedData.getContent(); 445 446 } catch (InvalidKeyException ex) { 447 throw new CMSException("Private key error: "+ex.getMessage()); 448 } catch (NoSuchAlgorithmException ex) { 449 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage()); 450 } 451 } 452 453 /** 454 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 455 * the recipient identified by recipient identifier. 456 * <p> 457 * This way of decrypting the content may be used for any type of RecipientInfo 458 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 459 * recipient in mind is identified by its recipient identifier. 460 * 461 * @param enc the encoded <code>AuthenticatedData</code> 462 * @param key the key to decrypt the message 463 * @param recipientID the recipient identifier uniquely identifying the key of the 464 * recipient 465 * 466 * @return the recovered message, as byte array 467 * @throws CMSException if the message cannot be recovered 468 * @throws IOException if an I/O error occurs 469 */ 470 public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 471 throws CMSException, IOException { 472 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 473 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais); 474 475 System.out.println("Information about the encrypted data:"); 476 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo(); 477 System.out.println("Content type: "+eci.getContentType().getName()); 478 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 479 480 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 481 482 // get the right RecipientInfo 483 System.out.println("\nSearch for RecipientInfo:"); 484 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID); 485 if (recipient != null) { 486 System.out.println("RecipientInfo: " + recipient); 487 } else { 488 throw new CMSException("No recipient with ID: " + recipientID); 489 } 490 // decrypt the content encryption key and the content 491 try { 492 System.out.println("Decrypt encrypted content encryption key..."); 493 SecretKey cek = recipient.decryptKey(key, recipientID); 494 System.out.println("Decrypt content with decrypted content encryption key..."); 495 authEnvelopedData.setupCipher(cek); 496 return authEnvelopedData.getContent(); 497 498 } catch (InvalidKeyException ex) { 499 throw new CMSException("Private key error: "+ex.getMessage()); 500 } catch (NoSuchAlgorithmException ex) { 501 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage()); 502 } 503 } 504 505 /** 506 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 507 * the recipient identified by its recipient certificate. 508 * 509 * @param enc the encoded <code>AuthEnvelopedData</code> 510 * @param key the key to decrypt the message 511 * @param recipientCert the certificate of the recipient 512 * 513 * @return the recovered message, as byte array 514 * 515 * @throws CMSException if the message cannot be recovered 516 */ 517 public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 518 throws CMSException, IOException { 519 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 520 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais); 521 522 System.out.println("Information about the encrypted data:"); 523 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo(); 524 System.out.println("Content type: "+eci.getContentType().getName()); 525 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 526 527 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 528 529 // decrypt the content encryption key and the content 530 try { 531 System.out.println("Decrypt the content..."); 532 authEnvelopedData.setupCipher(key, recipientCert); 533 return authEnvelopedData.getContent(); 534 535 } catch (InvalidKeyException ex) { 536 throw new CMSException("Private key error: "+ex.getMessage()); 537 } catch (NoSuchAlgorithmException ex) { 538 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage()); 539 } 540 } 541 542 543 /** 544 * Parses an AuthEnvelopedData and decrypts the content for all test recipients 545 * using the index into the recipientInfos field for identifying the recipient. 546 * 547 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData 548 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 549 * 550 * @throws Exception if some error occurs during decoding/decryption 551 */ 552 public void parseAuthEnvelopedDataWithRecipientInfoIndex(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception { 553 byte[] receivedMessage; 554 if (stream) { 555 // ecdhUser1 556 System.out.println("\nDecrypt for ecdhUser1:"); 557 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser1Pk_, 0); 558 System.out.print("\nDecrypted content: "); 559 System.out.println(new String(receivedMessage)); 560 // ecdhUser2 561 System.out.println("\nDecrypt for ecdhUser2:"); 562 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser2Pk_, 0); 563 System.out.print("\nDecrypted content: "); 564 System.out.println(new String(receivedMessage)); 565 } else { 566 // ecdhUser1 567 System.out.println("\nDecrypt for ecdhUser1:"); 568 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser1Pk_, 0); 569 System.out.print("\nDecrypted content: "); 570 System.out.println(new String(receivedMessage)); 571 // ecdhUser2 572 System.out.println("\nDecrypt for ecdhUser2:"); 573 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser2Pk_, 0); 574 System.out.print("\nDecrypted content: "); 575 System.out.println(new String(receivedMessage)); 576 } 577 } 578 579 /** 580 * Parses an AuthEnvelopedData and decrypts the content for all test recipients 581 * using their recipient identifiers for identifying the recipient. 582 * 583 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData 584 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 585 * 586 * @throws Exception if some error occurs during decoding/decryption 587 */ 588 public void parseAuthEnvelopedDataWithRecipientIdentifier(boolean stream, byte[] encodedAuthEnvelopedData) throws Exception { 589 byte[] receivedMessage; 590 if (stream) { 591 // ecdhUser1 592 System.out.println("\nDecrypt for ecdhUser1:"); 593 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_)); 594 System.out.print("\nDecrypted content: "); 595 System.out.println(new String(receivedMessage)); 596 // ecdhUser2 597 System.out.println("\nDecrypt for ecdhUser2:"); 598 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_)); 599 System.out.print("\nDecrypted content: "); 600 System.out.println(new String(receivedMessage)); 601 } else { 602 // ecdhUser1 603 System.out.println("\nDecrypt for ecdhUser1:"); 604 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser1Pk_, new IssuerAndSerialNumber(ecdhUser1_)); 605 System.out.print("\nDecrypted content: "); 606 System.out.println(new String(receivedMessage)); 607 // ecdhUser2 608 System.out.println("\nDecrypt for ecdhUser2:"); 609 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, ecdhUser2Pk_, new RecipientKeyIdentifier(ecdhUser2_)); 610 System.out.print("\nDecrypted content: "); 611 System.out.println(new String(receivedMessage)); 612 } 613 } 614 615 616 617 /** 618 * Starts the demo. 619 */ 620 public void start() { 621 622 // AES-GCM 623 AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone(); 624 start(contentAuthEncAlg); 625 626 // AES-CCM 627 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_CCM.clone(); 628 start(contentAuthEncAlg); 629 630 631 // ChaCha20Poly1305 632 contentAuthEncAlg = (AlgorithmID)AlgorithmID.chacha20Poly1305.clone(); 633 start(contentAuthEncAlg); 634 } 635 636 /** 637 * Starts the demo for the given content-authenticated encryption algorithm. 638 * 639 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 640 */ 641 public void start(AlgorithmID contentAuthEncAlg) { 642 // the test message 643 String m = "This is the test message."; 644 System.out.println("Test message: \""+m+"\""); 645 System.out.println(); 646 byte[] message = m.getBytes(); 647 648 try { 649 byte[] encoding; 650 System.out.println("Stream implementation demos"); 651 System.out.println("==========================="); 652 653 654 // the stream implementation 655 // 656 // test CMS AuthEnvelopedDataStream 657 // 658 System.out.println("\nCMS AuthEnvelopedDataStream demo [create]:\n"); 659 encoding = createAuthEnvelopedDataStream(message, (AlgorithmID)contentAuthEncAlg.clone()); 660 // transmit data 661 System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n"); 662 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 663 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encoding); 664 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 665 parseAuthEnvelopedDataWithRecipientIdentifier(true, encoding); 666 667 668 System.out.println("\nOutputStream implementation demos"); 669 System.out.println("================================="); 670 671 672 // the output stream implementation 673 // 674 // test CMS AuthEnvelopedDataOutputStream 675 // 676 System.out.println("\nCMS AuthEnvelopedDataOutputStream demo [create]:\n"); 677 encoding = createAuthEnvelopedDataOutputStream(message, (AlgorithmID)contentAuthEncAlg.clone()); 678 // transmit data 679 System.out.println("\nCMS AuthEnvelopedDataStream demo [parse]:\n"); 680 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 681 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encoding); 682 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 683 parseAuthEnvelopedDataWithRecipientIdentifier(true, encoding); 684 685 // the non-stream implementation 686 System.out.println("\nNon-stream implementation demos"); 687 System.out.println("==============================="); 688 689 690 // 691 // test CMS AuthEnvelopedData 692 // 693 System.out.println("\nCMS AuthEnvelopedData demo [create]:\n"); 694 encoding = createAuthEnvelopedData(message, (AlgorithmID)contentAuthEncAlg.clone()); 695 // transmit data 696 System.out.println("\nCMS AuthEnvelopedData demo [parse]:\n"); 697 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 698 parseAuthEnvelopedDataWithRecipientInfoIndex(false, encoding); 699 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 700 parseAuthEnvelopedDataWithRecipientIdentifier(false, encoding); 701 702 703 704 } catch (Exception ex) { 705 ex.printStackTrace(); 706 throw new RuntimeException(ex.toString()); 707 } 708 } 709 710 /** 711 * Main method. 712 * 713 * @throws IOException 714 * if an I/O error occurs when reading required keys 715 * and certificates from the keystore file 716 */ 717 public static void main(String argv[]) throws Exception { 718 719 DemoUtil.initDemos(); 720 ECCDemoUtil.installIaikEccProvider(); 721 (new ECDHAuthEnvelopedDataDemo()).start(); 722 System.out.println("\nReady!"); 723 System.in.read(); 724 } 725}