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/CAST128EnvelopedDataDemo.java 21 12.02.25 17:58 Dbratko $ 059 // $Revision: 21 $ 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.cms.SecurityProvider; 075 import iaik.security.random.SecRandom; 076 import iaik.utils.Util; 077 import iaik.x509.X509Certificate; 078 079 import java.io.ByteArrayInputStream; 080 import java.io.ByteArrayOutputStream; 081 import java.io.IOException; 082 import java.io.InputStream; 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.IvParameterSpec; 091 092 import demo.DemoUtil; 093 import demo.keystore.CMSKeyStore; 094 095 /** 096 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages 097 * for the CAST128 algorithm. 098 * <p> 099 * This demo compares the usage of class EnvelopedDataStream for encrypting the content 100 * using CAST128 with automatical (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 * CAST parameters are defined as: 107 * <pre> 108 * Parameters ::= SEQUENCE { 109 * iv OCTET STRING DEFAULT 0, 110 * keyLength INTEGER } 111 * </pre> 112 */ 113 public class CAST128EnvelopedDataDemo { 114 115 // certificate of user 1 116 X509Certificate user1; 117 // private key of user 1 118 PrivateKey user1_pk; 119 // certificate of user 2 120 X509Certificate user2; 121 // private key of user 2 122 PrivateKey user2_pk; 123 // secure random number generator 124 SecureRandom random; 125 126 /** 127 * Setup the demo certificate chains. 128 * 129 * Keys and certificate are retrieved from the demo KeyStore. 130 * 131 * @throws IOException if an file read error occurs 132 */ 133 public CAST128EnvelopedDataDemo() throws IOException { 134 135 System.out.println(); 136 System.out.println("**********************************************************************************"); 137 System.out.println("* CAST128EnvelopedDataDemo *"); 138 System.out.println("* (shows the usage of the EnvelopedData type for the CAST128 cipher) *"); 139 System.out.println("**********************************************************************************"); 140 System.out.println(); 141 // add all certificates to the list 142 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 143 user1 = certs[0]; 144 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 145 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 146 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 147 148 random = SecRandom.getDefault(); 149 } 150 151 /** 152 * Creates a CMS <code>EnvelopedDataStream</code> message. 153 * <p> 154 * 155 * @param message the message to be enveloped, as byte representation 156 * @param contentEA the content encryption algorithm 157 * @param keyLength the key length for the symmetric key 158 * @return the DER encoding of the <code>EnvelopedData</code> object just created 159 * @throws Exception if the <code>EnvelopedData</code> object cannot 160 * be created 161 */ 162 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception { 163 164 EnvelopedDataStream enveloped_data; 165 166 // we are testing the stream interface 167 ByteArrayInputStream is = new ByteArrayInputStream(message); 168 // create a new EnvelopedData object 169 try { 170 enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength); 171 } catch (NoSuchAlgorithmException ex) { 172 throw new CMSException("No implementation for contentEA.getAlgorithm().getName()."); 173 } 174 175 176 // create the recipient infos 177 RecipientInfo[] recipients = new RecipientInfo[2]; 178 // user1 is the first receiver 179 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 180 // user2 is the second receiver 181 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 182 // specify the recipients of the encrypted message 183 enveloped_data.setRecipientInfos(recipients); 184 185 // return the EnvelopedDate as DER encoded byte array with block size 2048 186 ByteArrayOutputStream os = new ByteArrayOutputStream(); 187 enveloped_data.writeTo(os, 2048); 188 return os.toByteArray(); 189 } 190 191 192 193 /** 194 * Creates a CMS <code>EnvelopedDataStream</code> message. 195 * <p> 196 * Keys and parameters, and EncryptedContentInfoStream are created outside 197 * the EnvelopedDataStream class. 198 * 199 * @param message the message to be enveloped, as byte representation 200 * @param cea the content encryption algorithm 201 * @param keyLength the key length for the symmetric key 202 * @return the DER encoding of the <code>EnvelopedData</code> object just created 203 * @throws Exception if the <code>EnvelopedData</code> object cannot 204 * be created 205 */ 206 public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception { 207 208 AlgorithmID contentEA = (AlgorithmID)cea.clone(); 209 ByteArrayInputStream is = new ByteArrayInputStream(message); 210 211 // generate parameters (iv and key length) 212 // create iv 213 byte[] iv = new byte[8]; 214 random.nextBytes(iv); 215 SEQUENCE parameter = new SEQUENCE(); 216 parameter.addComponent(new OCTET_STRING(iv)); 217 parameter.addComponent(new INTEGER(keyLength)); 218 contentEA.setParameter(parameter); 219 AlgorithmParameterSpec params = new IvParameterSpec(iv); 220 221 // generate the content encryption key 222 KeyGenerator keyGen = SecurityProvider.getSecurityProvider().getKeyGenerator(contentEA, keyLength); 223 // generate a new key 224 SecretKey secretKey = keyGen.generateKey(); 225 226 // create the EncryptedContentInfo for the content to be encrypted 227 EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is); 228 // setup the cipher for encryption 229 eci.setupCipher(contentEA, secretKey, params); 230 231 // create the recipient infos 232 RecipientInfo[] recipients = new RecipientInfo[2]; 233 // user1 is the first receiver 234 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 235 // encrypt the secret key for recipient 1 236 recipients[0].encryptKey(secretKey); 237 // user2 is the second receiver 238 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 239 // encrypt the secret key for recipient 2 240 recipients[1].encryptKey(secretKey); 241 // now create the EnvelopedDataStream 242 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci); 243 244 // return the EnvelopedDate as DER encoded byte array with block size 2048 245 ByteArrayOutputStream os = new ByteArrayOutputStream(); 246 enveloped_data.writeTo(os, 2048); 247 byte[] enc = os.toByteArray(); 248 return enc; 249 } 250 251 /** 252 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 253 * specified recipient and returns the decrypted (= original) message. 254 * <p> 255 * Decryption and cipher setup and EncryptedContentInfoStrean processing 256 * is performed outside class EnvelopedDataStream. 257 * 258 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 259 * @param privateKey the private key to decrypt the message 260 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 261 * to which the specified private key belongs 262 * 263 * @return the recovered message, as byte array 264 * @throws CMSException if the message cannot be recovered 265 */ 266 public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 267 268 // create the EnvelopedData object from a DER encoded byte array 269 // we are testing the stream interface 270 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 271 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 272 273 // get the recipient infos 274 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 275 276 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 277 278 for (int i=0; i<recipients.length; i++) { 279 System.out.println("Recipient "+(i+1)+":"); 280 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 281 } 282 // decrypt symmetric content encryption key, e.g.: 283 SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk); 284 285 //get the ECI from the enveloped data: 286 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 287 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 288 //get the content encryption algorithm: 289 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 290 System.out.println("Content Encryption Algorithm: " + contentEA); 291 292 // get the parameters as SEQUENCE 293 SEQUENCE seq = (SEQUENCE)contentEA.getParameter(); 294 // the iv is the first component 295 OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(0); 296 AlgorithmParameterSpec params = new IvParameterSpec((byte[])oct.getValue()); 297 298 //now setup the cipher with previously decrypted recipient key amd params 299 eci.setupCipher(secretKey, params); 300 //get and read the data thereby actually performing the decryption 301 InputStream data_is = eci.getInputStream(); 302 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 303 Util.copyStream(data_is, baos, null); 304 byte[] decrypted = baos.toByteArray(); 305 return decrypted; 306 } 307 308 /** 309 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 310 * specified recipient and returns the decrypted (= original) message. 311 * 312 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 313 * @param privateKey the private key to decrypt the message 314 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 315 * to which the specified private key belongs 316 * 317 * @return the recovered message, as byte array 318 * @throws Exception if the message cannot be recovered 319 */ 320 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 321 322 // create the EnvelopedData object from a DER encoded byte array 323 // we are testing the stream interface 324 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 325 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 326 327 // get the recipient infos 328 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 329 330 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 331 332 for (int i=0; i<recipients.length; i++) { 333 System.out.println("Recipient "+(i+1)+": "); 334 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 335 } 336 337 EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo(); 338 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 339 //get the content encryption algorithm: 340 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 341 System.out.println("Content Encryption Algorithm: " + contentEA); 342 //now setup the cipher with previously decrypted recipient key amd params 343 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 344 //get and read the data thereby actually performing the decryption 345 InputStream data_is = enveloped_data.getInputStream(); 346 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 347 Util.copyStream(data_is, baos, null); 348 byte[] decrypted = baos.toByteArray(); 349 return decrypted; 350 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 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n"); 376 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128); 377 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 //received_message = getEnvelopedDataStream(data, user2_pk, 1); 383 System.out.print("\nDecrypted content: "); 384 System.out.println(new String(received_message)); 385 386 // test against EncryptedContentInfoStream - EnvelopedDataStream creation 387 388 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n"); 389 390 System.out.println("Create EncryptedContentInfo for EnvelopedData..."); 391 data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128); 392 // transmit data 393 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 394 // user1 means index 0 (hardcoded for this demo) 395 received_message = getEnvelopedDataStream(data, user1_pk, 0); 396 System.out.print("\nDecrypted content: "); 397 System.out.println(new String(received_message)); 398 399 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n"); 400 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128); 401 // transmit data 402 403 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 404 // user1 means index 0 (hardcoded for this demo) 405 System.out.println("Decrypt EncryptedContentInfo of EnvelopedData..."); 406 received_message = getEncryptedContentInfoStream(data, user1_pk, 0); 407 System.out.print("\nDecrypted content: "); 408 System.out.println(new String(received_message)); 409 410 411 } catch (Exception ex) { 412 ex.printStackTrace(); 413 throw new RuntimeException(ex.toString()); 414 } 415 } 416 417 /** 418 * The main method. 419 * 420 * @throws Exception 421 * if some error occurs 422 */ 423 public static void main(String argv[]) throws Exception { 424 425 demo.DemoUtil.initDemos(); 426 427 (new CAST128EnvelopedDataDemo()).start(); 428 System.out.println("\nReady!"); 429 DemoUtil.waitKey(); 430 } 431 }