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