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