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