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