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/RC2EnvelopedDataDemo.java 22 12.02.25 17:58 Dbratko $ 059 // $Revision: 22 $ 060 // 061 062 package demo.cms.envelopedData; 063 064 import iaik.asn1.INTEGER; 065 import iaik.asn1.OCTET_STRING; 066 import iaik.asn1.ObjectID; 067 import iaik.asn1.SEQUENCE; 068 import iaik.asn1.structures.AlgorithmID; 069 import iaik.cms.CMSException; 070 import iaik.cms.EncryptedContentInfoStream; 071 import iaik.cms.EnvelopedDataStream; 072 import iaik.cms.KeyTransRecipientInfo; 073 import iaik.cms.RecipientInfo; 074 import iaik.security.random.SecRandom; 075 import iaik.utils.Util; 076 import iaik.x509.X509Certificate; 077 078 import java.io.ByteArrayInputStream; 079 import java.io.ByteArrayOutputStream; 080 import java.io.IOException; 081 import java.io.InputStream; 082 import java.math.BigInteger; 083 import java.security.NoSuchAlgorithmException; 084 import java.security.PrivateKey; 085 import java.security.SecureRandom; 086 import java.security.spec.AlgorithmParameterSpec; 087 088 import javax.crypto.KeyGenerator; 089 import javax.crypto.SecretKey; 090 import javax.crypto.spec.RC2ParameterSpec; 091 092 import demo.DemoUtil; 093 import demo.keystore.CMSKeyStore; 094 095 /** 096 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages 097 * for the RC2 algorithm. 098 * <p> 099 * This demo compares the usage of class EnvelopedDataStream for encrypting the content 100 * using RC2 with automatic (transparent) key/parameter handling against explicit 101 * key/parameter/EncrypedContentInfoStream handling. 102 * <p> 103 * All keys and certificates are read from a keystore created by the 104 * SetupCMSKeyStore program. 105 * <p> 106 * RC2 parameters are defined as: 107 * <ul> 108 * <li>RC2_CBC: Variable-key-size block cipher; parameters as used by S/MIME: 109 * rc2ParamterVersion and IV; encoded as SEQUENCE: 110 * <pre> 111 * RC2-CBC parameter ::= SEQUENCE { 112 * rc2ParameterVersion INTEGER, 113 * iv OCTET STRING (8)} 114 * 115 * For the effective-key-bits of 40, 64, and 128, the 116 * rc2ParameterVersion values are 160, 120, 58 respectively. 117 * </pre> 118 * </ul> 119 * 120 * Note that the usage of RC2 is deprecated but used here for this demo since it 121 * requires a specific parameter handling. 122 * 123 */ 124 public class RC2EnvelopedDataDemo { 125 126 // certificate of user 1 127 X509Certificate user1; 128 // private key of user 1 129 PrivateKey user1_pk; 130 // certificate of user 2 131 X509Certificate user2; 132 // private key of user 2 133 PrivateKey user2_pk; 134 // secure random number generator 135 SecureRandom random; 136 137 /** 138 * Setup the demo certificate chains. 139 * 140 * Keys and certificate are retrieved from the demo KeyStore. 141 * 142 * @throws IOException if an file read error occurs 143 */ 144 public RC2EnvelopedDataDemo() throws IOException { 145 146 System.out.println(); 147 System.out.println("****************************************************************************************"); 148 System.out.println("* RC2EnvelopedDataDemo *"); 149 System.out.println("* (shows the usage of the CMS EnvelopedData type implementation for the RC2 algorithm) *"); 150 System.out.println("****************************************************************************************"); 151 System.out.println(); 152 153 // add all certificates to the list 154 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 155 user1 = certs[0]; 156 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 157 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 158 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 159 160 random = SecRandom.getDefault(); 161 } 162 163 /** 164 * Creates a CMS <code>EnvelopedDataStream</code> message. 165 * <p> 166 * 167 * @param message the message to be enveloped, as byte representation 168 * @param contentEA the content encryption algorithm 169 * @param keyLength the key length for the symmetric key 170 * @return the DER encoding of the <code>EnvelopedData</code> object just created 171 * @throws CMSException if the <code>EnvelopedData</code> object cannot 172 * be created 173 */ 174 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception { 175 176 EnvelopedDataStream enveloped_data; 177 178 // we are testing the stream interface 179 ByteArrayInputStream is = new ByteArrayInputStream(message); 180 // create a new EnvelopedData object 181 try { 182 enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength); 183 } catch (NoSuchAlgorithmException ex) { 184 throw new CMSException("No implementation for contentEA.getAlgorithm().getName()."); 185 } 186 187 // create the recipient infos 188 RecipientInfo[] recipients = new RecipientInfo[2]; 189 // user1 is the first receiver 190 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 191 // user2 is the second receiver 192 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 193 // specify the recipients of the encrypted message 194 enveloped_data.setRecipientInfos(recipients); 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 return os.toByteArray(); 200 } 201 202 203 204 /** 205 * Creates a CMS <code>EnvelopedDataStream</code> message. 206 * <p> 207 * Keys and parameters, and EncryptedContentInfoStream are created outside 208 * the EnvelopedDataStream class. 209 * 210 * @param message the message to be enveloped, as byte representation 211 * @param contentEA the content encryption algorithm 212 * @param keyLength the key length for the symmetric key 213 * @return the DER encoding of the <code>EnvelopedData</code> object just created 214 * @throws Exception if the <code>EnvelopedData</code> object cannot 215 * be created 216 */ 217 public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception { 218 219 ByteArrayInputStream is = new ByteArrayInputStream(message); 220 221 // parameters: iv and rc2 parameter version 222 // create iv 223 byte[] iv = new byte[8]; 224 random.nextBytes(iv); 225 int rc2_param = 58; 226 switch (keyLength) { 227 case 40: 228 rc2_param = 160; 229 break; 230 case 64: 231 rc2_param = 120; 232 break; 233 default: // 128 234 rc2_param = 58; 235 keyLength = 128; 236 } 237 // create the paramters (SEQUENCE) to be sent 238 SEQUENCE parameter = new SEQUENCE(); 239 parameter.addComponent(new INTEGER(rc2_param)); 240 parameter.addComponent(new OCTET_STRING(iv)); 241 contentEA.setParameter(parameter); 242 AlgorithmParameterSpec params = new RC2ParameterSpec(keyLength,iv); 243 244 // generate a new content encryption key 245 KeyGenerator key_gen = KeyGenerator.getInstance("RC2"); 246 key_gen.init(keyLength); 247 SecretKey secretKey = key_gen.generateKey(); 248 249 // create the EncryptedContentInfo for the content to be encrypted 250 EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is); 251 // setup the cipher for encryption 252 eci.setupCipher(contentEA, secretKey, params); 253 254 // create the recipient infos 255 RecipientInfo[] recipients = new RecipientInfo[2]; 256 // user1 is the first receiver 257 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 258 // encrypt the secret key for recipient 1 259 recipients[0].encryptKey(secretKey); 260 // user2 is the second receiver 261 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 262 // encrypt the secret key for recipient 2 263 recipients[1].encryptKey(secretKey); 264 // now create the EnvelopedDataStream 265 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci); 266 267 // return the EnvelopedDate as DER encoded byte array with block size 2048 268 ByteArrayOutputStream os = new ByteArrayOutputStream(); 269 enveloped_data.writeTo(os, 2048); 270 byte[] enc = os.toByteArray(); 271 return enc; 272 273 } 274 275 /** 276 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 277 * specified recipient and returns the decrypted (= original) message. 278 * <p> 279 * Decryption and cipher setup and EncryptedContentInfoStrean processing 280 * is performed outside class EnvelopedDataStream. 281 * 282 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 283 * @param privateKey the private key to decrypt the message 284 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 285 * to which the specified private key belongs 286 * 287 * @return the recovered message, as byte array 288 * @throws Exception if the message cannot be recovered 289 */ 290 public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 291 292 // create the EnvelopedData object from a DER encoded byte array 293 // we are testing the stream interface 294 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 295 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 296 297 // get the recipient infos 298 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 299 300 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 301 302 for (int i=0; i<recipients.length; i++) { 303 System.out.println("Recipient "+(i+1)+":"); 304 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 305 } 306 // decrypt symmetric content encryption key, e.g.: 307 SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk); 308 309 //get the ECI from the enveloped data: 310 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 311 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 312 //get the content encryption algorithm: 313 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 314 System.out.println("Content Encryption Algorithm: " + contentEA); 315 // get the parameters as SEQUENCE 316 SEQUENCE seq = (SEQUENCE)contentEA.getParameter(); 317 // create an IvParameterSpec: 318 int rc2ParameterVersion = ((BigInteger)seq.getComponentAt(0).getValue()).intValue(); 319 int effective_key_bits = 32; 320 switch (rc2ParameterVersion) { 321 case 160: 322 effective_key_bits = 40; 323 break; 324 case 120: 325 effective_key_bits = 64; 326 break; 327 case 58: 328 effective_key_bits = 128; 329 break; 330 default: 331 throw new Exception("Invalid rc2ParameterVersion " + rc2ParameterVersion + "!"); 332 } 333 AlgorithmParameterSpec params = new RC2ParameterSpec(effective_key_bits,(byte[])seq.getComponentAt(1).getValue()); 334 335 //now setup the cipher with previously decrypted recipient key amd params 336 eci.setupCipher(secretKey, params); 337 //get and read the data thereby actually performing the decryption 338 InputStream data_is = eci.getInputStream(); 339 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 340 Util.copyStream(data_is, baos, null); 341 byte[] decrypted = baos.toByteArray(); 342 return decrypted; 343 } 344 345 /** 346 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 347 * specified recipient and returns the decrypted (= original) message. 348 * 349 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 350 * @param privateKey the private key to decrypt the message 351 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 352 * to which the specified private key belongs 353 * 354 * @return the recovered message, as byte array 355 * @throws Exception if the message cannot be recovered 356 */ 357 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 358 359 // create the EnvelopedData object from a DER encoded byte array 360 // we are testing the stream interface 361 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 362 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 363 364 // get the recipient infos 365 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 366 367 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 368 369 for (int i=0; i<recipients.length; i++) { 370 System.out.println("Recipient "+(i+1)+": "); 371 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 372 } 373 374 EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo(); 375 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 376 //get the content encryption algorithm: 377 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 378 System.out.println("Content Encryption Algorithm: " + contentEA); 379 //now setup the cipher with previously decrypted recipient key amd params 380 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 381 //get and read the data thereby actually performing the decryption 382 InputStream data_is = enveloped_data.getInputStream(); 383 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 384 Util.copyStream(data_is, baos, null); 385 byte[] decrypted = baos.toByteArray(); 386 return decrypted; 387 388 } 389 390 391 392 /** 393 * Starts the test. 394 */ 395 public void start() { 396 // the test message 397 String m = "This is the test message."; 398 System.out.println("Test message: "+m); 399 System.out.println(); 400 byte[] message = m.getBytes(); 401 402 try { 403 byte[] data; 404 byte[] received_message = null; 405 406 407 // the stream implementation 408 // 409 // test CMS EnvelopedDataStream 410 // 411 412 413 // RC2 414 System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n"); 415 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128); 416 417 // transmit data 418 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 419 // user1 means index 0 (hardcoded for this demo) 420 received_message = getEnvelopedDataStream(data, user1_pk, 0); 421 //received_message = getEnvelopedDataStream(data, user2_pk, 1); 422 System.out.print("\nDecrypted content: "); 423 System.out.println(new String(received_message)); 424 425 // test against EncryptedContentInfoStream - EnvelopedDataStream creation 426 427 System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n"); 428 429 System.out.println("Create EncryptedContentInfo for EnvelopedData..."); 430 data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128); 431 // transmit data 432 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 433 // user1 means index 0 (hardcoded for this demo) 434 received_message = getEnvelopedDataStream(data, user1_pk, 0); 435 System.out.print("\nDecrypted content: "); 436 System.out.println(new String(received_message)); 437 438 System.out.println("\nEnvelopedDataStream demo for algorithm RC2 [create]:\n"); 439 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.rc2_CBC.clone(), 128); 440 // transmit data 441 442 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 443 // user1 means index 0 (hardcoded for this demo) 444 System.out.println("Decrypt EncryptedContentInfo of EnvelopedData..."); 445 received_message = getEncryptedContentInfoStream(data, user1_pk, 0); 446 System.out.print("\nDecrypted content: "); 447 System.out.println(new String(received_message)); 448 449 450 } catch (Exception ex) { 451 ex.printStackTrace(); 452 throw new RuntimeException(ex.toString()); 453 } 454 } 455 456 /** 457 * The main method. 458 * 459 * @throws Exception 460 * if some error occurs 461 */ 462 public static void main(String argv[]) throws Exception { 463 464 demo.DemoUtil.initDemos(); 465 466 (new RC2EnvelopedDataDemo()).start(); 467 System.out.println("\nReady!"); 468 DemoUtil.waitKey(); 469 } 470 }