001// Copyright (C) 2002 IAIK 002// https://sic.tech/ 003// 004// Copyright (C) 2003 - 2025 Stiftung Secure Information and 005// Communication Technologies SIC 006// https://sic.tech/ 007// 008// All rights reserved. 009// 010// This source is provided for inspection purposes and recompilation only, 011// unless specified differently in a contract with IAIK. This source has to 012// be kept in strict confidence and must not be disclosed to any third party 013// under any circumstances. Redistribution in source and binary forms, with 014// or without modification, are <not> permitted in any case! 015// 016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 019// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 026// SUCH DAMAGE. 027// 028// $Header: /IAIK-CMS/current/src/demo/cms/ecc/EckaEGAuthEnvelopedDataDemo.java 12 12.02.25 17:58 Dbratko $ 029// $Revision: 12 $ 030// 031 032 033package demo.cms.ecc; 034 035import iaik.asn1.CodingException; 036import iaik.asn1.ObjectID; 037import iaik.asn1.structures.AlgorithmID; 038import iaik.asn1.structures.Attribute; 039import iaik.cms.AuthEnvelopedData; 040import iaik.cms.AuthEnvelopedDataOutputStream; 041import iaik.cms.AuthEnvelopedDataStream; 042import iaik.cms.CMSAlgorithmID; 043import iaik.cms.CMSException; 044import iaik.cms.CertificateIdentifier; 045import iaik.cms.CompressedData; 046import iaik.cms.CompressedDataOutputStream; 047import iaik.cms.CompressedDataStream; 048import iaik.cms.ContentInfo; 049import iaik.cms.ContentInfoOutputStream; 050import iaik.cms.ContentInfoStream; 051import iaik.cms.EncryptedContentInfo; 052import iaik.cms.EncryptedContentInfoStream; 053import iaik.cms.IssuerAndSerialNumber; 054import iaik.cms.KeyAgreeRecipientInfo; 055import iaik.cms.KeyIdentifier; 056import iaik.cms.RecipientInfo; 057import iaik.cms.RecipientKeyIdentifier; 058import iaik.cms.SignedData; 059import iaik.cms.SignedDataStream; 060import iaik.cms.SignerInfo; 061import iaik.cms.attributes.CMSContentType; 062import iaik.cms.attributes.SigningTime; 063import iaik.security.random.SecRandom; 064import iaik.utils.Util; 065import iaik.x509.X509Certificate; 066 067import java.io.ByteArrayInputStream; 068import java.io.ByteArrayOutputStream; 069import java.io.IOException; 070import java.io.InputStream; 071import java.security.InvalidKeyException; 072import java.security.Key; 073import java.security.NoSuchAlgorithmException; 074import java.security.PrivateKey; 075import java.security.SecureRandom; 076import java.security.SignatureException; 077 078import javax.crypto.SecretKey; 079 080import demo.DemoUtil; 081import demo.cms.ecc.keystore.CMSEccKeyStore; 082 083 084/** 085 * Demonstrates the usage of class {@link iaik.cms.SignedDataStream} and 086 * {@link iaik.cms.SignedData}, and {@link iaik.cms.AuthEnvelopedDataStream} and 087 * {@link iaik.cms.AuthEnvelopedData} according to the BSI Technical 088 * Recommendation <a href = 089 * "https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/TechnischeRichtlinien/TR03109/TR-03109-1_Anlage_CMS.pdf;jsessionid=3DD6E4FBAC90766F8E2AB3F79BB003C5.1_cid341?__blob=publicationFile&v=1" target="_blank"> 090 * BSI TR-03109-1</a> for transmitting signed authenticated encrypted 091 * data between Smart-Meter-Gateways and external market participants and the 092 * Smart Meter Gateway Administrator. 093 * <p> 094 * This demo uses AES-GCM as specified by <a href = "http://www.ietf.org/rfc/rfc5084.txt" target="_blank">RFC 5084</a> 095 * and AES-CBC-CMAC as specified by BSI TR-03109-1 for authenticated encryption. 096 * The demo compressed the data, creates an AuthEnvelopedData object, packs it into a SignedData and 097 * subsequently shows several ways that may be used for decrypting the content 098 * for some particular recipient. 099 * <p> 100 * Any keys/certificates required for this demo are read from a keystore 101 * file "cmsecc.keystore" located in your current working directory. If 102 * the keystore file does not exist you can create it by running the 103 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore} 104 * program. 105 * <p> 106 * Additionally to <code>iaik_cms.jar</code> you also must have 107 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href = 108 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank"> 109 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>), 110 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href = 111 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank"> 112 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>) 113 * in your classpath. 114 * 115 * @see iaik.cms.AuthEnvelopedDataStream 116 * @see iaik.cms.AuthEnvelopedData 117 * @see iaik.cms.SignedDataStream 118 * @see iaik.cms.SignedData 119 * @see iaik.cms.RecipientInfo 120 * @see iaik.cms.KeyAgreeRecipientInfo 121 * 122 */ 123public class EckaEGAuthEnvelopedDataDemo { 124 125 // certificate of signer 126 X509Certificate[] ecdsa256bitSignerCertChain_; 127 // private key of signer 128 PrivateKey ecdsa256bitSignerPrivateKey_; 129 130 // certificate of signer 131 X509Certificate[] ecdsa384bitSignerCertChain_; 132 // private key of signer 133 PrivateKey ecdsa384bitSignerPrivateKey_; 134 135 //certificate of signer 136 X509Certificate[] ecdsa521bitSignerCertChain_; 137 // private key of signer 138 PrivateKey ecdsa521bitSignerPrivateKey_; 139 140 141 // certificate of recipient 1 (recipient 1 is signer) 142 X509Certificate ecdh256bitRecipient1Cert_; 143 // private key of recipient 1 144 PrivateKey ecdh256bitRecipient1PrivateKey_; 145 // certificate of recipient 2 146 X509Certificate ecdh256bitRecipient2Cert_; 147 // private key of recipient 2 148 PrivateKey ecdh256bitRecipient2PrivateKey_; 149 150 //certificate of recipient 1 (recipient 1 is signer) 151 X509Certificate ecdh384bitRecipient1Cert_; 152 // private key of recipient 1 153 PrivateKey ecdh384bitRecipient1PrivateKey_; 154 // certificate of recipient 2 155 X509Certificate ecdh384bitRecipient2Cert_; 156 // private key of recipient 2 157 PrivateKey ecdh384bitRecipient2PrivateKey_; 158 159 //certificate of recipient 1 (recipient 1 is signer) 160 X509Certificate ecdh521bitRecipient1Cert_; 161 // private key of recipient 1 162 PrivateKey ecdh521bitRecipient1PrivateKey_; 163 // certificate of recipient 2 164 X509Certificate ecdh521bitRecipient2Cert_; 165 // private key of recipient 2 166 PrivateKey ecdh521bitRecipient2PrivateKey_; 167 168 // secure random number generator 169 SecureRandom random; 170 171 /** 172 * Setup the demo certificate chains. 173 * 174 * Keys and certificates are retrieved from the demo KeyStore ("cmsecc.keystore") 175 * file which has to be located in your current working directory and may be 176 * created by running {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore 177 * SetupCMSEccKeyStore}. 178 * 179 * @throws IOException if an file read error occurs 180 */ 181 public EckaEGAuthEnvelopedDataDemo() throws IOException { 182 183 System.out.println(); 184 System.out.println("**********************************************************************************"); 185 System.out.println("* EckaEGAuthEnvelopedDataDemo *"); 186 System.out.println("* (CMS AuthEnvelopedData/SignedData for type implementation for BSI TR-03109-1) *"); 187 System.out.println("**********************************************************************************"); 188 System.out.println(); 189 190 ecdsa256bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN); 191 ecdsa256bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN); 192 ecdsa384bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN); 193 ecdsa384bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN); 194 ecdsa521bitSignerCertChain_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN); 195 ecdsa521bitSignerPrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN); 196 197 198 ecdh256bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_1)[0]; 199 ecdh256bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_1); 200 ecdh256bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_2)[0]; 201 ecdh256bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_256_CRYPT_2); 202 203 ecdh384bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_1)[0]; 204 ecdh384bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_1); 205 ecdh384bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_2)[0]; 206 ecdh384bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_384_CRYPT_2); 207 208 ecdh521bitRecipient1Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_1)[0]; 209 ecdh521bitRecipient1PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_1); 210 ecdh521bitRecipient2Cert_ = CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_2)[0]; 211 ecdh521bitRecipient2PrivateKey_ = CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDH, CMSEccKeyStore.SZ_521_CRYPT_2); 212 random = SecRandom.getDefault(); 213 214 } 215 216 217 /** 218 * Creates a CMS <code>AuthEnvelopedData</code> message using class <code>AuthEnvelopedDataStream</code>. 219 * 220 * @param message the message to be authenticated-enveloped, as byte representation 221 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 222 * @param recipient1Cert the certificate of the first recipient (sender) 223 * @param recipient2Cert the certificate of the second recipient * 224 * 225 * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created 226 * 227 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot 228 * be created 229 * @throws IOException if an I/O error occurs 230 */ 231 public byte[] createAuthEnvelopedDataStream(byte[] message, 232 AlgorithmID contentAuthEncAlg, 233 X509Certificate recipient1Cert, 234 X509Certificate recipient2Cert) 235 throws CMSException, IOException { 236 237 System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg); 238 239 // we are testing the stream interface 240 ByteArrayInputStream is = new ByteArrayInputStream(message); 241 // create a new AuthEnvelopedData object 242 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(ObjectID.cms_compressedData, is, contentAuthEncAlg); 243 244 // create some authenticated attributes 245 try { 246 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) }; 247 authEnvelopedData.setAuthenticatedAttributes(attributes); 248 } catch (Exception ex) { 249 throw new CMSException("Error creating attribute: " + ex.toString()); 250 } 251 252 // create the recipient infos 253 RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert); 254 // specify the recipients of the encrypted message 255 authEnvelopedData.setRecipientInfos(recipients); 256 257 // wrap into ContentInfo 258 ContentInfoStream contentInfo = new ContentInfoStream(authEnvelopedData); 259 ByteArrayOutputStream os = new ByteArrayOutputStream(); 260 contentInfo.writeTo(os); 261 return os.toByteArray(); 262 } 263 264 /** 265 * Creates a CMS <code>AuthEnvelopedData</code> message using class 266 * <code>AuthEnvelopedDataOutputStream</code>. The content data is 267 * compressed inside this method. 268 * 269 * @param message the message to be authenticated-enveloped, as byte representation 270 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 271 * @param recipient1Cert the certificate of the first recipient (sender) 272 * @param recipient2Cert the certificate of the second recipient 273 * 274 * @return the BER encoding of the <code>AuthEnvelopedData</code> object just created 275 * 276 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot 277 * be created 278 * @throws IOException if an I/O error occurs 279 */ 280 public byte[] createAuthEnvelopedDataOutputStream(byte[] message, 281 AlgorithmID contentAuthEncAlg, 282 X509Certificate recipient1Cert, 283 X509Certificate recipient2Cert) 284 throws CMSException, IOException { 285 286 System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg); 287 288 // we are testing the stream interface 289 ByteArrayInputStream is = new ByteArrayInputStream(message); 290 // the stream to which to write the AuthEnvelopedData 291 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 292 AuthEnvelopedDataOutputStream authEnvelopedData; 293 294 // wrap AuthEnvelopedData into a ContentInfo 295 ContentInfoOutputStream contentInfoStream = 296 new ContentInfoOutputStream(ObjectID.cms_authEnvelopedData, resultStream); 297 298 // create a new AuthEnvelopedData object 299 authEnvelopedData = new AuthEnvelopedDataOutputStream(contentInfoStream, 300 contentAuthEncAlg); 301 302 // create the recipient infos 303 RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert); 304 // specify the recipients of the encrypted message 305 authEnvelopedData.setRecipientInfos(recipients); 306 307 // create some authenticated attributes 308 try { 309 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) }; 310 authEnvelopedData.setAuthenticatedAttributes(attributes); 311 } catch (Exception ex) { 312 throw new CMSException("Error creating attribute: " + ex.toString()); 313 } 314 315 // compress message 316 CompressedDataOutputStream compressedData = 317 new CompressedDataOutputStream(authEnvelopedData, 318 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone()); 319 320 int blockSize = 16; // in real world we would use a block size like 2048 321 // write in the data to be encrypted 322 byte[] buffer = new byte[blockSize]; 323 int bytesRead; 324 while ((bytesRead = is.read(buffer)) != -1) { 325 compressedData.write(buffer, 0, bytesRead); 326 } 327 328 // closing the stream finishes encryption and closes the underlying stream 329 compressedData.close(); 330 // authEnvelopedData.close(); 331 return resultStream.toByteArray(); 332 } 333 334 335 /** 336 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 337 * the recipient identified by its index into the recipientInfos field and verifies 338 * the message authentication code. 339 * <p> 340 * This way of decrypting the content may be used for any type of RecipientInfo 341 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to 342 * know at what index of the recipientInfo field the RecipientInfo for the 343 * particular recipient in mind can be found. If the recipient in mind uses 344 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may 345 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption 346 * keys for more than only one recipient; since the recipientInfoIndex only 347 * specifies the RecipientInfo but not the encrypted content encryption key 348 * -- if there are more than only one -- repeated decryption runs may be 349 * required as long as the decryption process completes successfully. 350 * 351 * @param encoding the <code>AuthEnvelopedData</code> object as DER encoded byte array 352 * @param key the key to decrypt the message 353 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 354 * to which the specified key belongs 355 * 356 * @return the recovered message, as byte array 357 * @throws CMSException if the message cannot be recovered or MAC verification fails 358 * @throws IOException if a stream read/write error occurs 359 */ 360 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, int recipientInfoIndex) 361 throws CMSException, IOException { 362 363 // create the AuthEnvelopedData object from a BER encoded byte array 364 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 365 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is); 366 367 System.out.println("Information about the authenticated encrypted data:"); 368 EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo(); 369 System.out.println("Content type: "+eci.getContentType().getName()); 370 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 371 372 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 373 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos(); 374 375 // for demonstration purposes we only look one time for all recipients included: 376 if (recipientInfoIndex == 0) { 377 int k = 0; 378 for (int i=0; i<recipients.length; i++) { 379 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 380 for (int j = 0; j < recipientIDs.length; j++) { 381 System.out.println("Recipient "+(++k)+":"); 382 System.out.println(recipientIDs[j]); 383 } 384 } 385 } 386 // decrypt the message for the first recipient and verify mac 387 try { 388 authEnvelopedData.setupCipher(key, recipientInfoIndex); 389 InputStream decrypted = authEnvelopedData.getInputStream(); 390 ByteArrayOutputStream os = new ByteArrayOutputStream(); 391 Util.copyStream(decrypted, os, null); 392 byte[] content = os.toByteArray(); 393 394 // get authenticated attributes 395 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType); 396 if (contentTypeAttribute != null) { 397 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue(); 398 System.out.println("Authenticated content type attribute included: " + contentType.get().getName()); 399 } 400 401 return content; 402 } catch (InvalidKeyException ex) { 403 throw new CMSException("Private key error: "+ex.toString()); 404 } catch (NoSuchAlgorithmException ex) { 405 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 406 } catch (CodingException ex) { 407 throw new CMSException("Error reading authenticated attributes: "+ex.toString()); 408 } 409 } 410 411 /** 412 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for 413 * the recipient identified by recipient identifier and verifies the message 414 * authentication code. 415 * <p> 416 * This way of decrypting the content may be used for any type of RecipientInfo 417 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 418 * recipient in mind is identified by its recipient identifier. 419 * 420 * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array 421 * @param key the key to decrypt the message 422 * @param recipientID the recipient identifier uniquely identifying the key of the 423 * recipient 424 * 425 * @return the recovered message, as byte array 426 * @throws CMSException if the message cannot be recovered 427 * @throws IOException if a stream read/write error occurs 428 */ 429 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, KeyIdentifier recipientID) 430 throws CMSException, IOException { 431 432 // create the AuthEnvelopedData object from a DER encoded byte array 433 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 434 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is); 435 436 System.out.println("Information about the encrypted data:"); 437 EncryptedContentInfoStream eci = authEnvelopedData.getEncryptedContentInfo(); 438 System.out.println("Content type: "+eci.getContentType().getName()); 439 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 440 441 // get the right RecipientInfo 442 System.out.println("\nSearch for RecipientInfo:"); 443 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID); 444 if (recipient != null) { 445 System.out.println("RecipientInfo: " + recipient); 446 } else { 447 throw new CMSException("No recipient with ID: " + recipientID); 448 } 449 // decrypt the content encryption key and the content; verify mac 450 try { 451 System.out.println("Decrypt encrypted content encryption key..."); 452 SecretKey cek = recipient.decryptKey(key, recipientID); 453 System.out.println("Decrypt content with decrypted content encryption key..."); 454 authEnvelopedData.setupCipher(cek); 455 InputStream decrypted = authEnvelopedData.getInputStream(); 456 ByteArrayOutputStream os = new ByteArrayOutputStream(); 457 Util.copyStream(decrypted, os, null); 458 byte[] content = os.toByteArray(); 459 460 // get authenticated attributes 461 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType); 462 if (contentTypeAttribute != null) { 463 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue(); 464 System.out.println("Authenticated content type attribute included: " + contentType.get().getName()); 465 } 466 467 return content; 468 } catch (InvalidKeyException ex) { 469 throw new CMSException("Private key error: "+ex.toString()); 470 } catch (NoSuchAlgorithmException ex) { 471 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 472 } catch (CodingException ex) { 473 throw new CMSException("Error reading authenticated attributes: "+ex.toString()); 474 } 475 } 476 477 /** 478 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 479 * the recipient identified by its recipient certificate and verifies the message 480 * authentication code. 481 * <p> 482 * 483 * @param encoding the <code>AuthEnvelopedData</code> object as BER encoded byte array 484 * @param key the key to decrypt the message 485 * @param recipientCert the certificate of the recipient having a RecipientInfo of 486 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo 487 * 488 * @return the recovered message, as byte array 489 * @throws CMSException if the message cannot be recovered 490 * @throws IOException if a stream read/write error occurs 491 */ 492 public byte[] getAuthEnvelopedDataStream(byte[] encoding, Key key, X509Certificate recipientCert) 493 throws CMSException, IOException { 494 495 // create the AuthEnvelopedData object from a BER encoded byte array 496 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 497 AuthEnvelopedDataStream authEnvelopedData = new AuthEnvelopedDataStream(is); 498 499 System.out.println("Information about the encrypted data:"); 500 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)authEnvelopedData.getEncryptedContentInfo(); 501 System.out.println("Content type: "+eci.getContentType().getName()); 502 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 503 504 // decrypt the content encryption key and the content; verify mac 505 try { 506 System.out.println("Decrypt the content..."); 507 authEnvelopedData.setupCipher(key, recipientCert); 508 InputStream decrypted = authEnvelopedData.getInputStream(); 509 ByteArrayOutputStream os = new ByteArrayOutputStream(); 510 Util.copyStream(decrypted, os, null); 511 byte[] content = os.toByteArray(); 512 513 // get authenticated attributes 514 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType); 515 if (contentTypeAttribute != null) { 516 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue(); 517 System.out.println("Authenticated content type attribute included: " + contentType.get().getName()); 518 } 519 520 return content; 521 } catch (InvalidKeyException ex) { 522 throw new CMSException("Private key error: "+ex.toString()); 523 } catch (NoSuchAlgorithmException ex) { 524 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 525 } catch (CodingException ex) { 526 throw new CMSException("Error reading authenticated attributes: "+ex.toString()); 527 } 528 } 529 530 531 // non stream 532 533 /** 534 * Creates a CMS <code>AuthEnvelopedData</code> message. 535 * 536 * @param message the message to be enveloped, as byte representation 537 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 538 * @param recipient1Cert the certificate of the first recipient (sender) 539 * @param recipient2Cert the certificate of the second recipient 540 * 541 * @return the encoded <code>AuthEnvelopedData</code>, as byte array 542 * 543 * @throws CMSException if the <code>AuthEnvelopedData</code> object cannot 544 * be created 545 */ 546 public byte[] createAuthEnvelopedData( 547 byte[] message, 548 AlgorithmID contentAuthEncAlg, 549 X509Certificate recipient1Cert, 550 X509Certificate recipient2Cert) 551 throws CMSException { 552 553 System.out.println("Create a new AuthEnvelopedData message using " + contentAuthEncAlg); 554 555 AuthEnvelopedData authEnvelopedData; 556 557 // create a new AuthEnvelopedData object 558 authEnvelopedData = new AuthEnvelopedData(ObjectID.cms_compressedData, message, contentAuthEncAlg); 559 560 // create some authenticated attributes 561 try { 562 Attribute[] attributes = { new Attribute(new CMSContentType(ObjectID.cms_data)) }; 563 authEnvelopedData.setAuthenticatedAttributes(attributes); 564 } catch (Exception ex) { 565 throw new CMSException("Error creating attribute: " + ex.toString()); 566 } 567 568 // set the RecipientInfos 569 RecipientInfo[] recipients = createRecipients(contentAuthEncAlg, recipient1Cert, recipient2Cert); 570 authEnvelopedData.setRecipientInfos(recipients); 571 authEnvelopedData.getEncryptedContentInfo().setBlockSize(2048); 572 573 // wrap into ContentInfo 574 ContentInfo contentInfo = new ContentInfo(authEnvelopedData); 575 // return encoded EnvelopedData 576 return contentInfo.getEncoded(); 577 } 578 579 580 /** 581 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 582 * the recipient identified by its index into the recipientInfos field and verifies 583 * the message authentication code. 584 * <p> 585 * This way of decrypting the content may be used for any type of RecipientInfo 586 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo), but requires to 587 * know at what index of the recipientInfo field the RecipientInfo for the 588 * particular recipient in mind can be found. If the recipient in mind uses 589 * a RecipientInfo of type KeyAgreeRecipientInfo some processing overhead may 590 * take place because a KeyAgreeRecipientInfo may contain encrypted content-encryption 591 * keys for more than only one recipient; since the recipientInfoIndex only 592 * specifies the RecipientInfo but not the encrypted content encryption key 593 * -- if there are more than only one -- repeated decryption runs may be 594 * required as long as the decryption process completes successfully. 595 * 596 * @param enc the encoded <code>AuthEnvelopedData</code> 597 * 598 * @param key the key to decrypt the message 599 * 600 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 601 * to which the specified key belongs 602 * 603 * @return the recovered message, as byte array 604 * 605 * @throws CMSException if the message cannot be recovered 606 * @throws IOException if an I/O error occurs 607 */ 608 public byte[] getAuthEnvelopedData(byte[] enc, Key key, int recipientInfoIndex) 609 throws CMSException, IOException { 610 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 611 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais); 612 613 System.out.println("Information about the encrypted data:"); 614 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo(); 615 System.out.println("Content type: "+eci.getContentType().getName()); 616 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 617 618 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 619 RecipientInfo[] recipients = authEnvelopedData.getRecipientInfos(); 620 621 // for demonstration purposes we only look one time for all recipients included: 622 if (recipientInfoIndex == 0) { 623 int k = 0; 624 for (int i=0; i<recipients.length; i++) { 625 KeyIdentifier[] recipientIDs = recipients[i].getRecipientIdentifiers(); 626 for (int j = 0; j < recipientIDs.length; j++) { 627 System.out.println("Recipient "+(++k)+":"); 628 System.out.println(recipientIDs[j]); 629 } 630 } 631 } 632 633 // decrypt the message and verify the mac 634 try { 635 authEnvelopedData.setupCipher(key, recipientInfoIndex); 636 byte[] content = authEnvelopedData.getContent(); 637 638 // get authenticated attributes 639 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType); 640 if (contentTypeAttribute != null) { 641 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue(); 642 System.out.println("Authenticated content type attribute included: " + contentType.get().getName()); 643 } 644 645 return content; 646 } catch (InvalidKeyException ex) { 647 throw new CMSException("Private key error: "+ex.toString()); 648 } catch (NoSuchAlgorithmException ex) { 649 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 650 } catch (CodingException ex) { 651 throw new CMSException("Error reading authenticated attributes: "+ex.toString()); 652 } 653 } 654 655 /** 656 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 657 * the recipient identified by recipient identifier. 658 * <p> 659 * This way of decrypting the content may be used for any type of RecipientInfo 660 * (KeyTransRecipientInfo, KeyAgreeRecipientInfo, KEKRecipientInfo). The 661 * recipient in mind is identified by its recipient identifier. 662 * 663 * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object 664 * @param key the key to decrypt the message 665 * @param recipientID the recipient identifier uniquely identifying the key of the 666 * recipient 667 * 668 * @return the recovered message, as byte array 669 * @throws CMSException if the message cannot be recovered 670 * @throws IOException if an I/O error occurs 671 */ 672 public byte[] getAuthEnvelopedData(byte[] enc, Key key, KeyIdentifier recipientID) 673 throws CMSException, IOException { 674 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 675 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais); 676 677 System.out.println("Information about the encrypted data:"); 678 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo(); 679 System.out.println("Content type: "+eci.getContentType().getName()); 680 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 681 682 // get the right RecipientInfo 683 System.out.println("\nSearch for RecipientInfo:"); 684 RecipientInfo recipient = authEnvelopedData.getRecipientInfo(recipientID); 685 if (recipient != null) { 686 System.out.println("RecipientInfo: " + recipient); 687 } else { 688 throw new CMSException("No recipient with ID: " + recipientID); 689 } 690 // decrypt the content encryption key and the content 691 try { 692 byte[] content = null; 693 System.out.println("Decrypt encrypted content encryption key..."); 694 SecretKey cek = recipient.decryptKey(key, recipientID); 695 // decrypt content and verify mac 696 System.out.println("Decrypt content with decrypted content encryption key..."); 697 authEnvelopedData.setupCipher(cek); 698 content = authEnvelopedData.getContent(); 699 // get authenticated attributes 700 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType); 701 if (contentTypeAttribute != null) { 702 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue(); 703 System.out.println("Authenticated content type attribute included: " + contentType.get().getName()); 704 } 705 706 return content; 707 } catch (InvalidKeyException ex) { 708 throw new CMSException("Private key error: "+ex.toString()); 709 } catch (NoSuchAlgorithmException ex) { 710 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 711 } catch (CodingException ex) { 712 throw new CMSException("Error reading authenticated attributes: "+ex.toString()); 713 } 714 } 715 716 /** 717 * Decrypts the encrypted content of the given <code>AuthEnvelopedData</code> object for 718 * the recipient identified by its recipient certificate. 719 * 720 * @param enc the BER encoded <code>AuthEnvelopedData</code> ASN.1 object 721 * @param key the key to decrypt the message 722 * @param recipientCert the certificate of the recipient having a RecipientInfo of 723 * type KeyTransRecipientInfo or KeyAgreeRecipientInfo 724 * 725 * @return the recovered message, as byte array 726 * @throws CMSException if the message cannot be recovered 727 */ 728 public byte[] getAuthEnvelopedData(byte[] enc, Key key, X509Certificate recipientCert) 729 throws CMSException, IOException { 730 ByteArrayInputStream bais = new ByteArrayInputStream(enc); 731 AuthEnvelopedData authEnvelopedData = new AuthEnvelopedData(bais); 732 733 System.out.println("Information about the encrypted data:"); 734 EncryptedContentInfo eci = (EncryptedContentInfo)authEnvelopedData.getEncryptedContentInfo(); 735 System.out.println("Content type: "+eci.getContentType().getName()); 736 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 737 738 // decrypt the content encryption key and the content 739 try { 740 System.out.println("Decrypt the content and verify mac..."); 741 // decrypt content and verify mac 742 authEnvelopedData.setupCipher(key, recipientCert); 743 744 byte[] content = authEnvelopedData.getContent(); 745 746 // get authenticated attributes 747 Attribute contentTypeAttribute = authEnvelopedData.getAuthenticatedAttribute(ObjectID.contentType); 748 if (contentTypeAttribute != null) { 749 CMSContentType contentType = (CMSContentType)contentTypeAttribute.getAttributeValue(); 750 System.out.println("Authenticated content type attribute included: " + contentType.get().getName()); 751 } 752 753 return content; 754 } catch (InvalidKeyException ex) { 755 throw new CMSException("Private key error: "+ex.toString()); 756 } catch (NoSuchAlgorithmException ex) { 757 throw new CMSException("Content encryption algorithm not implemented: "+ex.toString()); 758 } catch (CodingException ex) { 759 throw new CMSException("Error reading authenticated attributes: "+ex.toString()); 760 } 761 762 } 763 764 /** 765 * Creates the RecipientInfos. 766 * 767 * @param contentAuthEncAlg the content encryption algorithm 768 * @param recipient1Cert the certificate of the first recipient (sender) 769 * @param recipient2Cert the certificate of the second recipient 770 * 771 * @return the RecipientInfos created, one 772 * KeyAgreeRecipientInfo (for two recipients with same domain 773 * parameters) 774 * 775 * @throws CMSException if an error occurs when creating the recipient infos 776 */ 777 public RecipientInfo[] createRecipients( 778 AlgorithmID contentAuthEncAlg, 779 X509Certificate recipient1Cert, 780 X509Certificate recipient2Cert) throws CMSException { 781 782 RecipientInfo[] recipients = new RecipientInfo[1]; 783 try { 784 785 // next recipients use key agreement 786 // the key encryption (key agreement) algorithm to use: 787 AlgorithmID keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA256.clone(); 788 // the key wrap algorithm to use: 789 AlgorithmID keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes128_wrap.clone(); 790 // the length of the key encryption key to be generated: 791 int kekLength = 128; 792 if ((contentAuthEncAlg.equals(AlgorithmID.aes192_GCM) || 793 (contentAuthEncAlg.equals(AlgorithmID.aes_CBC_CMAC_192)))) { 794 keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA384.clone(); 795 keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes192_wrap.clone(); 796 kekLength = 192; 797 } else if ((contentAuthEncAlg.equals(AlgorithmID.aes256_GCM) || 798 (contentAuthEncAlg.equals(AlgorithmID.aes_CBC_CMAC_256)))) { 799 keyEA = (AlgorithmID)AlgorithmID.ecka_eg_X963KDF_SHA512.clone(); 800 keyWrapAlg = (AlgorithmID)AlgorithmID.cms_aes256_wrap.clone(); 801 kekLength = 256; 802 } 803 recipients[0] = new KeyAgreeRecipientInfo(keyEA, keyWrapAlg, kekLength); 804 // ecdhUser1 is the third receiver (cert identified by IssuerAndSerialNumber) 805 ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(recipient1Cert, CertificateIdentifier.ISSUER_AND_SERIALNUMBER); 806 // ecdhUser2 is the fourth receiver (cert identified by RecipientKeyIdentifier) 807 ((KeyAgreeRecipientInfo)recipients[0]).addRecipient(recipient2Cert, CertificateIdentifier.RECIPIENT_KEY_IDENTIFIER); 808 809 } catch (Exception ex) { 810 throw new CMSException("Error adding recipients: " + ex.getMessage()); 811 } 812 return recipients; 813 } 814 815 /** 816 * Parses an AuthEnvelopedData and decrypts the content for all test recipients 817 * using the index into the recipientInfos field for identifying the recipient. 818 * 819 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData 820 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 821 * @param recipient1PrivateKey the private key of the first recipient (sender) 822 * @param recipient2PrivateKey the private key of the second recipient 823 * 824 * @throws Exception if some error occurs during decoding/decryption 825 */ 826 public void parseAuthEnvelopedDataWithRecipientInfoIndex( 827 boolean stream, 828 byte[] encodedAuthEnvelopedData, 829 PrivateKey recipient1PrivateKey, 830 PrivateKey recipient2PrivateKey) throws Exception { 831 byte[] receivedMessage; 832 if (stream) { 833 // ecdhUser1 834 System.out.println("\nDecrypt for ecdhUser1:"); 835 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, 0); 836 // decompress 837 receivedMessage = getCompressedDataStream(receivedMessage); 838 System.out.print("\nDecrypted content: "); 839 System.out.println(new String(receivedMessage)); 840 // ecdhUser2 841 System.out.println("\nDecrypt for ecdhUser2:"); 842 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, 0); 843 // decompress 844 receivedMessage = getCompressedDataStream(receivedMessage); 845 System.out.print("\nDecrypted content: "); 846 System.out.println(new String(receivedMessage)); 847 848 } else { 849 // ecdhUser1 850 System.out.println("\nDecrypt for ecdhUser1:"); 851 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, 0); 852 // decompress 853 receivedMessage = getCompressedData(receivedMessage); 854 System.out.print("\nDecrypted content: "); 855 System.out.println(new String(receivedMessage)); 856 // ecdhUser2 857 System.out.println("\nDecrypt for ecdhUser2:"); 858 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, 0); 859 // decompress 860 receivedMessage = getCompressedData(receivedMessage); 861 System.out.print("\nDecrypted content: "); 862 System.out.println(new String(receivedMessage)); 863 864 } 865 } 866 867 /** 868 * Parses an AuthEnvelopedData and decrypts the content for all test recipients 869 * using their recipient identifiers for identifying the recipient. 870 * 871 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData 872 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 873 * @param recipient1PrivateKey the private key of the first recipient (sender) 874 * @param recipient1Cert the certificate of the first recipient (sender) 875 * @param recipient2PrivateKey the private key of the second recipient 876 * @param recipient2Cert the certificate of the second recipient 877 * 878 * @throws Exception if some error occurs during decoding/decryption 879 */ 880 public void parseAuthEnvelopedDataWithRecipientIdentifier( 881 boolean stream, 882 byte[] encodedAuthEnvelopedData, 883 PrivateKey recipient1PrivateKey, 884 X509Certificate recipient1Cert, 885 PrivateKey recipient2PrivateKey, 886 X509Certificate recipient2Cert) throws Exception { 887 byte[] receivedMessage; 888 if (stream) { 889 // ecdhUser1 890 System.out.println("\nDecrypt for ecdhUser1:"); 891 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, new IssuerAndSerialNumber(recipient1Cert)); 892 // decompress 893 receivedMessage = getCompressedDataStream(receivedMessage); 894 System.out.print("\nDecrypted content: "); 895 System.out.println(new String(receivedMessage)); 896 // ecdhUser2 897 System.out.println("\nDecrypt for ecdhUser2:"); 898 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, new RecipientKeyIdentifier(recipient2Cert)); 899 // decompress 900 receivedMessage = getCompressedDataStream(receivedMessage); 901 System.out.print("\nDecrypted content: "); 902 System.out.println(new String(receivedMessage)); 903 } else { 904 // ecdhUser1 905 System.out.println("\nDecrypt for ecdhUser1:"); 906 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, new IssuerAndSerialNumber(recipient1Cert)); 907 // decompress 908 receivedMessage = getCompressedData(receivedMessage); 909 System.out.print("\nDecrypted content: "); 910 System.out.println(new String(receivedMessage)); 911 // ecdhUser2 912 System.out.println("\nDecrypt for ecdhUser2:"); 913 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, new RecipientKeyIdentifier(recipient2Cert)); 914 // decompress 915 receivedMessage = getCompressedData(receivedMessage); 916 System.out.print("\nDecrypted content: "); 917 System.out.println(new String(receivedMessage)); 918 } 919 } 920 921 /** 922 * Parses an AuthEnvelopedData and decrypts the content for all test recipients 923 * using their recipient certificate for identifying the recipient. 924 * 925 * @param stream whether to use AuthEnvelopedDataStream or AuthEnvelopedData 926 * @param encodedAuthEnvelopedData the encoded AuthEnvelopedData object 927 * @param recipient1PrivateKey the private key of the first recipient (sender) 928 * @param recipient1Cert the certificate of the first recipient (sender) 929 * @param recipient2PrivateKey the private key of the second recipient 930 * @param recipient2Cert the certificate of the second recipient 931 * 932 * @throws Exception if some error occurs during decoding/decryption 933 */ 934 public void parseAuthEnvelopedDataWithRecipientCert( 935 boolean stream, 936 byte[] encodedAuthEnvelopedData, 937 PrivateKey recipient1PrivateKey, 938 X509Certificate recipient1Cert, 939 PrivateKey recipient2PrivateKey, 940 X509Certificate recipient2Cert) throws Exception { 941 byte[] receivedMessage; 942 if (stream) { 943 // ecdhUser1 944 System.out.println("\nDecrypt for ecdhUser1:"); 945 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert); 946 // decompress 947 receivedMessage = getCompressedDataStream(receivedMessage); 948 System.out.print("\nDecrypted content: "); 949 System.out.println(new String(receivedMessage)); 950 // ecdhUser2 951 System.out.println("\nDecrypt for ecdhUser2:"); 952 receivedMessage = getAuthEnvelopedDataStream(encodedAuthEnvelopedData, recipient2PrivateKey, recipient2Cert); 953 // decompress 954 receivedMessage = getCompressedDataStream(receivedMessage); 955 System.out.print("\nDecrypted content: "); 956 System.out.println(new String(receivedMessage)); 957 } else { 958 // ecdhUser1 959 System.out.println("\nDecrypt for ecdhUser1:"); 960 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert); 961 // decompress 962 receivedMessage = getCompressedData(receivedMessage); 963 System.out.print("\nDecrypted content: "); 964 System.out.println(new String(receivedMessage)); 965 // ecdhUser2 966 System.out.println("\nDecrypt for ecdhUser2:"); 967 receivedMessage = getAuthEnvelopedData(encodedAuthEnvelopedData, recipient2PrivateKey, recipient2Cert); 968 // decompress 969 receivedMessage = getCompressedData(receivedMessage); 970 System.out.print("\nDecrypted content: "); 971 System.out.println(new String(receivedMessage)); 972 } 973 } 974 975 /** 976 * Creates an ECDSA signed CMS <code>SignedDataStream</code> object and wraps it by a 977 * CMS <code>ContentInfoStream</code>. 978 * 979 * @param message the message to be signed, as byte representation 980 * @param mode the transmission mode, either IMPLICIT or EXPLICIT 981 * @param hashAlgorithm the hash algorithm to be used 982 * @param signatureAlgorithm the signature algorithm to be used 983 * @param signerKey the private key of the signer 984 * @param certificates the certificate chain of the signer 985 * 986 * @return the DER encoding of the <code>ContentInfo</code> object just created 987 * 988 * @throws CMSException if the <code>SignedData</code>, <code>ContentInfo</code> 989 * object cannot be created 990 * @throws IOException if an I/O related error occurs 991 */ 992 public byte[] createSignedDataStream(byte[] message, 993 int mode, 994 AlgorithmID hashAlgorithm, 995 AlgorithmID signatureAlgorithm, 996 PrivateKey signerKey, 997 X509Certificate[] certificates) 998 throws CMSException, IOException { 999 1000 System.out.print("Create a new message signed with " + signatureAlgorithm.getName()); 1001 1002 // we are testing the stream interface 1003 ByteArrayInputStream is = new ByteArrayInputStream(message); 1004 // create a new SignedData object which includes the data 1005 SignedDataStream signed_data = new SignedDataStream(is, ObjectID.cms_authEnvelopedData, mode); 1006 1007 // SignedData shall include the certificate chain for verifying 1008 signed_data.setCertificates(certificates); 1009 1010 // cert at index 0 is the user certificate 1011 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]); 1012 1013 // create a new SignerInfo 1014 AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone(); 1015 // CMS-ECDSA requires to encode the parameter field as NULL (see RFC 3278) 1016 ecdsaSig.encodeAbsentParametersAsNull(true); 1017 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey); 1018 1019 try { 1020 // create some signed attributes 1021 // the message digest attribute is automatically added 1022 Attribute[] attributes = new Attribute[2]; 1023 // content type is data 1024 CMSContentType contentType = new CMSContentType(ObjectID.cms_authEnvelopedData); 1025 attributes[0] = new Attribute(contentType); 1026 // signing time is now 1027 SigningTime signingTime = new SigningTime(); 1028 attributes[1] = new Attribute(signingTime); 1029 1030 // set the attributes 1031 signer_info.setSignedAttributes(attributes); 1032 } catch (Exception ex) { 1033 throw new CMSException("Error adding attributes: " + ex.toString()); 1034 } 1035 1036 // finish the creation of SignerInfo by calling method addSigner 1037 try { 1038 signed_data.addSignerInfo(signer_info); 1039 } catch (NoSuchAlgorithmException ex) { 1040 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 1041 } 1042 1043 // write the data through SignedData to any out-of-band place 1044 if (mode == SignedDataStream.EXPLICIT) { 1045 InputStream data_is = signed_data.getInputStream(); 1046 byte[] buf = new byte[1024]; 1047 int r; 1048 while ((r = data_is.read(buf)) > 0) { 1049 ; // skip data 1050 } 1051 } 1052 1053 signed_data.setBlockSize(2048); 1054 // create the ContentInfo 1055 ContentInfoStream cis = new ContentInfoStream(signed_data); 1056 // return the SignedData as DER encoded byte array with block size 2048 1057 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1058 cis.writeTo(os); 1059 return os.toByteArray(); 1060 } 1061 1062 1063 /** 1064 * Parses a CMS <code>ContentInfo</code> object holding a <code>SignedData</code> 1065 * object and verifies the signature. 1066 * 1067 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 1068 * object as BER encoded byte array 1069 * @param message the the message which was transmitted out-of-band (explicit signed) 1070 * @param certificates the certificate of the signer (used for alternative signature verification) 1071 * 1072 * @return the inherent message as byte array 1073 * 1074 * @throws CMSException if any signature does not verify 1075 * @throws IOException if an I/O related error occurs 1076 */ 1077 public byte[] getSignedDataStream(byte[] signedData, byte[] message, X509Certificate[] certificates) 1078 throws CMSException, IOException { 1079 1080 // we are testing the stream interface 1081 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 1082 1083 SignedDataStream signed_data = new SignedDataStream(is); 1084 1085 if (signed_data.getMode() == SignedDataStream.EXPLICIT) { 1086 // in explicit mode explicitly supply the content for hash computation 1087 signed_data.setInputStream(new ByteArrayInputStream(message)); 1088 } 1089 1090 // get an InputStream for reading the signed content and update hash computation 1091 InputStream data = signed_data.getInputStream(); 1092 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1093 Util.copyStream(data, os, null); 1094 1095 System.out.println("SignedData contains the following signer information:"); 1096 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 1097 1098 int numberOfSignerInfos = signer_infos.length; 1099 if (numberOfSignerInfos == 0) { 1100 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 1101 System.err.println(warning); 1102 throw new CMSException(warning); 1103 } else { 1104 for (int i = 0; i < numberOfSignerInfos; i++) { 1105 try { 1106 // verify the signed data using the SignerInfo at index i 1107 X509Certificate signer_cert = signed_data.verify(i); 1108 // if the signature is OK the certificate of the signer is returned 1109 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 1110 // check for some included attributes 1111 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 1112 if (signingTime != null) { 1113 System.out.println("This message has been signed at " + signingTime.get()); 1114 } 1115 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 1116 if (contentType != null) { 1117 System.out.println("The content has CMS content type " + contentType.get().getName()); 1118 } 1119 } catch (SignatureException ex) { 1120 // if the signature is not OK a SignatureException is thrown 1121 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 1122 throw new CMSException(ex.toString()); 1123 } 1124 } 1125 // now check alternative signature verification 1126 System.out.println("Now check the signature assuming that no certs have been included:"); 1127 try { 1128 SignerInfo signer_info = signed_data.verify(certificates[0]); 1129 // if the signature is OK the certificate of the signer is returned 1130 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 1131 } catch (SignatureException ex) { 1132 // if the signature is not OK a SignatureException is thrown 1133 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN()); 1134 throw new CMSException(ex.toString()); 1135 } 1136 // in practice we also would validate the signer certificate(s) 1137 } 1138 return os.toByteArray(); 1139 } 1140 1141 1142 /** 1143 * Creates an ECDSA signed CMS <code>SignedData</code> object and wraps it by a CMS 1144 * <code>ContentInfo</code> object. 1145 * <p> 1146 * 1147 * @param message the message to be signed, as byte representation 1148 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT 1149 * @param hashAlgorithm the hash algorithm to be used 1150 * @param signatureAlgorithm the signature algorithm to be used 1151 * @param signerKey the private key of the signer 1152 * @param certificates the certificate chain of the signer 1153 * 1154 * @return the DER encoded <code>SignedData</code>-<code>ContentInfo</code> object 1155 * 1156 * @throws CMSException if the <code>SignedData</code>-<code>ContentInfo</code> object cannot 1157 * be created 1158 * @throws IOException if an I/O related error occurs 1159 */ 1160 public byte[] createSignedData(byte[] message, 1161 int mode, 1162 AlgorithmID hashAlgorithm, 1163 AlgorithmID signatureAlgorithm, 1164 PrivateKey signerKey, 1165 X509Certificate[] certificates) 1166 throws CMSException, IOException { 1167 1168 System.out.println("Create a new message signed with " + signatureAlgorithm.getName()); 1169 1170 // create a new SignedData object which includes the data 1171 SignedData signed_data = new SignedData(message, ObjectID.cms_authEnvelopedData, mode); 1172 1173 // SignedData shall include the certificate chain for verifying 1174 signed_data.setCertificates(certificates); 1175 1176 // cert at index 0 is the user certificate 1177 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]); 1178 1179 // create a new SignerInfo 1180 AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone(); 1181 // CMS-ECC requires that the parameters field is encoded as ASN.1 NULL object (see RFC 3278) 1182 ecdsaSig.encodeAbsentParametersAsNull(true); 1183 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey); 1184 1185 try { 1186 // create some signed attributes 1187 // the message digest attribute is automatically added 1188 Attribute[] attributes = new Attribute[2]; 1189 // content type is data 1190 CMSContentType contentType = new CMSContentType(ObjectID.cms_authEnvelopedData); 1191 attributes[0] = new Attribute(contentType); 1192 // signing time is now 1193 SigningTime signingTime = new SigningTime(); 1194 attributes[1] = new Attribute(signingTime); 1195 1196 // set the attributes 1197 signer_info.setSignedAttributes(attributes); 1198 } catch (Exception ex) { 1199 throw new CMSException("Error adding attributes: " + ex.toString()); 1200 } 1201 1202 // finish the creation of SignerInfo by calling method addSigner 1203 try { 1204 signed_data.addSignerInfo(signer_info); 1205 } catch (NoSuchAlgorithmException ex) { 1206 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 1207 } 1208 1209 ContentInfo ci = new ContentInfo(signed_data); 1210 return ci.getEncoded(); 1211 } 1212 1213 1214 /** 1215 * Parses a CMS <code>ContentInfo</code> holding a <code>SignedData</code> 1216 * object and verifies the signature. 1217 * 1218 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 1219 * object as DER encoded byte array 1220 * @param message the message which was transmitted out-of-band (explicit signed) 1221 * @param certificates the certificate of the signer (used for alternative signature verification) 1222 * 1223 * @return the inherent message as byte array 1224 * 1225 * @throws CMSException if any signature does not verify 1226 * @throws IOException if an I/O related error occurs 1227 */ 1228 public byte[] getSignedData(byte[] signedData, byte[] message, X509Certificate[] certificates) 1229 throws CMSException, IOException { 1230 1231 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 1232 // create the SignedData object 1233 SignedData signed_data = new SignedData(is); 1234 1235 if (signed_data.getMode() == SignedData.EXPLICIT) { 1236 // in explcit mode explictly supply the content data to do the hash calculation 1237 signed_data.setContent(message); 1238 } 1239 1240 System.out.println("SignedData contains the following signer information:"); 1241 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 1242 1243 int numberOfSignerInfos = signer_infos.length; 1244 if (numberOfSignerInfos == 0) { 1245 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 1246 System.err.println(warning); 1247 throw new CMSException(warning); 1248 } else { 1249 for (int i = 0; i < numberOfSignerInfos; i++) { 1250 try { 1251 // verify the signed data using the SignerInfo at index i 1252 X509Certificate signer_cert = signed_data.verify(i); 1253 // if the signature is OK the certificate of the signer is returned 1254 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 1255 // check some attributes 1256 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 1257 if (signingTime != null) { 1258 System.out.println("This message has been signed at " + signingTime.get()); 1259 } 1260 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 1261 if (contentType != null) { 1262 System.out.println("The content has CMS content type " + contentType.get().getName()); 1263 } 1264 } catch (SignatureException ex) { 1265 // if the signature is not OK a SignatureException is thrown 1266 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 1267 throw new CMSException(ex.toString()); 1268 } 1269 } 1270 1271 // now check alternative signature verification 1272 System.out.println("Now check the signature assuming that no certs have been included:"); 1273 try { 1274 SignerInfo signer_info = signed_data.verify(certificates[0]); 1275 // if the signature is OK the certificate of the signer is returned 1276 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 1277 } catch (SignatureException ex) { 1278 // if the signature is not OK a SignatureException is thrown 1279 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN()); 1280 throw new CMSException(ex.toString()); 1281 } 1282 // in practice we also would validate the signer certificate(s) 1283 } 1284 return signed_data.getContent(); 1285 } 1286 1287 /** 1288 * Creates a CMS <code>CompressedData</code> object. 1289 * <p> 1290 * @param message the message to be compressed, as byte representation 1291 * 1292 * @return the BER encoding of the <code>CompressedData</code> object just created 1293 * 1294 * @throws CMSException if the <code>CompressedData</code> object cannot 1295 * be created 1296 * @throws IOException if an I/O error occurs 1297 * @throws NoSuchAlgorithmException if the compression algorithm is not supported 1298 */ 1299 public byte[] createCompressedDataStream(byte[] message) 1300 throws CMSException, IOException, NoSuchAlgorithmException { 1301 1302 System.out.println("Create a new CompressedData message"); 1303 1304 // we are testing the stream interface 1305 ByteArrayInputStream is = new ByteArrayInputStream(message); 1306 1307 // create a new CompressedData object 1308 CompressedDataStream compressedData = new CompressedDataStream(is, 1309 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(), 1310 CompressedDataStream.IMPLICIT); 1311 1312 // for testing return the CompressedData as BER encoded byte array with block size of 4 1313 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1314 compressedData.setBlockSize(4); 1315 ContentInfoStream cis = new ContentInfoStream(compressedData); 1316 cis.writeTo(os); 1317 return os.toByteArray(); 1318 } 1319 1320 /** 1321 * Parses a CMS <code>CompressedData</code> object. 1322 * 1323 * @param encoding the <code>CompressedData</code> object as BER encoded byte array 1324 * 1325 * @return the decompressed message as byte array 1326 * 1327 * @throws CMSException if the CompressedData cannot be parsed 1328 * @throws IOException if an I/O error occurs 1329 * @throws NoSuchAlgorithmException if the compression algorithm is not supported 1330 */ 1331 public byte[] getCompressedDataStream(byte[] encoding) 1332 throws CMSException, IOException, NoSuchAlgorithmException { 1333 1334 System.out.println("Parse CompressedData message."); 1335 // we are testing the stream interface 1336 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 1337 // create the CompressedData object 1338 CompressedDataStream compressedData = new CompressedDataStream(is); 1339 // get an InputStream for reading and decompressing the content 1340 InputStream data = compressedData.getInputStream(); 1341 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1342 Util.copyStream(data, os, null); 1343 1344 return os.toByteArray(); 1345 } 1346 1347 1348 /** 1349 * Creates a CMS <code>CompressedData</code> object. 1350 * <p> 1351 * 1352 * @param message the message to be compressed, as byte representation 1353 * 1354 * @return the DER encoded <code>CompressedData</code> 1355 * 1356 * @throws CMSException if the <code>CompressedData</code> object cannot 1357 * be created 1358 * @throws IOException if an I/O error occurs 1359 * @throws NoSuchAlgorithmException if the compression algorithm is not supported 1360 */ 1361 public byte[] createCompressedData(byte[] message) 1362 throws CMSException, IOException, NoSuchAlgorithmException { 1363 1364 System.out.println("Create a new CompressedData message"); 1365 1366 // create a new CompressedData object 1367 CompressedData compressedData = new CompressedData(message, 1368 (AlgorithmID)CMSAlgorithmID.zlib_compress.clone(), 1369 CompressedData.IMPLICIT); 1370 ContentInfo ci = new ContentInfo(compressedData); 1371 return ci.getEncoded(); 1372 } 1373 1374 /** 1375 * Parses a CMS <code>CompressedData</code> object. 1376 * 1377 * @param encoding the DER encoded <code>CompressedData</code> object 1378 * 1379 * @return the decompressed message as byte array 1380 * 1381 * @throws CMSException if the CompressedData cannot be parsed 1382 * @throws IOException if an I/O error occurs 1383 * @throws NoSuchAlgorithmException if the compression algorithm is not supported 1384 */ 1385 public byte[] getCompressedData(byte[] encoding) 1386 throws CMSException, IOException, NoSuchAlgorithmException { 1387 1388 System.out.println("Parse CompressedData message."); 1389 ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding); 1390 // create the CompressedData object 1391 CompressedData compressedData = new CompressedData(encodedStream); 1392 // decompress 1393 return compressedData.getContent(); 1394 } 1395 1396 /** 1397 * Starts the test. 1398 */ 1399 public void start() { 1400 1401 PrivateKey signerPrivateKey = null; 1402 X509Certificate[] signerCertChain = null; 1403 PrivateKey recipient1PrivateKey = null; 1404 X509Certificate recipient1Cert = null; 1405 PrivateKey recipient2PrivateKey = null; 1406 X509Certificate recipient2Cert = null; 1407 1408 // AES-GCM 1409 1410 // AES128-GCM 1411 AlgorithmID contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes128_GCM.clone(); 1412 signerPrivateKey = ecdsa256bitSignerPrivateKey_; 1413 signerCertChain = ecdsa256bitSignerCertChain_; 1414 recipient1PrivateKey = ecdh256bitRecipient1PrivateKey_; 1415 recipient1Cert = ecdh256bitRecipient1Cert_; 1416 recipient2PrivateKey = ecdh256bitRecipient2PrivateKey_; 1417 recipient2Cert = ecdh256bitRecipient2Cert_; 1418 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1419 // AES192-GCM 1420 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes192_GCM.clone(); 1421 signerPrivateKey = ecdsa384bitSignerPrivateKey_; 1422 signerCertChain = ecdsa384bitSignerCertChain_; 1423 recipient1PrivateKey = ecdh384bitRecipient1PrivateKey_; 1424 recipient1Cert = ecdh384bitRecipient1Cert_; 1425 recipient2PrivateKey = ecdh384bitRecipient2PrivateKey_; 1426 recipient2Cert = ecdh384bitRecipient2Cert_; 1427 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1428 // AES256-GCM 1429 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes256_GCM.clone(); 1430 signerPrivateKey = ecdsa521bitSignerPrivateKey_; 1431 signerCertChain = ecdsa521bitSignerCertChain_; 1432 recipient1PrivateKey = ecdh521bitRecipient1PrivateKey_; 1433 recipient1Cert = ecdh521bitRecipient1Cert_; 1434 recipient2PrivateKey = ecdh521bitRecipient2PrivateKey_; 1435 recipient2Cert = ecdh521bitRecipient2Cert_; 1436 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1437 1438 // AES-CBC-CMAC 1439 1440 // AES-CBC-CMAC-128 1441 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_128.clone(); 1442 signerPrivateKey = ecdsa256bitSignerPrivateKey_; 1443 signerCertChain = ecdsa256bitSignerCertChain_; 1444 recipient1PrivateKey = ecdh256bitRecipient1PrivateKey_; 1445 recipient1Cert = ecdh256bitRecipient1Cert_; 1446 recipient2PrivateKey = ecdh256bitRecipient2PrivateKey_; 1447 recipient2Cert = ecdh256bitRecipient2Cert_; 1448 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1449 // AES-CBC-CMAC-192 1450 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_192.clone(); 1451 signerPrivateKey = ecdsa384bitSignerPrivateKey_; 1452 signerCertChain = ecdsa384bitSignerCertChain_; 1453 recipient1PrivateKey = ecdh384bitRecipient1PrivateKey_; 1454 recipient1Cert = ecdh384bitRecipient1Cert_; 1455 recipient2PrivateKey = ecdh384bitRecipient2PrivateKey_; 1456 recipient2Cert = ecdh384bitRecipient2Cert_; 1457 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1458 // AES-CBC-CMAC-256 1459 contentAuthEncAlg = (AlgorithmID)AlgorithmID.aes_CBC_CMAC_256.clone(); 1460 signerPrivateKey = ecdsa521bitSignerPrivateKey_; 1461 signerCertChain = ecdsa521bitSignerCertChain_; 1462 recipient1PrivateKey = ecdh521bitRecipient1PrivateKey_; 1463 recipient1Cert = ecdh521bitRecipient1Cert_; 1464 recipient2PrivateKey = ecdh521bitRecipient2PrivateKey_; 1465 recipient2Cert = ecdh521bitRecipient2Cert_; 1466 start(contentAuthEncAlg, signerPrivateKey, signerCertChain, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1467 } 1468 1469 /** 1470 * Starts the test for the given content-authenticated encryption algorithm. 1471 * 1472 * @param contentAuthEncAlg the id of the content-authenticated encryption algorithm 1473 */ 1474 public void start(AlgorithmID contentAuthEncAlg, 1475 PrivateKey signerPrivateKey, 1476 X509Certificate[] signerCertChain, 1477 PrivateKey recipient1PrivateKey, 1478 X509Certificate recipient1Cert, 1479 PrivateKey recipient2PrivateKey, 1480 X509Certificate recipient2Cert) { 1481 // the test message 1482 String m = "This is the test message."; 1483 System.out.println("Test message: \""+m+"\""); 1484 System.out.println(); 1485 byte[] message = m.getBytes(); 1486 1487 try { 1488 byte[] encodedAuthEnvelopedData; 1489 byte[] encodedSignedAuthEnvelopedData; 1490 System.out.println("Stream implementation demos"); 1491 System.out.println("==========================="); 1492 1493 AlgorithmID hashAlgorithm = AlgorithmID.sha256; 1494 AlgorithmID signatureAlgorithm = AlgorithmID.ecdsa_With_SHA256; 1495 1496 1497 // the stream implementation 1498 System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [create]:\n"); 1499 // compress content 1500 byte[] compressedData = createCompressedDataStream(message); 1501 encodedAuthEnvelopedData = createAuthEnvelopedDataStream(compressedData, 1502 (AlgorithmID)contentAuthEncAlg.clone(), 1503 recipient1Cert, 1504 recipient2Cert); 1505 encodedSignedAuthEnvelopedData = createSignedDataStream(encodedAuthEnvelopedData, 1506 SignedDataStream.IMPLICIT, 1507 (AlgorithmID)hashAlgorithm.clone(), 1508 (AlgorithmID)signatureAlgorithm.clone(), 1509 signerPrivateKey, 1510 signerCertChain); 1511 1512 // transmit data 1513 System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [parse]:\n"); 1514 // verify signature 1515 encodedAuthEnvelopedData = getSignedDataStream(encodedSignedAuthEnvelopedData, null, signerCertChain); 1516 // parse contents 1517 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 1518 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey); 1519 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 1520 parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1521 System.out.println("Decrypt for the several recipients using their certificate."); 1522 parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1523 1524 // the non-stream implementation 1525 System.out.println("\nNon-stream implementation demos"); 1526 System.out.println("==============================="); 1527 1528 1529 System.out.println("\nCMS Signed AuthEnvelopedData demo [create]:\n"); 1530 // compress content 1531 compressedData = createCompressedData(message); 1532 encodedAuthEnvelopedData = createAuthEnvelopedData(compressedData, 1533 (AlgorithmID)contentAuthEncAlg.clone(), 1534 recipient1Cert, 1535 recipient2Cert); 1536 encodedSignedAuthEnvelopedData = createSignedData(encodedAuthEnvelopedData, 1537 SignedData.IMPLICIT, 1538 (AlgorithmID)hashAlgorithm.clone(), 1539 (AlgorithmID)signatureAlgorithm.clone(), 1540 signerPrivateKey, 1541 signerCertChain); 1542 // transmit data 1543 System.out.println("\nCMS Signed AuthEnvelopedData demo [parse]:\n"); 1544 // verify signature 1545 encodedAuthEnvelopedData = getSignedData(encodedSignedAuthEnvelopedData, null, signerCertChain); 1546 // parse contents 1547 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 1548 parseAuthEnvelopedDataWithRecipientInfoIndex(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey); 1549 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 1550 parseAuthEnvelopedDataWithRecipientIdentifier(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1551 System.out.println("Decrypt for the several recipients using their certificate."); 1552 parseAuthEnvelopedDataWithRecipientCert(false, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1553 1554 1555 System.out.println("OutputStream implementation demos"); 1556 System.out.println("==========================="); 1557 1558 1559 // the stream implementation 1560 System.out.println("\nCMS Signed AuthEnvelopedDataOutputStream demo [create]:\n"); 1561 // compression of content is done in method createAuthEnvelopedDataOutputStream 1562 encodedAuthEnvelopedData = createAuthEnvelopedDataOutputStream(message, 1563 (AlgorithmID)contentAuthEncAlg.clone(), recipient1Cert, 1564 recipient2Cert); 1565 encodedSignedAuthEnvelopedData = createSignedDataStream(encodedAuthEnvelopedData, 1566 SignedDataStream.IMPLICIT, 1567 (AlgorithmID)hashAlgorithm.clone(), 1568 (AlgorithmID)signatureAlgorithm.clone(), 1569 signerPrivateKey, 1570 signerCertChain); 1571 1572 // transmit data 1573 System.out.println("\nCMS Signed AuthEnvelopedDataStream demo [parse]:\n"); 1574 // verify signature 1575 encodedAuthEnvelopedData = getSignedDataStream(encodedSignedAuthEnvelopedData, null, signerCertChain); 1576 // parse contents 1577 System.out.println("Decrypt for the several recipients using their index into the recipientInfos field."); 1578 parseAuthEnvelopedDataWithRecipientInfoIndex(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient2PrivateKey); 1579 System.out.println("Decrypt for the several recipients using their RecipientIdentifier."); 1580 parseAuthEnvelopedDataWithRecipientIdentifier(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1581 System.out.println("Decrypt for the several recipients using their certificate."); 1582 parseAuthEnvelopedDataWithRecipientCert(true, encodedAuthEnvelopedData, recipient1PrivateKey, recipient1Cert, recipient2PrivateKey, recipient2Cert); 1583 1584 1585 } catch (Exception ex) { 1586 ex.printStackTrace(); 1587 throw new RuntimeException(ex.toString()); 1588 } 1589 } 1590 1591 1592 /** 1593 * Main method. 1594 * 1595 * @throws IOException 1596 * if an I/O error occurs when reading required keys 1597 * and certificates from files 1598 */ 1599 public static void main(String argv[]) throws Exception { 1600 1601 DemoUtil.initDemos(); 1602 ECCDemoUtil.installIaikEccProvider(); 1603 1604 (new EckaEGAuthEnvelopedDataDemo()).start(); 1605 System.out.println("\nReady!"); 1606 DemoUtil.waitKey(); 1607 } 1608}