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