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