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/PasswordRecipientInfoDemo.java 31 12.02.25 17:58 Dbratko $ 059 // $Revision: 31 $ 060 // 061 062 package demo.cms.envelopedData; 063 064 import iaik.asn1.UTF8String; 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.cms.CMSAlgorithmID; 067 import iaik.cms.CMSException; 068 import iaik.cms.ContentInfo; 069 import iaik.cms.ContentInfoStream; 070 import iaik.cms.EncryptedContentInfo; 071 import iaik.cms.EncryptedContentInfoStream; 072 import iaik.cms.EnvelopedData; 073 import iaik.cms.EnvelopedDataStream; 074 import iaik.cms.PasswordRecipientInfo; 075 import iaik.cms.RecipientInfo; 076 import iaik.security.random.SecRandom; 077 import iaik.security.spec.PBEKeyAndParameterSpec; 078 import iaik.utils.Util; 079 080 import java.io.ByteArrayInputStream; 081 import java.io.ByteArrayOutputStream; 082 import java.io.IOException; 083 import java.io.InputStream; 084 import java.security.AlgorithmParameters; 085 import java.security.NoSuchAlgorithmException; 086 import java.security.SecureRandom; 087 import java.security.spec.AlgorithmParameterSpec; 088 089 import javax.crypto.SecretKey; 090 091 import demo.DemoUtil; 092 093 /** 094 * This class shows the usage of the CMS RecipientInfo type {@link iaik.cms.PasswordRecipientInfo 095 * PasswordRecipientInfo} as specified by <a href = "http://www.ietf.org/rfc/rfc5652.txt" target="_blank"> RFC 5652</a>. 096 */ 097 public class PasswordRecipientInfoDemo { 098 099 100 // secure random number generator 101 SecureRandom random; 102 103 /** 104 * Default constructor. 105 */ 106 public PasswordRecipientInfoDemo() { 107 System.out.println(); 108 System.out.println("**********************************************************************************"); 109 System.out.println("* PasswordRecipientInfoDemo *"); 110 System.out.println("* (shows the usage of the CMS PasswordRecipientInfo type implementation) *"); 111 System.out.println("**********************************************************************************"); 112 System.out.println(); 113 114 random = SecRandom.getDefault(); 115 } 116 117 118 /** 119 * Creates a CMS <code>EnvelopedData</code> with a PasswordRecipientInfo 120 * and wraps it into a ContentInfo (stream implementation). 121 * 122 * @param message the message to be enveloped, as byte representation 123 * @param password the password from which to derive the key encryption key (kek) 124 * @param keyDerivationAlg the key derivation function to be used for deriving the kek 125 * @param keyDerivatoinParamSpec any parameters required by the key derivation function 126 * @param keyEncrAlg the ID of the key-encryption (key-wrap) algorithm to be used 127 * for encrypting the content-encryption key 128 * @param keyEncrParams any algorithm parameters to be used for intializing the 129 * key wrap cipher 130 * @return the encoded ContentInfo containing the EnvelopedData object just created 131 * 132 * @throws CMSException if the <code>EnvelopedData</code> object cannot 133 * be created 134 * @throws IOException if an I/O error occurs 135 */ 136 public byte[] createEnvelopedDataStream(byte[] message, 137 char[] password, 138 AlgorithmID keyDerivationAlg, 139 AlgorithmParameterSpec keyDerivatoinParamSpec, 140 AlgorithmID keyEncrAlg, 141 AlgorithmParameters keyEncrParams) 142 throws CMSException, IOException { 143 144 EnvelopedDataStream envelopedData; 145 146 // we are testing the stream interface 147 ByteArrayInputStream is = new ByteArrayInputStream(message); 148 // create a new EnvelopedData object encrypted with AES 149 try { 150 envelopedData = new EnvelopedDataStream(is, (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 151 } catch (NoSuchAlgorithmException ex) { 152 throw new CMSException("Cannot init EnvelopedDataStream: " + ex.toString()); 153 } 154 155 // create the PasswordRecipientInfo 156 PasswordRecipientInfo pri; 157 try { 158 pri = new PasswordRecipientInfo(password, 159 keyDerivationAlg, 160 keyDerivatoinParamSpec, 161 keyEncrAlg, 162 keyEncrParams); 163 } catch (Exception ex) { 164 throw new CMSException("Cannot create PasswordRecipientInfo: " + ex.toString()); 165 } 166 167 168 // specify the recipients of the encrypted message 169 RecipientInfo[] recipients = { pri }; 170 envelopedData.setRecipientInfos(recipients); 171 // return the EnvelopedDate as DER encoded byte array with block size 2048 172 ByteArrayOutputStream os = new ByteArrayOutputStream(); 173 envelopedData.setBlockSize(2048); 174 ContentInfoStream cis = new ContentInfoStream(envelopedData); 175 cis.writeTo(os); 176 return os.toByteArray(); 177 } 178 179 /** 180 * PBE based decrypts the encrypted content of the given EnvelopedData object 181 * and returns the decrypted (= original) message (stream implementation). 182 * 183 * @param encoding the encoded ContentInfo containing an EnvelopedData object 184 * @param password the password from which to derive the key-encryption key (kek) 185 * to be used for decrypting the content-encryption key (cek) 186 * @param cekAlgName the name of the cek (content encryption key) algorithm 187 * 188 * @return the recovered message, as byte array 189 * @throws CMSException if the message cannot be recovered 190 * @throws IOException if an I/O error occurs 191 */ 192 public byte[] getEnvelopedDataStream(byte[] encoding, char[] password, String cekAlgName) throws CMSException, IOException { 193 194 // create the EnvelopedData object from a DER encoded byte array 195 // we are testing the stream interface 196 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 197 EnvelopedDataStream envelopedData = new EnvelopedDataStream(is); 198 199 System.out.println("Information about the encrypted data:"); 200 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)envelopedData.getEncryptedContentInfo(); 201 System.out.println("Content type: "+eci.getContentType().getName()); 202 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 203 204 System.out.println("\nInformation about the RecipientInfo :"); 205 PasswordRecipientInfo recipient = (PasswordRecipientInfo)envelopedData.getRecipientInfos()[0]; 206 System.out.println(recipient); 207 208 // decrypt the message 209 try { 210 SecretKey cek = recipient.decryptKey(password, cekAlgName); 211 envelopedData.setupCipher(cek); 212 InputStream decrypted = envelopedData.getInputStream(); 213 ByteArrayOutputStream os = new ByteArrayOutputStream(); 214 Util.copyStream(decrypted, os, null); 215 216 return os.toByteArray(); 217 218 } catch (Exception ex) { 219 throw new CMSException("Cannot decrypt message. " + ex.toString()); 220 } 221 } 222 223 224 /** 225 * Creates a CMS <code>EnvelopedData</code> with a PasswordRecipientInfo 226 * and wraps it into a ContentInfo. 227 * 228 * @param message the message to be enveloped, as byte representation 229 * @param password the password from which to derive the key encryption key (kek) 230 * @param keyDerivationAlg the key derivation function to be used for deriving the kek 231 * @param keyDerivatoinParamSpec any parameters required by the key derivation function 232 * @param keyEncrAlg the ID of the key-encryption (key-wrap) algorithm to be used 233 * for encrypting the content-encryption key 234 * @param keyEncrParams any algorithm parameters to be used for intializing the 235 * key wrap cipher 236 * @return the encoded ContentInfo containing the EnvelopedData object just created 237 * 238 * @throws CMSException if the <code>EnvelopedData</code> object cannot 239 * be created 240 */ 241 public byte[] createEnvelopedData(byte[] message, 242 char[] password, 243 AlgorithmID keyDerivationAlg, 244 AlgorithmParameterSpec keyDerivatoinParamSpec, 245 AlgorithmID keyEncrAlg, 246 AlgorithmParameters keyEncrParams) 247 throws CMSException { 248 249 EnvelopedData envelopedData; 250 251 // create a new EnvelopedData object encrypted with AES 252 try { 253 envelopedData = new EnvelopedData(message, (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 254 } catch (NoSuchAlgorithmException ex) { 255 throw new CMSException(ex.toString()); 256 } 257 258 // create the PasswordRecipientInfo 259 PasswordRecipientInfo pri; 260 try { 261 pri = new PasswordRecipientInfo(password, 262 keyDerivationAlg, 263 keyDerivatoinParamSpec, 264 keyEncrAlg, 265 keyEncrParams); 266 } catch (Exception ex) { 267 throw new CMSException("Cannot create PasswordRecipientInfo: " + ex.toString()); 268 } 269 270 271 // specify the recipients of the encrypted message 272 RecipientInfo[] recipients = { pri }; 273 envelopedData.setRecipientInfos(recipients); 274 275 // wrap into contentInfo 276 ContentInfo ci = new ContentInfo(envelopedData); 277 // return the EnvelopedDate as DER encoded byte array 278 return ci.toByteArray(); 279 } 280 281 /** 282 * PBE based decrypts the encrypted content of the given EnvelopedData object 283 * and returns the decrypted (= original) message. 284 * 285 * @param encoding the encoded ContentInfo containing an EnvelopedData object 286 * @param password the password from which to derive the key-encryption key (kek) 287 * to be used for decrypting the content-encryption key (cek) 288 * @param cekAlgName the name of the cek (content encryption key) algorithm 289 * 290 * @return the recovered message, as byte array 291 * 292 * @throws CMSException if the message cannot be recovered 293 * @throws IOException if an I/O error occurs 294 */ 295 public byte[] getEnvelopedData(byte[] encoding, char[] password, String cekAlgName) throws CMSException, IOException { 296 297 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 298 299 EnvelopedData envelopedData = new EnvelopedData(is); 300 301 System.out.println("Information about the encrypted data:"); 302 EncryptedContentInfo eci = (EncryptedContentInfo)envelopedData.getEncryptedContentInfo(); 303 System.out.println("Content type: "+eci.getContentType().getName()); 304 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 305 306 System.out.println("\nInformation about the RecipientInfo :"); 307 PasswordRecipientInfo recipient = (PasswordRecipientInfo)envelopedData.getRecipientInfos()[0]; 308 System.out.println(recipient); 309 310 // decrypt the message 311 try { 312 SecretKey cek = recipient.decryptKey(password, cekAlgName); 313 envelopedData.setupCipher(cek); 314 return envelopedData.getContent(); 315 316 } catch (Exception ex) { 317 throw new CMSException("Cannot decrypt message: " + ex.toString()); 318 } 319 } 320 321 322 323 /** 324 * Starts the demo. 325 */ 326 public void start() { 327 // the test message 328 String m = "This is the test message."; 329 System.out.println("Test message: \""+m+"\""); 330 System.out.println(); 331 byte[] message = m.getBytes(); 332 333 // the password 334 char[] password = "topSecret".toCharArray(); 335 336 try { 337 byte[] encodedEnvelopedData; 338 byte[] receivedMessage = null; 339 340 int kekLen = 32; // we use AES with a 256 bit key as kek algorithm 341 int iterationCount = 10000; 342 byte[] salt = new byte[32]; 343 random.nextBytes(salt); 344 PBEKeyAndParameterSpec keyDerivationParamSpec = 345 new PBEKeyAndParameterSpec(UTF8String.getUTF8EncodingFromCharArray(password), 346 salt, 347 iterationCount, 348 kekLen); 349 350 351 System.out.println("Stream implementation demo"); 352 System.out.println("==========================="); 353 354 // the stream implementation 355 // test CMS EnvelopedDataStream 356 // 357 System.out.println("\nEnvelopedDataStream demo [create]:\n"); 358 // key derivation function 359 AlgorithmID keyDerivationAlg = (AlgorithmID)AlgorithmID.pbkdf2.clone(); 360 // key encryption algorithm 361 AlgorithmID keyEncryptionAlg = (AlgorithmID)CMSAlgorithmID.pwri_kek.clone(); 362 // for PWRI-KEK set the kek encryption algorithm parameter 363 AlgorithmID kekEncryptionAlg = (AlgorithmID)AlgorithmID.aes256_CBC.clone(); 364 keyEncryptionAlg.setParameter(kekEncryptionAlg.toASN1Object()); 365 // the name of the content encryption algorithm 366 String cekAlgName = "AES256"; 367 // we can use null as password since it is already set in keyDerivationParamSpec 368 encodedEnvelopedData = createEnvelopedDataStream(message, 369 null, 370 (AlgorithmID)keyDerivationAlg.clone(), 371 keyDerivationParamSpec, 372 keyEncryptionAlg, 373 null); 374 // transmit data 375 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 376 // user1 means index 0 (hardcoded for this demo) 377 receivedMessage = getEnvelopedDataStream(encodedEnvelopedData, password, cekAlgName); 378 System.out.print("\nDecrypted content: "); 379 System.out.println(new String(receivedMessage)); 380 381 // the non-stream implementation 382 System.out.println("\nNon-stream implementation demo"); 383 System.out.println("==============================="); 384 385 386 // 387 // test CMS EnvelopedData 388 // 389 System.out.println("\nEnvelopedData demo [create]:\n"); 390 // key derivation function 391 keyDerivationAlg = (AlgorithmID)AlgorithmID.pbkdf2.clone(); 392 // key encryption algorithm 393 keyEncryptionAlg = (AlgorithmID)CMSAlgorithmID.pwri_kek.clone(); 394 // for PWRI-KEK set the kek encryption algorithm parameter 395 kekEncryptionAlg = (AlgorithmID)AlgorithmID.aes256_CBC.clone(); 396 keyEncryptionAlg.setParameter(kekEncryptionAlg.toASN1Object()); 397 // the name of the content encryption algorithm 398 cekAlgName = "AES256"; 399 // we can use null as password since it is already set in keyDerivationParamSpec 400 encodedEnvelopedData = createEnvelopedData(message, 401 null, 402 keyDerivationAlg, 403 keyDerivationParamSpec, 404 keyEncryptionAlg, 405 null); 406 // transmit data 407 System.out.println("\nEnvelopedData demo [parse]:\n"); 408 // user1 means index 0 (hardcoded for this demo) 409 receivedMessage = getEnvelopedData(encodedEnvelopedData, password, cekAlgName); 410 System.out.print("\nDecrypted content: "); 411 System.out.println(new String(receivedMessage)); 412 413 414 415 System.out.println("Ready!"); 416 417 418 419 } catch (Exception ex) { 420 ex.printStackTrace(); 421 throw new RuntimeException(ex.toString()); 422 } 423 } 424 425 426 /** 427 * Main method. 428 * 429 * @throws Exception 430 * if some error occurs 431 */ 432 public static void main(String argv[]) throws Exception { 433 434 DemoUtil.initDemos(); 435 436 (new PasswordRecipientInfoDemo()).start(); 437 438 DemoUtil.waitKey(); 439 } 440 }