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/authenticatedData/AuthenticatedDataOutputStreamDemo.java 19 12.02.25 17:58 Dbratko $ 059 // $Revision: 19 $demo.cms.authenticatedData 060 package demo.cms.authenticatedData; 061 062 import iaik.asn1.ObjectID; 063 import iaik.asn1.structures.AlgorithmID; 064 import iaik.asn1.structures.Attribute; 065 import iaik.cms.AuthenticatedDataOutputStream; 066 import iaik.cms.AuthenticatedDataStream; 067 import iaik.cms.CMSAlgorithmID; 068 import iaik.cms.CMSException; 069 import iaik.cms.CertificateIdentifier; 070 import iaik.cms.ContentInfoOutputStream; 071 import iaik.cms.IssuerAndSerialNumber; 072 import iaik.cms.KEKIdentifier; 073 import iaik.cms.KEKRecipientInfo; 074 import iaik.cms.KeyAgreeRecipientInfo; 075 import iaik.cms.KeyIdentifier; 076 import iaik.cms.KeyTransRecipientInfo; 077 import iaik.cms.OriginatorInfo; 078 import iaik.cms.RecipientInfo; 079 import iaik.cms.RecipientKeyIdentifier; 080 import iaik.cms.SecurityProvider; 081 import iaik.cms.SubjectKeyID; 082 import iaik.cms.attributes.CMSContentType; 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.KeyGenerator; 098 import javax.crypto.SecretKey; 099 100 import demo.DemoUtil; 101 import demo.keystore.CMSKeyStore; 102 103 /** 104 * Demonstrates the usage of class {@link iaik.cms.AuthenticatedDataOutputStream} and 105 * {@link iaik.cms.AuthenticatedDataOutputStream} for recipient-specific protecting the 106 * integrity of message using the CMS type AuthenticatedData. 107 * <p> 108 * <b>Attention:</b> This demo uses Static-Static Diffie-Hellman as key management 109 * technique for providing origin authentication. 110 * <p> 111 * This demo requires that you have <code>iaik_esdh.jar</code> 112 * (or <code>iaik_jce_full.jar</code>) in your classpath. 113 * You can download it from <a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank"> 114 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>. 115 * 116 * 117 * @see iaik.cms.AuthenticatedDataStream 118 * @see iaik.cms.AuthenticatedDataOutputStream 119 * 120 */ 121 public class AuthenticatedDataOutputStreamDemo { 122 123 // certificate of rsaUser 1 124 X509Certificate rsaUser1; 125 // private key of rsaUser 1 126 PrivateKey rsaUser1_pk; 127 // certificate of rsaUser 2 128 X509Certificate rsaUser2; 129 // private key of rsaUser 2 130 PrivateKey rsaUser2_pk; 131 132 // certificate of (originator) SSDH User 1 (static-static Diffie-Hellman) 133 X509Certificate ssdhUser1; 134 X509Certificate[] originatorCerts; 135 // private key of SSDH User 1 136 PrivateKey ssdhUser1_pk; 137 // certificate of SSDH User 2 (static-static Diffie-Hellman) 138 X509Certificate ssdhUser2; 139 // private key of SSDH User 2 140 PrivateKey ssdhUser2_pk; 141 142 // key encryption key for KEKRecipientInfo 143 SecretKey kek; 144 byte[] kekID; 145 146 147 // secure random number generator 148 SecureRandom random; 149 150 /** 151 * Setup the demo certificate chains. 152 * 153 * Keys and certificate are retrieved from the demo KeyStore which 154 * has to be located in your current working directory and may be 155 * created by running {@link demo.keystore.SetupCMSKeyStore 156 * SetupCMSKeyStore}. 157 * 158 * @throws IOException if an file read error occurs 159 */ 160 public AuthenticatedDataOutputStreamDemo() throws IOException { 161 162 System.out.println(); 163 System.out.println("**********************************************************************************"); 164 System.out.println("* AuthenticatedDataOutputStream demo *"); 165 System.out.println("* (shows the usage of the CMS AuthenticatedDataOutputStream implementation) *"); 166 System.out.println("**********************************************************************************"); 167 System.out.println(); 168 169 // add all certificates to the list 170 rsaUser1 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 171 rsaUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 172 rsaUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 173 rsaUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 174 175 originatorCerts = CMSKeyStore.getCertificateChain(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_1); 176 ssdhUser1 = originatorCerts[0]; 177 ssdhUser1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_1); 178 ssdhUser2 = CMSKeyStore.getCertificateChain(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 179 ssdhUser2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.SSDH, CMSKeyStore.SZ_2048_CRYPT_2); 180 181 random = SecRandom.getDefault(); 182 183 // create a secret key encryption key for a KEKRecipientInfo 184 KeyGenerator kg; 185 try { 186 kg = SecurityProvider.getSecurityProvider().getKeyGenerator("3DES", 192); 187 } catch (NoSuchAlgorithmException ex) { 188 throw new IOException("KeyGenerator for 3DES not avcailable!"); 189 } 190 kek = kg.generateKey(); 191 kekID = new byte[] { 00, 00, 00, 01 }; 192 193 } 194 195 196 /** 197 * Creates a CMS <code>AuthenticatedDataOutputStream</code> for the given message message. 198 * 199 * @param message the message to be authenticated, as byte representation 200 * @param macAlgorithm the mac algorithm to be used 201 * @param macKeyLength the length of the temporary MAC key to be generated 202 * @param digestAlgorithm the digest algorithm to be used to calculate a digest 203 * from the content if authenticated attributes should 204 * be included 205 * @param mode whether to include the content into the AuthenticatedData ({@link 206 * AuthenticatedDataStream#IMPLICIT implicit}) or to not include it 207 * ({@link AuthenticatedDataStream#EXPLICIT explicit}) 208 * 209 * @return the BER encoding of the <code>AuthenticatedData</code> object just created, 210 * wrapped into a ContentInfo 211 * 212 * @throws CMSException if the <code>AuthenticatedData</code> object cannot 213 * be created 214 * @throws IOException if an I/O error occurs 215 */ 216 public byte[] createAuthenticatedDataStream(byte[] message, 217 AlgorithmID macAlgorithm, 218 int macKeyLength, 219 AlgorithmID digestAlgorithm, 220 int mode) 221 throws CMSException, IOException { 222 223 AlgorithmID macAlg = (AlgorithmID)macAlgorithm.clone(); 224 AlgorithmID digestAlg = null; 225 if (digestAlgorithm != null) { 226 digestAlg = (AlgorithmID)digestAlgorithm.clone(); 227 } 228 ObjectID contentType = ObjectID.cms_data; 229 230 AuthenticatedDataOutputStream authenticatedData; 231 232 // a stream from which to read the data to be authenticated 233 ByteArrayInputStream is = new ByteArrayInputStream(message); 234 235 // the stream to which to write the AuthenticatedData 236 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 237 238 // wrap AuthenticatedData into a ContentInfo 239 ContentInfoOutputStream contentInfoStream = 240 new ContentInfoOutputStream(ObjectID.cms_authData, resultStream); 241 242 // create AuthenticatedDataOutputStream 243 try { 244 authenticatedData = new AuthenticatedDataOutputStream(contentType, 245 contentInfoStream, 246 macAlg, 247 macKeyLength, 248 null, 249 digestAlg, 250 mode); 251 } catch (NoSuchAlgorithmException ex) { 252 throw new CMSException(ex.toString()); 253 } 254 255 // static-static mode: set OriginatorInfo 256 OriginatorInfo originator = new OriginatorInfo(); 257 originator.setCertificates(originatorCerts); 258 authenticatedData.setOriginatorInfo(originator); 259 // create the recipient infos 260 RecipientInfo[] recipients = createRecipients(); 261 // specify the recipients of the authenticated message 262 authenticatedData.setRecipientInfos(recipients); 263 264 if (digestAlgorithm != null) { 265 // create some authenticated attributes 266 // (the message digest attribute is automatically added) 267 try { 268 Attribute[] attributes = { new Attribute(new CMSContentType(contentType)) }; 269 authenticatedData.setAuthenticatedAttributes(attributes); 270 } catch (Exception ex) { 271 throw new CMSException("Error creating attribute: " + ex.toString()); 272 } 273 } 274 275 int blockSize = 20; // in real world we would use a block size like 2048 276 // write in the data to be signed 277 byte[] buffer = new byte[blockSize]; 278 int bytesRead; 279 while ((bytesRead = is.read(buffer)) != -1) { 280 authenticatedData.write(buffer, 0, bytesRead); 281 } 282 283 // closing the stream adds auth/unauth attributes, calculates and adds the mac value, . 284 authenticatedData.close(); 285 return resultStream.toByteArray(); 286 } 287 288 /** 289 * Decrypts the encrypted MAC key for the recipient identified by its index 290 * into the recipientInfos field and uses the MAC key to verify 291 * the authenticated data. 292 * <p> 293 * This way of decrypting the MAC key and verifying the content may be used for 294 * any type of RecipientInfo (KeyTransRecipientInfo, KeyAgreeRecipientInfo, 295 * KEKRecipientInfo, PasswordRecipeintInfo, OtherRecipientInfo), but requires to 296 * know at what index of the recipientInfos field the RecipientInfo for the 297 * particular recipient in mind can be found. 298 * If the recipient in mind uses a RecipientInfo of type KeyAgreeRecipientInfo 299 * some processing overhead may take place because a KeyAgreeRecipientInfo may 300 * contain encrypted mac keys for more than only one recipient; since the 301 * recipientInfoIndex only specifies the RecipientInfo but not the encrypted 302 * mac key -- if there are more than only one -- repeated decryption runs may be 303 * required as long as the decryption process completes successfully. 304 * 305 * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array 306 * @param message the content message, if transmitted by other means (explicit mode) 307 * @param key the key to decrypt the mac key 308 * @param recipientInfoIndex the index of the right <code>RecipientInfo</code> to 309 * which the given key belongs 310 * 311 * @return the verified message, as byte array 312 * 313 * @throws CMSException if the authenticated data cannot be verified 314 * @throws IOException if a stream read/write error occurs 315 */ 316 public byte[] getAuthenticatedDataStream(byte[] encoding, 317 byte[] message, 318 Key key, 319 int recipientInfoIndex) 320 throws CMSException, IOException { 321 322 // parse the BER encoded AuthenticatedData 323 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 324 AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is); 325 326 if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) { 327 // in explicit mode explicitly supply the content for hash/mac computation 328 authenticatedData.setInputStream(new ByteArrayInputStream(message)); 329 } 330 331 System.out.println("\nThis message can be verified by the following recipients:"); 332 RecipientInfo[] recipients = authenticatedData.getRecipientInfos(); 333 334 // for demonstration purposes we only look one time for all recipients included: 335 if (recipientInfoIndex == 0) { 336 int k = 0; 337 for (int i=0; i<recipients.length; i++) { 338 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 339 for (int j = 0; j < recipientIDs.length; j++) { 340 System.out.println("Recipient "+(++k)+":"); 341 System.out.println(recipientIDs[j]); 342 } 343 } 344 } 345 // decrypt the mac key and verify the mac for the indented recipient 346 try { 347 authenticatedData.setupMac(key, recipientInfoIndex); 348 InputStream contentStream = authenticatedData.getInputStream(); 349 ByteArrayOutputStream os = new ByteArrayOutputStream(); 350 Util.copyStream(contentStream, os, null); 351 352 if (authenticatedData.verifyMac() == false) { 353 throw new CMSException("Mac verification error!"); 354 } 355 System.out.println("Mac successfully verified!"); 356 357 return os.toByteArray(); 358 359 } catch (InvalidKeyException ex) { 360 throw new CMSException("Key error: "+ex.getMessage()); 361 } catch (NoSuchAlgorithmException ex) { 362 throw new CMSException(ex.toString()); 363 } 364 } 365 366 /** 367 * Decrypts the encrypted MAC key for the recipient identified by recipient identifier 368 * and uses the MAC key to verify the authenticated data. 369 * <p> 370 * This way of decrypting the encrypted mac key may be used for any type of RecipientInfo 371 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 372 * recipient in mind is identified by its recipient identifier. 373 * 374 * @param encoding the <code>AuthenticatedData</code> object as BER encoded byte array 375 * @param key the key to decrypt the encrypted mac key 376 * @param recipientID the recipient identifier uniquely identifying the key of the 377 * recipient 378 * 379 * @return the verified message, as byte array 380 * 381 * @throws CMSException if the authenticated data cannot be verified 382 * @throws IOException if a stream read/write error occurs 383 */ 384 public byte[] getAuthenticatedDataStream(byte[] encoding, 385 byte[] message, 386 Key key, 387 KeyIdentifier recipientID) 388 throws CMSException, IOException { 389 390 // parse the BER encoded AuthenticatedData 391 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 392 AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is); 393 394 if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) { 395 // in explicit mode explicitly supply the content for hash/mac computation 396 authenticatedData.setInputStream(new ByteArrayInputStream(message)); 397 } 398 399 // get the right RecipientInfo 400 System.out.println("\nSearch for RecipientInfo:"); 401 RecipientInfo recipient = authenticatedData.getRecipientInfo(recipientID); 402 if (recipient != null) { 403 System.out.println("RecipientInfo: " + recipient); 404 } else { 405 throw new CMSException("No recipient with ID: " + recipientID); 406 } 407 // decrypt the mac key and verify the content mac 408 try { 409 System.out.println("Decrypt encrypted mac key..."); 410 SecretKey cek = recipient.decryptKey(key, recipientID); 411 System.out.println("Verify content mac with decrypted mac key..."); 412 authenticatedData.setupMac(cek); 413 InputStream contentStream = authenticatedData.getInputStream(); 414 ByteArrayOutputStream os = new ByteArrayOutputStream(); 415 Util.copyStream(contentStream, os, null); 416 417 if (authenticatedData.verifyMac() == false) { 418 throw new CMSException("Mac verification error!"); 419 } 420 System.out.println("Mac successfully verified!"); 421 422 return os.toByteArray(); 423 424 } catch (InvalidKeyException ex) { 425 throw new CMSException("Key error: "+ex.getMessage()); 426 } catch (NoSuchAlgorithmException ex) { 427 throw new CMSException(ex.toString()); 428 } 429 } 430 431 /** 432 * Decrypts the encrypted content of the given <code>AuthenticatedData</code> object for 433 * the recipient identified by its recipient certificate or kekID. 434 * <p> 435 * 436 * @param encoding the <code>AuthenticatedData</code> object as DER encoded byte array 437 * @param key the key to decrypt the message 438 * @param recipientCert the certificate of the recipient having a RecipientInfo of 439 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo 440 * @param kekID the kekID identifying the recipient key when using a RecipientInfo 441 * of type KEKRecipientInfo 442 * 443 * @return the verified message, as byte array 444 * 445 * @throws CMSException if the authenticated data cannot be verified 446 * @throws IOException if a stream read/write error occurs 447 */ 448 public byte[] getAuthenticatedDataStream(byte[] encoding, 449 byte[] message, 450 Key key, 451 X509Certificate recipientCert, 452 byte[] kekID) 453 throws CMSException, IOException { 454 455 // create the AuthenticatedData object from a DER encoded byte array 456 // we are testing the stream interface 457 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 458 AuthenticatedDataStream authenticatedData = new AuthenticatedDataStream(is); 459 460 if (authenticatedData.getMode() == AuthenticatedDataStream.EXPLICIT) { 461 // in explicit mode explicitly supply the content for hash/mac computation 462 authenticatedData.setInputStream(new ByteArrayInputStream(message)); 463 } 464 465 466 // decrypt the mac key and verify the content mac 467 try { 468 System.out.println("Verify mac..."); 469 if (recipientCert != null) { 470 authenticatedData.setupMac(key, recipientCert); 471 } else { 472 // KEKRecipientInfo 473 authenticatedData.setupMac(key, new KEKIdentifier(kekID)); 474 } 475 InputStream contentStream = authenticatedData.getInputStream(); 476 ByteArrayOutputStream os = new ByteArrayOutputStream(); 477 Util.copyStream(contentStream, os, null); 478 479 if (authenticatedData.verifyMac() == false) { 480 throw new CMSException("Mac verification error!"); 481 } 482 System.out.println("Mac successfully verified!"); 483 484 return os.toByteArray(); 485 486 } catch (InvalidKeyException ex) { 487 throw new CMSException("Key error: "+ex.getMessage()); 488 } catch (NoSuchAlgorithmException ex) { 489 throw new CMSException(ex.toString()); 490 } 491 } 492 493 494 /** 495 * Creates the RecipientInfos. 496 * 497 * @return the RecipientInfos created 498 * 499 * @throws CMSException if an error occurs when creating the recipient infos 500 */ 501 public RecipientInfo[] createRecipients() throws CMSException { 502 503 504 RecipientInfo[] recipients = new RecipientInfo[4]; 505 try { 506 507 // rsaUser1 is the first receiver (cert identified by IssuerAndSerialNumber) 508 recipients[0] = new KeyTransRecipientInfo(rsaUser1, 509 (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 510 // rsaUser2 is the second receiver (cert identifief by SubjectKeyIdentifier) 511 recipients[1] = new KeyTransRecipientInfo(rsaUser2, 512 CertificateIdentifier.SUBJECT_KEY_IDENTIFIER, 513 (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 514 515 // next recipients use key agreement (Static-Static Diffie-Hellman) 516 // the key encryption (key agreement) algorithm to use: 517 AlgorithmID keyEA = (AlgorithmID)AlgorithmID.ssdhKeyAgreement.clone(); 518 // the key wrap algorithm to use: 519 AlgorithmID keyWrapAlg = (AlgorithmID)CMSAlgorithmID.cms_HMACwithAES_wrap.clone(); 520 // the length of the key encryption key to be generated: 521 int kekLength = 128; 522 // in static-static mode we may supply user keying material 523 byte[] ukm = new byte[64]; 524 random.nextBytes(ukm); 525 // ssdhUser1 is originator 526 recipients[2] = new KeyAgreeRecipientInfo(ssdhUser1, 527 ssdhUser1_pk, 528 KeyIdentifier.ISSUER_AND_SERIALNUMBER, 529 keyEA, 530 keyWrapAlg, 531 kekLength, 532 ukm); 533 // add ssdhUser1 (originator) as recipient, too 534 ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(ssdhUser1, CertificateIdentifier.ISSUER_AND_SERIALNUMBER); 535 // ssdhUser2 is the recipient (cert identified by RecipientKeyIdentifier) 536 ((KeyAgreeRecipientInfo)recipients[2]).addRecipient(ssdhUser2, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER); 537 538 // last receiver uses a symmetric key encryption key 539 AlgorithmID kea = (AlgorithmID)CMSAlgorithmID.cms_aes256_wrap.clone(); 540 KEKIdentifier kekIdentifier = new KEKIdentifier(kekID); 541 recipients[3] = new KEKRecipientInfo(kekIdentifier, kea, kek); 542 543 } catch (Exception ex) { 544 throw new CMSException("Error adding recipients: " + ex.getMessage()); 545 } 546 return recipients; 547 } 548 549 /** 550 * Parses an AuthenticatedData, decrypts the mac keys for all test recipients 551 * using the index into the recipientInfos field for identifying the recipient 552 * and verifies the content mac. 553 * 554 * @param encodedAuthenticatedData the encoded AuthenticatedData object 555 * 556 * @throws Exception if some error occurs during mac key decryption / mac verification 557 */ 558 public void parseAuthenticatedDataWithRecipientInfoIndex(byte[] encodedAuthenticatedData, 559 byte[] message) 560 throws Exception { 561 562 byte[] received_message; 563 564 // rsaUser1 565 System.out.println("\nVerify MAC for rsaUser1:"); 566 567 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 568 message, 569 rsaUser1_pk, 570 0); 571 System.out.print("\nContent: "); 572 System.out.println(new String(received_message)); 573 574 // rsaUser2 575 System.out.println("\nVerify MAC for rsaUser2:"); 576 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 577 message, 578 rsaUser2_pk, 579 1); 580 581 // ssdhUser1 582 System.out.println("\nVerify MAC for ssdhUser1:"); 583 584 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 585 message, 586 ssdhUser1_pk, 587 2); 588 System.out.print("\nContent: "); 589 System.out.println(new String(received_message)); 590 591 // ssdhUser2 592 System.out.println("\nVerify MAC for ssdhUser2:"); 593 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 594 message, 595 ssdhUser2_pk, 596 2); 597 598 // kekUser 599 System.out.println("\nVerify MAC for kekUser:"); 600 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 601 message, 602 kek, 603 3); 604 System.out.print("\nContent: "); 605 System.out.println(new String(received_message)); 606 } 607 608 /** 609 * Parses an AuthenticatedData, decrypts the mac keys for all test recipients 610 * using their recipient identifiers for identifying the recipient 611 * and verifies the content mac. 612 * 613 * @param encodedAuthenticatedData the encoded AuthenticatedData object 614 * 615 * @throws Exception if some error occurs during mac key decryption / mac verification 616 */ 617 public void parseAuthenticatedDataWithRecipientIdentifier(byte[] encodedAuthenticatedData, 618 byte[] message) 619 throws Exception { 620 621 byte[] received_message; 622 623 // rsaUser1 624 System.out.println("\nVerify MAC for rsaUser1:"); 625 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 626 message, 627 rsaUser1_pk, 628 new IssuerAndSerialNumber(rsaUser1)); 629 System.out.print("\nContent: "); 630 System.out.println(new String(received_message)); 631 632 // rsaUser2 633 System.out.println("\nVerify MAC for rsaUser2:"); 634 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 635 message, 636 rsaUser2_pk, 637 new SubjectKeyID(rsaUser2)); 638 639 // ssdhUser1 640 System.out.println("\nVerify MAC for ssdhUser1:"); 641 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 642 message, 643 ssdhUser1_pk, 644 new IssuerAndSerialNumber(ssdhUser1)); 645 System.out.print("\nContent: "); 646 System.out.println(new String(received_message)); 647 648 // ssdhUser2 649 System.out.println("\nVerify MAC for ssdhUser2:"); 650 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 651 message, 652 ssdhUser2_pk, 653 new RecipientKeyIdentifier(ssdhUser2)); 654 655 // kekUser 656 System.out.println("\nVerify MAC for kekUser:"); 657 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 658 message, 659 kek, 660 new KEKIdentifier(kekID)); 661 662 System.out.print("\nDecrypted content: "); 663 System.out.println(new String(received_message)); 664 665 System.out.print("\nContent: "); 666 System.out.println(new String(received_message)); 667 } 668 669 /** 670 * Parses an AuthenticatedData, decrypts the encrypted mac keys for all test recipients 671 * using their recipient certificate (or KEK id) for identifying the recipient 672 * and verifies the content mac. 673 * 674 * @param encodedAuthenticatedData the encoded AuthenticatedData object 675 * 676 * @throws Exception if some error occurs during mac key decryption / mac verification 677 */ 678 public void parseAuthenticatedDataWithRecipientCertOrKEKId(byte[] encodedAuthenticatedData, 679 byte[] message) 680 throws Exception { 681 682 byte[] received_message; 683 // rsaUser1 684 System.out.println("\nVerify MAC for rsaUser1:"); 685 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 686 message, 687 rsaUser1_pk, 688 rsaUser1, 689 null); 690 System.out.print("\nContent: "); 691 System.out.println(new String(received_message)); 692 // rsaUser2 693 System.out.println("\nVerify MAC for rsaUser2:"); 694 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 695 message, 696 rsaUser2_pk, 697 rsaUser2, 698 null); 699 700 701 // ssdhUser1 702 System.out.println("\nVerify MAC for ssdhUser1:"); 703 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 704 message, 705 ssdhUser1_pk, 706 ssdhUser1, 707 null); 708 System.out.print("\nContent: "); 709 System.out.println(new String(received_message)); 710 // ssdhUser2 711 System.out.println("\nVerify MAC for ssdhUser2:"); 712 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 713 message, 714 ssdhUser2_pk, 715 ssdhUser2, 716 null); 717 // kekUser 718 System.out.println("\nVerify MAC for kekUser:"); 719 received_message = getAuthenticatedDataStream(encodedAuthenticatedData, 720 message, 721 kek, 722 null, 723 kekID); 724 System.out.print("\nDecrypted content: "); 725 System.out.println(new String(received_message)); 726 727 System.out.print("\nContent: "); 728 System.out.println(new String(received_message)); 729 } 730 731 /** 732 * Starts the test. 733 */ 734 public void start() { 735 // the test message 736 String m = "This is the test message."; 737 System.out.println("Test message: \""+m+"\""); 738 System.out.println(); 739 byte[] message = m.getBytes(); 740 741 try { 742 byte[] encodedAuthenticatedData; 743 744 AlgorithmID macAlgorithm = (AlgorithmID)AlgorithmID.hMAC_SHA256.clone(); 745 int macKeyLength = 32; 746 AlgorithmID digestAlgorithm = (AlgorithmID)AlgorithmID.sha256.clone(); 747 748 System.out.println("Stream implementation demos"); 749 System.out.println("==========================="); 750 751 // implict mode; with authenticated attributes 752 System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, implicit mode]:\n"); 753 encodedAuthenticatedData = createAuthenticatedDataStream(message, 754 macAlgorithm, 755 macKeyLength, 756 digestAlgorithm, 757 AuthenticatedDataOutputStream.IMPLICIT); 758 // transmit data 759 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n"); 760 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field."); 761 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, null); 762 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier."); 763 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, null); 764 System.out.println("Decrypt and verify for the several recipients using their certificate or kek."); 765 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, null); 766 767 // implict mode; without authenticated attributes 768 System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, implicit mode]:\n"); 769 encodedAuthenticatedData = createAuthenticatedDataStream(message, 770 macAlgorithm, 771 macKeyLength, 772 null, 773 AuthenticatedDataOutputStream.IMPLICIT); 774 // transmit data 775 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, implicit mode]:\n"); 776 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field."); 777 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, null); 778 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier."); 779 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, null); 780 System.out.println("Decrypt and verify for the several recipients using their certificate or kek."); 781 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, null); 782 783 784 // explict mode; with authenticated attributes 785 System.out.println("\nCMS AuthenticatedDataOutputStream demo with authenticated attributes [create, explicit mode]:\n"); 786 encodedAuthenticatedData = createAuthenticatedDataStream(message, 787 macAlgorithm, 788 macKeyLength, 789 digestAlgorithm, 790 AuthenticatedDataOutputStream.EXPLICIT); 791 // transmit data 792 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, explicit mode]:\n"); 793 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field."); 794 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, message); 795 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier."); 796 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, message); 797 System.out.println("Decrypt and verify for the several recipients using their certificate or kek."); 798 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, message); 799 800 // explict mode; without authenticated attributes 801 System.out.println("\nCMS AuthenticatedDataOutputStream demo without authenticated attributes [create, explicit mode]:\n"); 802 encodedAuthenticatedData = createAuthenticatedDataStream(message, 803 macAlgorithm, 804 macKeyLength, 805 null, 806 AuthenticatedDataOutputStream.EXPLICIT); 807 // transmit data 808 System.out.println("\nCMS AuthenticatedDataOutputStream demo [parse, explicit mode]:\n"); 809 System.out.println("Decrypt and verify for the several recipients using their index into the recipientInfos field."); 810 parseAuthenticatedDataWithRecipientInfoIndex(encodedAuthenticatedData, message); 811 System.out.println("Decrypt and verify for the several recipients using their RecipientIdentifier."); 812 parseAuthenticatedDataWithRecipientIdentifier(encodedAuthenticatedData, message); 813 System.out.println("Decrypt and verify for the several recipients using their certificate or kek."); 814 parseAuthenticatedDataWithRecipientCertOrKEKId(encodedAuthenticatedData, message); 815 816 } catch (Exception ex) { 817 ex.printStackTrace(); 818 throw new RuntimeException(ex.toString()); 819 } 820 } 821 822 /** 823 * Main method. 824 * 825 * @throws IOException 826 * if an I/O error occurs when reading required keys 827 * and certificates from files 828 */ 829 public static void main(String argv[]) throws Exception { 830 831 DemoUtil.initDemos(); 832 833 (new AuthenticatedDataOutputStreamDemo()).start(); 834 System.out.println("Ready!"); 835 System.in.read(); 836 } 837 }