001 // Copyright (C) 2002 IAIK 002 // https://jce.iaik.tugraz.at 003 // 004 // Copyright (C) 2003 - 2025 Stiftung Secure Information and 005 // Communication Technologies SIC 006 // https://sic.tech 007 // 008 // All rights reserved. 009 // 010 // Redistribution and use in source and binary forms, with or without 011 // modification, are permitted provided that the following conditions 012 // are met: 013 // 1. Redistributions of source code must retain the above copyright 014 // notice, this list of conditions and the following disclaimer. 015 // 2. Redistributions in binary form must reproduce the above copyright 016 // notice, this list of conditions and the following disclaimer in the 017 // documentation and/or other materials provided with the distribution. 018 // 019 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 020 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 021 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 023 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 024 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 025 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 026 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 027 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 028 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 029 // SUCH DAMAGE. 030 031 // Copyright (C) 2002 IAIK 032 // https://sic.tech/ 033 // 034 // Copyright (C) 2003 - 2025 Stiftung Secure Information and 035 // Communication Technologies SIC 036 // https://sic.tech/ 037 // 038 // All rights reserved. 039 // 040 // This source is provided for inspection purposes and recompilation only, 041 // unless specified differently in a contract with IAIK. This source has to 042 // be kept in strict confidence and must not be disclosed to any third party 043 // under any circumstances. Redistribution in source and binary forms, with 044 // or without modification, are <not> permitted in any case! 045 // 046 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 047 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 048 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 049 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 050 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 051 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 052 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 053 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 054 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 055 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 056 // SUCH DAMAGE. 057 // 058 // $Header: /IAIK-CMS/current/src/demo/cms/envelopedData/ArcFourEnvelopedDataDemo.java 20 12.02.25 17:58 Dbratko $ 059 // $Revision: 20 $ 060 // 061 062 package demo.cms.envelopedData; 063 064 import iaik.asn1.ObjectID; 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.cms.CMSException; 067 import iaik.cms.EncryptedContentInfoStream; 068 import iaik.cms.EnvelopedDataStream; 069 import iaik.cms.KeyTransRecipientInfo; 070 import iaik.cms.RecipientInfo; 071 import iaik.security.random.SecRandom; 072 import iaik.utils.Util; 073 import iaik.x509.X509Certificate; 074 075 import java.io.ByteArrayInputStream; 076 import java.io.ByteArrayOutputStream; 077 import java.io.IOException; 078 import java.io.InputStream; 079 import java.security.NoSuchAlgorithmException; 080 import java.security.PrivateKey; 081 import java.security.SecureRandom; 082 import java.security.spec.AlgorithmParameterSpec; 083 084 import javax.crypto.KeyGenerator; 085 import javax.crypto.SecretKey; 086 087 import demo.keystore.CMSKeyStore; 088 089 /** 090 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages 091 * for the ARCFOUR algorithm. The ARCFOUR stream cipher is believed to be compatible with 092 * RC4[TM], a proprietary cipher of RSA Security Inc.. 093 * <p> 094 * This demo compares the usage of class EnvelopedDataStream for encrypting the content 095 * using ARCFOUR with automatical (transparent) key/parameter handling against explicit 096 * key/parameter/EncrypedContentInfoStream handling. 097 * <p> 098 * All keys and certificates are read from a keystore created by the 099 * SetupCMSKeyStore program. 100 * <p> 101 * ARCFOUR requires no parameters. 102 */ 103 public class ArcFourEnvelopedDataDemo { 104 105 // certificate of user 1 106 X509Certificate user1; 107 // private key of user 1 108 PrivateKey user1_pk; 109 // certificate of user 2 110 X509Certificate user2; 111 // private key of user 2 112 PrivateKey user2_pk; 113 // secure random number generator 114 SecureRandom random; 115 116 /** 117 * Setup the demo certificate chains. 118 * 119 * Keys and certificate are retrieved from the demo KeyStore. 120 * 121 * @throws IOException if an file read error occurs 122 */ 123 public ArcFourEnvelopedDataDemo() throws IOException { 124 125 System.out.println(); 126 System.out.println("********************************************************************************************"); 127 System.out.println("* ArcFourEnvelopedDataDemo *"); 128 System.out.println("* (shows the usage of the CMS EnvelopedData type implementation for the ARCFOUR algorithm) *"); 129 System.out.println("********************************************************************************************"); 130 System.out.println(); 131 132 // add all certificates to the list 133 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 134 user1 = certs[0]; 135 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 136 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 137 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 138 139 random = SecRandom.getDefault(); 140 } 141 142 /** 143 * Creates a CMS <code>EnvelopedDataStream</code> message. 144 * <p> 145 * 146 * @param message the message to be enveloped, as byte representation 147 * @param contentEA the content encryption algorithm 148 * @param keyLength the key length for the symmetric key 149 * @return the DER encoding of the <code>EnvelopedData</code> object just created 150 * @throws CMSException if the <code>EnvelopedData</code> object cannot 151 * be created 152 */ 153 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception { 154 155 EnvelopedDataStream enveloped_data; 156 157 // we are testing the stream interface 158 ByteArrayInputStream is = new ByteArrayInputStream(message); 159 // create a new EnvelopedData object 160 try { 161 enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength); 162 } catch (NoSuchAlgorithmException ex) { 163 throw new CMSException("No implementation for contentEA.getAlgorithm().getName()."); 164 } 165 166 167 // create the recipient infos 168 RecipientInfo[] recipients = new RecipientInfo[2]; 169 // user1 is the first receiver 170 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 171 // user2 is the second receiver 172 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 173 // specify the recipients of the encrypted message 174 enveloped_data.setRecipientInfos(recipients); 175 176 // return the EnvelopedDate as DER encoded byte array with block size 2048 177 ByteArrayOutputStream os = new ByteArrayOutputStream(); 178 enveloped_data.writeTo(os, 2048); 179 return os.toByteArray(); 180 } 181 182 183 184 /** 185 * Creates a CMS <code>EnvelopedDataStream</code> message. 186 * <p> 187 * Keys and parameters, and EncryptedContentInfoStream are created outside 188 * the EnvelopedDataStream class. 189 * 190 * @param message the message to be enveloped, as byte representation 191 * @param cea the content encryption algorithm 192 * @param keyLength the key length for the symmetric key 193 * @return the DER encoding of the <code>EnvelopedData</code> object just created 194 * @throws Exception if the <code>EnvelopedData</code> object cannot 195 * be created 196 */ 197 public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception { 198 199 AlgorithmID contentEA = (AlgorithmID)cea.clone(); 200 ByteArrayInputStream is = new ByteArrayInputStream(message); 201 202 // generate the content encryption key 203 KeyGenerator key_gen = KeyGenerator.getInstance("ARCFOUR"); 204 key_gen.init(keyLength); 205 // generate a new key 206 SecretKey secretKey = key_gen.generateKey(); 207 208 // create the EncryptedContentInfo for the content to be encrypted 209 EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is); 210 // setup the cipher for encryption 211 eci.setupCipher(contentEA, secretKey, (AlgorithmParameterSpec)null); 212 213 // create the recipient infos 214 RecipientInfo[] recipients = new RecipientInfo[2]; 215 // user1 is the first receiver 216 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 217 // encrypt the secret key for recipient 1 218 recipients[0].encryptKey(secretKey); 219 // user2 is the second receiver 220 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 221 // encrypt the secret key for recipient 2 222 recipients[1].encryptKey(secretKey); 223 // now create the EnvelopedDataStream 224 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci); 225 226 // return the EnvelopedDate as DER encoded byte array with block size 2048 227 ByteArrayOutputStream os = new ByteArrayOutputStream(); 228 enveloped_data.writeTo(os, 2048); 229 byte[] enc = os.toByteArray(); 230 return enc; 231 232 } 233 234 /** 235 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 236 * specified recipient and returns the decrypted (= original) message. 237 * <p> 238 * Decryption and cipher setup and EncryptedContentInfoStrean processing 239 * is performed outside class EnvelopedDataStream. 240 * 241 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 242 * @param privateKey the private key to decrypt the message 243 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 244 * to which the specified private key belongs 245 * 246 * @return the recovered message, as byte array 247 * @throws Exception if the message cannot be recovered 248 */ 249 public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 250 251 // create the EnvelopedData object from a DER encoded byte array 252 // we are testing the stream interface 253 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 254 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 255 256 AlgorithmParameterSpec params = null; 257 // get the recipient infos 258 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 259 260 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 261 262 for (int i=0; i<recipients.length; i++) { 263 System.out.println("Recipient "+(i+1)+":"); 264 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 265 } 266 // decrypt symmetric content encryption key, e.g.: 267 SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk); 268 269 //get the ECI from the enveloped data: 270 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 271 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 272 //get the content encryption algorithm: 273 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 274 System.out.println("Content Encryption Algorithm: " + contentEA); 275 //now setup the cipher with previously decrypted recipient key amd params 276 eci.setupCipher(secretKey); 277 //get and read the data thereby actually performing the decryption 278 InputStream data_is = eci.getInputStream(); 279 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 280 Util.copyStream(data_is, baos, null); 281 byte[] decrypted = baos.toByteArray(); 282 return decrypted; 283 } 284 285 /** 286 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 287 * specified recipient and returns the decrypted (= original) message. 288 * 289 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 290 * @param privateKey the private key to decrypt the message 291 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 292 * to which the specified private key belongs 293 * 294 * @return the recovered message, as byte array 295 * @throws Exception if the message cannot be recovered 296 */ 297 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 298 299 // create the EnvelopedData object from a DER encoded byte array 300 // we are testing the stream interface 301 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 302 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 303 304 // get the recipient infos 305 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 306 307 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 308 309 for (int i=0; i<recipients.length; i++) { 310 System.out.println("Recipient "+(i+1)+": "); 311 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 312 } 313 314 EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo(); 315 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 316 //get the content encryption algorithm: 317 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 318 System.out.println("Content Encryption Algorithm: " + contentEA); 319 //now setup the cipher with previously decrypted recipient key amd params 320 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 321 //get and read the data thereby actually performing the decryption 322 InputStream data_is = enveloped_data.getInputStream(); 323 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 324 Util.copyStream(data_is, baos, null); 325 byte[] decrypted = baos.toByteArray(); 326 return decrypted; 327 328 } 329 330 331 332 /** 333 * Starts the test. 334 */ 335 public void start() { 336 // the test message 337 String m = "This is the test message."; 338 System.out.println("Test message: "+m); 339 System.out.println(); 340 byte[] message = m.getBytes(); 341 342 try { 343 byte[] data; 344 byte[] received_message = null; 345 346 347 // the stream implementation 348 // 349 // test CMS EnvelopedDataStream 350 // 351 352 System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n"); 353 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128); 354 355 // transmit data 356 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 357 // user1 means index 0 (hardcoded for this demo) 358 received_message = getEnvelopedDataStream(data, user1_pk, 0); 359 //received_message = getEnvelopedDataStream(data, user2_pk, 1); 360 System.out.print("\nDecrypted content: "); 361 System.out.println(new String(received_message)); 362 363 // test against EncryptedContentInfoStream - EnvelopedDataStream creation 364 365 System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n"); 366 367 System.out.println("Create EncryptedContentInfo for EnvelopedData..."); 368 data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128); 369 // transmit data 370 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 371 // user1 means index 0 (hardcoded for this demo) 372 received_message = getEnvelopedDataStream(data, user1_pk, 0); 373 System.out.print("\nDecrypted content: "); 374 System.out.println(new String(received_message)); 375 376 System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n"); 377 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128); 378 // transmit data 379 380 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 381 // user1 means index 0 (hardcoded for this demo) 382 System.out.println("Decrypt EncryptedContentInfo of EnvelopedData..."); 383 received_message = getEncryptedContentInfoStream(data, user1_pk, 0); 384 System.out.print("\nDecrypted content: "); 385 System.out.println(new String(received_message)); 386 387 388 } catch (Exception ex) { 389 ex.printStackTrace(); 390 throw new RuntimeException(ex.toString()); 391 } 392 } 393 394 /** 395 * The main method. 396 * 397 * @throws Exception 398 * if some error occurs 399 */ 400 public static void main(String argv[]) throws Exception { 401 402 demo.DemoUtil.initDemos(); 403 404 (new ArcFourEnvelopedDataDemo()).start(); 405 System.out.println("\nReady!"); 406 System.in.read(); 407 } 408 }