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