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