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/CAST128EnvelopedDataDemo.java 21 12.02.25 17:58 Dbratko $ 029// $Revision: 21 $ 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.cms.SecurityProvider; 045import iaik.security.random.SecRandom; 046import iaik.utils.Util; 047import iaik.x509.X509Certificate; 048 049import java.io.ByteArrayInputStream; 050import java.io.ByteArrayOutputStream; 051import java.io.IOException; 052import java.io.InputStream; 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.IvParameterSpec; 061 062import demo.DemoUtil; 063import demo.keystore.CMSKeyStore; 064 065/** 066 * This class demonstrates the EnvelopedDataStream/EncryptedContentInfoStream usages 067 * for the CAST128 algorithm. 068 * <p> 069 * This demo compares the usage of class EnvelopedDataStream for encrypting the content 070 * using CAST128 with automatical (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 * CAST parameters are defined as: 077 * <pre> 078 * Parameters ::= SEQUENCE { 079 * iv OCTET STRING DEFAULT 0, 080 * keyLength INTEGER } 081 * </pre> 082 */ 083public class CAST128EnvelopedDataDemo { 084 085 // certificate of user 1 086 X509Certificate user1; 087 // private key of user 1 088 PrivateKey user1_pk; 089 // certificate of user 2 090 X509Certificate user2; 091 // private key of user 2 092 PrivateKey user2_pk; 093 // secure random number generator 094 SecureRandom random; 095 096 /** 097 * Setup the demo certificate chains. 098 * 099 * Keys and certificate are retrieved from the demo KeyStore. 100 * 101 * @throws IOException if an file read error occurs 102 */ 103 public CAST128EnvelopedDataDemo() throws IOException { 104 105 System.out.println(); 106 System.out.println("**********************************************************************************"); 107 System.out.println("* CAST128EnvelopedDataDemo *"); 108 System.out.println("* (shows the usage of the EnvelopedData type for the CAST128 cipher) *"); 109 System.out.println("**********************************************************************************"); 110 System.out.println(); 111 // add all certificates to the list 112 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 113 user1 = certs[0]; 114 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 115 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 116 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 117 118 random = SecRandom.getDefault(); 119 } 120 121 /** 122 * Creates a CMS <code>EnvelopedDataStream</code> message. 123 * <p> 124 * 125 * @param message the message to be enveloped, as byte representation 126 * @param contentEA the content encryption algorithm 127 * @param keyLength the key length for the symmetric key 128 * @return the DER encoding of the <code>EnvelopedData</code> object just created 129 * @throws Exception if the <code>EnvelopedData</code> object cannot 130 * be created 131 */ 132 public byte[] createEnvelopedDataStream(byte[] message, AlgorithmID contentEA, int keyLength) throws Exception { 133 134 EnvelopedDataStream enveloped_data; 135 136 // we are testing the stream interface 137 ByteArrayInputStream is = new ByteArrayInputStream(message); 138 // create a new EnvelopedData object 139 try { 140 enveloped_data = new EnvelopedDataStream(is, contentEA, keyLength); 141 } catch (NoSuchAlgorithmException ex) { 142 throw new CMSException("No implementation for contentEA.getAlgorithm().getName()."); 143 } 144 145 146 // create the recipient infos 147 RecipientInfo[] recipients = new RecipientInfo[2]; 148 // user1 is the first receiver 149 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 150 // user2 is the second receiver 151 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 152 // specify the recipients of the encrypted message 153 enveloped_data.setRecipientInfos(recipients); 154 155 // return the EnvelopedDate as DER encoded byte array with block size 2048 156 ByteArrayOutputStream os = new ByteArrayOutputStream(); 157 enveloped_data.writeTo(os, 2048); 158 return os.toByteArray(); 159 } 160 161 162 163 /** 164 * Creates a CMS <code>EnvelopedDataStream</code> message. 165 * <p> 166 * Keys and parameters, and EncryptedContentInfoStream are created outside 167 * the EnvelopedDataStream class. 168 * 169 * @param message the message to be enveloped, as byte representation 170 * @param cea the content encryption algorithm 171 * @param keyLength the key length for the symmetric key 172 * @return the DER encoding of the <code>EnvelopedData</code> object just created 173 * @throws Exception if the <code>EnvelopedData</code> object cannot 174 * be created 175 */ 176 public byte[] createEncryptedContentInfoStream(byte[] message, AlgorithmID cea, int keyLength) throws Exception { 177 178 AlgorithmID contentEA = (AlgorithmID)cea.clone(); 179 ByteArrayInputStream is = new ByteArrayInputStream(message); 180 181 // generate parameters (iv and key length) 182 // create iv 183 byte[] iv = new byte[8]; 184 random.nextBytes(iv); 185 SEQUENCE parameter = new SEQUENCE(); 186 parameter.addComponent(new OCTET_STRING(iv)); 187 parameter.addComponent(new INTEGER(keyLength)); 188 contentEA.setParameter(parameter); 189 AlgorithmParameterSpec params = new IvParameterSpec(iv); 190 191 // generate the content encryption key 192 KeyGenerator keyGen = SecurityProvider.getSecurityProvider().getKeyGenerator(contentEA, keyLength); 193 // generate a new key 194 SecretKey secretKey = keyGen.generateKey(); 195 196 // create the EncryptedContentInfo for the content to be encrypted 197 EncryptedContentInfoStream eci = new EncryptedContentInfoStream(ObjectID.cms_data, is); 198 // setup the cipher for encryption 199 eci.setupCipher(contentEA, secretKey, params); 200 201 // create the recipient infos 202 RecipientInfo[] recipients = new RecipientInfo[2]; 203 // user1 is the first receiver 204 recipients[0] = new KeyTransRecipientInfo(user1, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 205 // encrypt the secret key for recipient 1 206 recipients[0].encryptKey(secretKey); 207 // user2 is the second receiver 208 recipients[1] = new KeyTransRecipientInfo(user2, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 209 // encrypt the secret key for recipient 2 210 recipients[1].encryptKey(secretKey); 211 // now create the EnvelopedDataStream 212 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(recipients, eci); 213 214 // return the EnvelopedDate as DER encoded byte array with block size 2048 215 ByteArrayOutputStream os = new ByteArrayOutputStream(); 216 enveloped_data.writeTo(os, 2048); 217 byte[] enc = os.toByteArray(); 218 return enc; 219 } 220 221 /** 222 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 223 * specified recipient and returns the decrypted (= original) message. 224 * <p> 225 * Decryption and cipher setup and EncryptedContentInfoStrean processing 226 * is performed outside class EnvelopedDataStream. 227 * 228 * @param encoding the <code>EnvelopedData</code> object as DER encoded byte array 229 * @param privateKey the private key to decrypt the message 230 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 231 * to which the specified private key belongs 232 * 233 * @return the recovered message, as byte array 234 * @throws CMSException if the message cannot be recovered 235 */ 236 public byte[] getEncryptedContentInfoStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws Exception { 237 238 // create the EnvelopedData object from a DER encoded byte array 239 // we are testing the stream interface 240 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 241 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 242 243 // get the recipient infos 244 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 245 246 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 247 248 for (int i=0; i<recipients.length; i++) { 249 System.out.println("Recipient "+(i+1)+":"); 250 System.out.println(recipients[i].getRecipientIdentifiers()[0]); 251 } 252 // decrypt symmetric content encryption key, e.g.: 253 SecretKey secretKey = recipients[recipientInfoIndex].decryptKey(user1_pk); 254 255 //get the ECI from the enveloped data: 256 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 257 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 258 //get the content encryption algorithm: 259 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 260 System.out.println("Content Encryption Algorithm: " + contentEA); 261 262 // get the parameters as SEQUENCE 263 SEQUENCE seq = (SEQUENCE)contentEA.getParameter(); 264 // the iv is the first component 265 OCTET_STRING oct = (OCTET_STRING)seq.getComponentAt(0); 266 AlgorithmParameterSpec params = new IvParameterSpec((byte[])oct.getValue()); 267 268 //now setup the cipher with previously decrypted recipient key amd params 269 eci.setupCipher(secretKey, params); 270 //get and read the data thereby actually performing the decryption 271 InputStream data_is = eci.getInputStream(); 272 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 273 Util.copyStream(data_is, baos, null); 274 byte[] decrypted = baos.toByteArray(); 275 return decrypted; 276 } 277 278 /** 279 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 280 * specified recipient and returns the decrypted (= original) message. 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[] getEnvelopedDataStream(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 307 EncryptedContentInfoStream eci = enveloped_data.getEncryptedContentInfo(); 308 System.out.println("\nContent type of encrypted data: " + eci.getContentType()); 309 //get the content encryption algorithm: 310 AlgorithmID contentEA = eci.getContentEncryptionAlgorithm(); 311 System.out.println("Content Encryption Algorithm: " + contentEA); 312 //now setup the cipher with previously decrypted recipient key amd params 313 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 314 //get and read the data thereby actually performing the decryption 315 InputStream data_is = enveloped_data.getInputStream(); 316 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 317 Util.copyStream(data_is, baos, null); 318 byte[] decrypted = baos.toByteArray(); 319 return decrypted; 320 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 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n"); 346 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128); 347 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 //received_message = getEnvelopedDataStream(data, user2_pk, 1); 353 System.out.print("\nDecrypted content: "); 354 System.out.println(new String(received_message)); 355 356 // test against EncryptedContentInfoStream - EnvelopedDataStream creation 357 358 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n"); 359 360 System.out.println("Create EncryptedContentInfo for EnvelopedData..."); 361 data = createEncryptedContentInfoStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128); 362 // transmit data 363 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 364 // user1 means index 0 (hardcoded for this demo) 365 received_message = getEnvelopedDataStream(data, user1_pk, 0); 366 System.out.print("\nDecrypted content: "); 367 System.out.println(new String(received_message)); 368 369 System.out.println("\nEnvelopedDataStream demo for algorithm CAST [create]:\n"); 370 data = createEnvelopedDataStream(message, (AlgorithmID)AlgorithmID.cast5_CBC.clone(), 128); 371 // transmit data 372 373 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 374 // user1 means index 0 (hardcoded for this demo) 375 System.out.println("Decrypt EncryptedContentInfo of EnvelopedData..."); 376 received_message = getEncryptedContentInfoStream(data, user1_pk, 0); 377 System.out.print("\nDecrypted content: "); 378 System.out.println(new String(received_message)); 379 380 381 } catch (Exception ex) { 382 ex.printStackTrace(); 383 throw new RuntimeException(ex.toString()); 384 } 385 } 386 387 /** 388 * The main method. 389 * 390 * @throws Exception 391 * if some error occurs 392 */ 393 public static void main(String argv[]) throws Exception { 394 395 demo.DemoUtil.initDemos(); 396 397 (new CAST128EnvelopedDataDemo()).start(); 398 System.out.println("\nReady!"); 399 DemoUtil.waitKey(); 400 } 401}