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/envelopedData/ArcFourEnvelopedDataDemo.java 20 12.02.25 17:58 Dbratko $ 029// $Revision: 20 $ 030// 031 032package demo.cms.envelopedData; 033 034import iaik.asn1.ObjectID; 035import iaik.asn1.structures.AlgorithmID; 036import iaik.cms.CMSException; 037import iaik.cms.EncryptedContentInfoStream; 038import iaik.cms.EnvelopedDataStream; 039import iaik.cms.KeyTransRecipientInfo; 040import iaik.cms.RecipientInfo; 041import iaik.security.random.SecRandom; 042import iaik.utils.Util; 043import iaik.x509.X509Certificate; 044 045import java.io.ByteArrayInputStream; 046import java.io.ByteArrayOutputStream; 047import java.io.IOException; 048import java.io.InputStream; 049import java.security.NoSuchAlgorithmException; 050import java.security.PrivateKey; 051import java.security.SecureRandom; 052import java.security.spec.AlgorithmParameterSpec; 053 054import javax.crypto.KeyGenerator; 055import javax.crypto.SecretKey; 056 057import demo.keystore.CMSKeyStore; 058 059/** 060 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages 061 * for the ARCFOUR algorithm. The ARCFOUR stream cipher is believed to be compatible with 062 * RC4[TM], a proprietary cipher of RSA Security Inc.. 063 * <p> 064 * This demo compares the usage of class EnvelopedDataStream for encrypting the content 065 * using ARCFOUR with automatical (transparent) key/parameter handling against explicit 066 * key/parameter/EncrypedContentInfoStream handling. 067 * <p> 068 * All keys and certificates are read from a keystore created by the 069 * SetupCMSKeyStore program. 070 * <p> 071 * ARCFOUR requires no parameters. 072 */ 073public class ArcFourEnvelopedDataDemo { 074 075 // certificate of user 1 076 X509Certificate user1; 077 // private key of user 1 078 PrivateKey user1_pk; 079 // certificate of user 2 080 X509Certificate user2; 081 // private key of user 2 082 PrivateKey user2_pk; 083 // secure random number generator 084 SecureRandom random; 085 086 /** 087 * Setup the demo certificate chains. 088 * 089 * Keys and certificate are retrieved from the demo KeyStore. 090 * 091 * @throws IOException if an file read error occurs 092 */ 093 public ArcFourEnvelopedDataDemo() throws IOException { 094 095 System.out.println(); 096 System.out.println("********************************************************************************************"); 097 System.out.println("* ArcFourEnvelopedDataDemo *"); 098 System.out.println("* (shows the usage of the CMS EnvelopedData type implementation for the ARCFOUR algorithm) *"); 099 System.out.println("********************************************************************************************"); 100 System.out.println(); 101 102 // add all certificates to the list 103 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 104 user1 = certs[0]; 105 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 106 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 107 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 108 109 random = SecRandom.getDefault(); 110 } 111 112 /** 113 * Creates a CMS <code>EnvelopedDataStream</code> message. 114 * <p> 115 * 116 * @param message the message to be enveloped, as byte representation 117 * @param contentEA the content encryption algorithm 118 * @param keyLength the key length for the symmetric key 119 * @return the DER encoding of the <code>EnvelopedData</code> object just created 120 * @throws CMSException if the <code>EnvelopedData</code> object cannot 121 * be created 122 */ 123 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception { 124 125 EnvelopedDataStream enveloped_data; 126 127 // we are testing the stream interface 128 ByteArrayInputStream is = new ByteArrayInputStream(message); 129 // create a new EnvelopedData object 130 try { 131 enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength); 132 } catch (NoSuchAlgorithmException ex) { 133 throw new CMSException("No implementation for contentEA.getAlgorithm().getName()."); 134 } 135 136 137 // create the recipient infos 138 RecipientInfo[] recipients = new RecipientInfo[2]; 139 // user1 is the first receiver 140 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 141 // user2 is the second receiver 142 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 143 // specify the recipients of the encrypted message 144 enveloped_data.setRecipientInfos(recipients); 145 146 // return the EnvelopedDate as DER encoded byte array with block size 2048 147 ByteArrayOutputStream os = new ByteArrayOutputStream(); 148 enveloped_data.writeTo(os, 2048); 149 return os.toByteArray(); 150 } 151 152 153 154 /** 155 * Creates a CMS <code>EnvelopedDataStream</code> message. 156 * <p> 157 * Keys and parameters, and EncryptedContentInfoStream are created outside 158 * the EnvelopedDataStream class. 159 * 160 * @param message the message to be enveloped, as byte representation 161 * @param cea the content encryption algorithm 162 * @param keyLength the key length for the symmetric key 163 * @return the DER encoding of the <code>EnvelopedData</code> object just created 164 * @throws Exception if the <code>EnvelopedData</code> object cannot 165 * be created 166 */ 167 public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception { 168 169 AlgorithmID contentEA = (AlgorithmID)cea.clone(); 170 ByteArrayInputStream is = new ByteArrayInputStream(message); 171 172 // generate the content encryption key 173 KeyGenerator key_gen = KeyGenerator.getInstance("ARCFOUR"); 174 key_gen.init(keyLength); 175 // generate a new key 176 SecretKey secretKey = key_gen.generateKey(); 177 178 // create the EncryptedContentInfo for the content to be encrypted 179 EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is); 180 // setup the cipher for encryption 181 eci.setupCipher(contentEA, secretKey, (AlgorithmParameterSpec)null); 182 183 // create the recipient infos 184 RecipientInfo[] recipients = new RecipientInfo[2]; 185 // user1 is the first receiver 186 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 187 // encrypt the secret key for recipient 1 188 recipients[0].encryptKey(secretKey); 189 // user2 is the second receiver 190 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 191 // encrypt the secret key for recipient 2 192 recipients[1].encryptKey(secretKey); 193 // now create the EnvelopedDataStream 194 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci); 195 196 // return the EnvelopedDate as DER encoded byte array with block size 2048 197 ByteArrayOutputStream os = new ByteArrayOutputStream(); 198 enveloped_data.writeTo(os, 2048); 199 byte[] enc = os.toByteArray(); 200 return enc; 201 202 } 203 204 /** 205 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 206 * specified recipient and returns the decrypted (= original) message. 207 * <p> 208 * Decryption and cipher setup and EncryptedContentInfoStrean processing 209 * is performed outside class EnvelopedDataStream. 210 * 211 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 212 * @param privateKey the private key to decrypt the message 213 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 214 * to which the specified private key belongs 215 * 216 * @return the recovered message, as byte array 217 * @throws Exception if the message cannot be recovered 218 */ 219 public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 220 221 // create the EnvelopedData object from a DER encoded byte array 222 // we are testing the stream interface 223 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 224 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 225 226 AlgorithmParameterSpec params = null; 227 // get the recipient infos 228 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 229 230 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 231 232 for (int i=0; i<recipients.length; i++) { 233 System.out.println("Recipient "+(i+1)+":"); 234 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 235 } 236 // decrypt symmetric content encryption key, e.g.: 237 SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk); 238 239 //get the ECI from the enveloped data: 240 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 241 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 242 //get the content encryption algorithm: 243 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 244 System.out.println("Content Encryption Algorithm: " + contentEA); 245 //now setup the cipher with previously decrypted recipient key amd params 246 eci.setupCipher(secretKey); 247 //get and read the data thereby actually performing the decryption 248 InputStream data_is = eci.getInputStream(); 249 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 250 Util.copyStream(data_is, baos, null); 251 byte[] decrypted = baos.toByteArray(); 252 return decrypted; 253 } 254 255 /** 256 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 257 * specified recipient and returns the decrypted (= original) message. 258 * 259 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 260 * @param privateKey the private key to decrypt the message 261 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 262 * to which the specified private key belongs 263 * 264 * @return the recovered message, as byte array 265 * @throws Exception if the message cannot be recovered 266 */ 267 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 268 269 // create the EnvelopedData object from a DER encoded byte array 270 // we are testing the stream interface 271 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 272 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 273 274 // get the recipient infos 275 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 276 277 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 278 279 for (int i=0; i<recipients.length; i++) { 280 System.out.println("Recipient "+(i+1)+": "); 281 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 282 } 283 284 EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo(); 285 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 286 //get the content encryption algorithm: 287 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 288 System.out.println("Content Encryption Algorithm: " + contentEA); 289 //now setup the cipher with previously decrypted recipient key amd params 290 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 291 //get and read the data thereby actually performing the decryption 292 InputStream data_is = enveloped_data.getInputStream(); 293 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 294 Util.copyStream(data_is, baos, null); 295 byte[] decrypted = baos.toByteArray(); 296 return decrypted; 297 298 } 299 300 301 302 /** 303 * Starts the test. 304 */ 305 public void start() { 306 // the test message 307 String m = "This is the test message."; 308 System.out.println("Test message: "+m); 309 System.out.println(); 310 byte[] message = m.getBytes(); 311 312 try { 313 byte[] data; 314 byte[] received_message = null; 315 316 317 // the stream implementation 318 // 319 // test CMS EnvelopedDataStream 320 // 321 322 System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n"); 323 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128); 324 325 // transmit data 326 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 327 // user1 means index 0 (hardcoded for this demo) 328 received_message = getEnvelopedDataStream(data, user1_pk, 0); 329 //received_message = getEnvelopedDataStream(data, user2_pk, 1); 330 System.out.print("\nDecrypted content: "); 331 System.out.println(new String(received_message)); 332 333 // test against EncryptedContentInfoStream - EnvelopedDataStream creation 334 335 System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n"); 336 337 System.out.println("Create EncryptedContentInfo for EnvelopedData..."); 338 data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128); 339 // transmit data 340 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 341 // user1 means index 0 (hardcoded for this demo) 342 received_message = getEnvelopedDataStream(data, user1_pk, 0); 343 System.out.print("\nDecrypted content: "); 344 System.out.println(new String(received_message)); 345 346 System.out.println("\nEnvelopedDataStream demo for algorithm ARCFOUR [create]:\n"); 347 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.arcfour.clone(), 128); 348 // transmit data 349 350 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 351 // user1 means index 0 (hardcoded for this demo) 352 System.out.println("Decrypt EncryptedContentInfo of EnvelopedData..."); 353 received_message = getEncryptedContentInfoStream(data, user1_pk, 0); 354 System.out.print("\nDecrypted content: "); 355 System.out.println(new String(received_message)); 356 357 358 } catch (Exception ex) { 359 ex.printStackTrace(); 360 throw new RuntimeException(ex.toString()); 361 } 362 } 363 364 /** 365 * The main method. 366 * 367 * @throws Exception 368 * if some error occurs 369 */ 370 public static void main(String argv[]) throws Exception { 371 372 demo.DemoUtil.initDemos(); 373 374 (new ArcFourEnvelopedDataDemo()).start(); 375 System.out.println("\nReady!"); 376 System.in.read(); 377 } 378}