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