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 059 package demo.cms.envelopedData; 060 061 import java.io.ByteArrayInputStream; 062 import java.io.ByteArrayOutputStream; 063 import java.io.IOException; 064 import java.io.InputStream; 065 import java.security.AlgorithmParameters; 066 import java.security.InvalidAlgorithmParameterException; 067 import java.security.InvalidKeyException; 068 import java.security.MessageDigest; 069 import java.security.NoSuchAlgorithmException; 070 import java.security.PrivateKey; 071 import java.security.spec.InvalidParameterSpecException; 072 073 import demo.DemoUtil; 074 import demo.keystore.CMSKeyStore; 075 import iaik.asn1.OCTET_STRING; 076 import iaik.asn1.structures.AlgorithmID; 077 import iaik.cms.CMSException; 078 import iaik.cms.ContentInfo; 079 import iaik.cms.ContentInfoStream; 080 import iaik.cms.EncryptedContentInfo; 081 import iaik.cms.EncryptedContentInfoStream; 082 import iaik.cms.EnvelopedData; 083 import iaik.cms.EnvelopedDataStream; 084 import iaik.cms.KeyTransRecipientInfo; 085 import iaik.cms.RecipientInfo; 086 import iaik.cms.SecurityProvider; 087 import iaik.cms.Utils; 088 import iaik.pkcs.pkcs1.MGF1ParameterSpec; 089 import iaik.pkcs.pkcs1.MaskGenerationAlgorithm; 090 import iaik.pkcs.pkcs1.RSAOaepParameterSpec; 091 import iaik.utils.Util; 092 import iaik.x509.X509Certificate; 093 094 095 /** 096 * This class demonstrates the CMS EnvelopedData implementation for 097 * the RSA-OAEP (PKCS#1v2.1) algorithm. 098 * <p> 099 * All keys and certificates are read from a keystore created by the 100 * SetupKeyStore program. 101 * @version File Revision <!-- $$Revision: --> 21 <!-- $ --> 102 */ 103 public class OaepEnvelopedDataDemo { 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 114 /** 115 * Setup the demo certificate chains. 116 * 117 * Keys and certificate are retrieved from the demo KeyStore. 118 * 119 * @throws IOException if an file read error occurs 120 */ 121 public OaepEnvelopedDataDemo() throws IOException { 122 123 System.out.println(); 124 System.out.println("**********************************************************************************"); 125 System.out.println("* OaepEnvelopedDataDemo *"); 126 System.out.println("* (shows the usage of the CMS EnvelopedData type with the RSA OAEP method) *"); 127 System.out.println("**********************************************************************************"); 128 System.out.println(); 129 130 // add all certificates to the list 131 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 132 user1 = certs[0]; 133 user1_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 134 user2 = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 135 user2_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 136 } 137 138 139 /** 140 * Creates a CMS <code>EnvelopedDataStream</code> message. 141 * 142 * @param message the message to be enveloped, as byte representation 143 * @return the BER encoded ContentInfo containing the EnvelopedData object just created 144 * 145 * @throws CMSException if the <code>EnvelopedData</code> object cannot 146 * be created 147 * @throws IOException if an I/O error occurs 148 */ 149 public byte[] createEnvelopedDataStream(byte[] message) throws CMSException, IOException { 150 151 EnvelopedDataStream enveloped_data; 152 153 // we are testing the stream interface 154 ByteArrayInputStream is = new ByteArrayInputStream(message); 155 // create a new EnvelopedData object encrypted with AES 156 try { 157 enveloped_data = new EnvelopedDataStream(is, (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 158 } catch (NoSuchAlgorithmException ex) { 159 throw new CMSException(ex.toString()); 160 } 161 162 try { 163 // create the recipient infos 164 RecipientInfo[] recipients = new RecipientInfo[2]; 165 AlgorithmID hashID = AlgorithmID.sha256; 166 AlgorithmID oaepID = Utils.createOaepAlgorithmID(hashID); 167 // user1 is the first receiver 168 recipients[0] = new KeyTransRecipientInfo(user1, oaepID); 169 // user2 is the second receiver (OAEP with user defined parameters) 170 oaepID = Utils.createOaepAlgorithmID(hashID); 171 recipients[1] = new KeyTransRecipientInfo(user2, oaepID); 172 // specify the recipients of the encrypted message 173 enveloped_data.setRecipientInfos(recipients); 174 } catch (Exception e) { 175 throw new CMSException("Error adding recipients: " + e.toString()); 176 } 177 178 // return the EnvelopedDate as DER encoded byte array with block size 2048 179 ByteArrayOutputStream os = new ByteArrayOutputStream(); 180 enveloped_data.setBlockSize(2048); 181 ContentInfoStream cis = new ContentInfoStream(enveloped_data); 182 cis.writeTo(os); 183 return os.toByteArray(); 184 } 185 186 /** 187 * Decrypts the encrypted content of the given EnvelopedData object for the 188 * specified recipient and returns the decrypted (= original) message. 189 * 190 * @param encoding the BER encoded ContentInfo containing an EnvelopedData object 191 * @param privateKey the private key to decrypt the message 192 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 193 * to which the specified private key belongs 194 * 195 * @return the recovered message, as byte array 196 * @throws CMSException if the message cannot be recovered 197 * @throws IOException if an I/O error occurs 198 */ 199 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) 200 throws CMSException, IOException { 201 202 // create the EnvelopedData object from a DER encoded byte array 203 // we are testing the stream interface 204 ByteArrayInputStream is = new ByteArrayInputStream(encoding); 205 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is); 206 207 System.out.println("Information about the encrypted data:"); 208 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo(); 209 System.out.println("Content type: "+eci.getContentType().getName()); 210 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 211 212 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 213 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 214 for (int i=0; i<recipients.length; i++) { 215 System.out.println("Recipient: "+(i+1)); 216 System.out.print(recipients[i].getRecipientIdentifiers()[0]); 217 } 218 219 // decrypt the message 220 try { 221 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 222 InputStream decrypted = enveloped_data.getInputStream(); 223 ByteArrayOutputStream os = new ByteArrayOutputStream(); 224 Util.copyStream(decrypted, os, null); 225 226 return os.toByteArray(); 227 228 } catch (InvalidKeyException ex) { 229 System.out.println("Private key error: "+ex.getMessage()); 230 return null; 231 } catch (NoSuchAlgorithmException ex) { 232 System.out.println("Content encryption algorithm not implemented: "+ex.getMessage()); 233 return null; 234 } 235 } 236 237 238 /** 239 * Creates a CMS <code>EnvelopedData</code> message. 240 * 241 * @param message the message to be enveloped, as byte representation 242 * @return a BER encoded ContentInfo holding the EnvelopedData object just created 243 * @throws CMSException if the <code>EnvelopedData</code> object cannot 244 * be created 245 * @throws IOException if an I/O error occurs 246 */ 247 public byte[] createEnvelopedData(byte[] message) throws CMSException, IOException { 248 249 EnvelopedData enveloped_data; 250 251 // create a new EnvelopedData object encrypted with AES 252 try { 253 enveloped_data = new EnvelopedData(message, (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 254 } catch (NoSuchAlgorithmException ex) { 255 throw new CMSException(ex.toString()); 256 } 257 258 try { 259 // create the recipient infos 260 RecipientInfo[] recipients = new RecipientInfo[2]; 261 AlgorithmID hashID = AlgorithmID.sha256; 262 AlgorithmID oaepID = Utils.createOaepAlgorithmID(hashID); 263 // user1 is the first receiver 264 recipients[0] = new KeyTransRecipientInfo(user1, oaepID); 265 // user2 is the second receiver (OAEP with user defined parameters) 266 oaepID = Utils.createOaepAlgorithmID(hashID); 267 recipients[1] = new KeyTransRecipientInfo(user2, oaepID); 268 // specify the recipients of the encrypted message 269 enveloped_data.setRecipientInfos(recipients); 270 } catch (Exception e) { 271 throw new CMSException("Error adding recipients: " + e.toString()); 272 } 273 274 ContentInfo ci = new ContentInfo(enveloped_data); 275 // return the EnvelopedData as DER encoded byte array 276 return ci.toByteArray(); 277 } 278 279 /** 280 * Decrypts the encrypted content of the given <code>EnvelopedData</code> object for the 281 * specified recipient and returns the decrypted (= original) message. 282 * 283 * @param encoding the ContentInfo encoding holding an EnvelopedData 284 * @param privateKey the private key to decrypt the message 285 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array 286 * to which the specified private key belongs 287 * 288 * @return the recovered message, as byte array 289 * @throws CMSException if the message cannot be recovered 290 * @throws IOException if an I/O error occurs 291 */ 292 public byte[] getEnvelopedData(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) 293 throws CMSException, IOException { 294 295 EnvelopedData enveloped_data = new EnvelopedData(new ByteArrayInputStream(encoding)); 296 297 System.out.println("Information about the encrypted data:"); 298 EncryptedContentInfo eci = (EncryptedContentInfo)enveloped_data.getEncryptedContentInfo(); 299 System.out.println("Content type: "+eci.getContentType().getName()); 300 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName()); 301 302 System.out.println("\nThis message can be decrypted by the owners of the following certificates:"); 303 RecipientInfo[] recipients = enveloped_data.getRecipientInfos(); 304 for (int i=0; i<recipients.length; i++) { 305 System.out.println("Recipient: "+(i+1)); 306 System.out.print(recipients[i].getRecipientIdentifiers()[0]); 307 } 308 309 // decrypt the message 310 try { 311 enveloped_data.setupCipher(privateKey, recipientInfoIndex); 312 return enveloped_data.getContent(); 313 314 } catch (InvalidKeyException ex) { 315 System.out.println("Private key error: "+ex.getMessage()); 316 return null; 317 } catch (NoSuchAlgorithmException ex) { 318 System.out.println("Content encryption algorithm not implemented: "+ex.getMessage()); 319 return null; 320 } 321 } 322 323 324 /** 325 * Shows thw CMS EnvelopedData implementation for 326 * the RSA-OAEP (PKCS#1v2.1) algorithm. 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[] encodedEnvelopedData; 337 byte[] received_message = null; 338 System.out.println("Stream implementation demos (OAEP)"); 339 System.out.println("=================================="); 340 341 342 343 // the stream implementation 344 // 345 // test CMS EnvelopedDataStream 346 // 347 System.out.println("\nEnvelopedDataStream demo [create]:\n"); 348 encodedEnvelopedData = createEnvelopedDataStream(message); 349 // transmit data 350 System.out.println("\nEnvelopedDataStream demo [parse]:\n"); 351 // user1 means index 0 (hardcoded for this demo) 352 System.out.println("\nDecrypt for recipient 1:\n"); 353 received_message = getEnvelopedDataStream(encodedEnvelopedData, user1_pk, 0); 354 System.out.print("\nDecrypted content: "); 355 System.out.println(new String(received_message)); 356 357 // user2 means index 1 (hardcoded for this demo) 358 System.out.println("\nDecrypt for recipient 1:\n"); 359 received_message = getEnvelopedDataStream(encodedEnvelopedData, user2_pk, 1); 360 System.out.print("\nDecrypted content: "); 361 System.out.println(new String(received_message)); 362 363 364 // the non-stream implementation 365 System.out.println("\nNon-stream implementation demos (OAEP)"); 366 System.out.println("========================================"); 367 368 // 369 // test CMS EnvelopedData 370 // 371 System.out.println("\nEnvelopedData demo [create]:\n"); 372 encodedEnvelopedData = createEnvelopedData(message); 373 // transmit data 374 System.out.println("\nEnvelopedData demo [parse]:\n"); 375 System.out.println("\nDecrypt for recipient 1:\n"); 376 // user1 means index 0 (hardcoded for this demo) 377 received_message = getEnvelopedData(encodedEnvelopedData, user1_pk, 0); 378 System.out.print("\nDecrypted content: "); 379 System.out.println(new String(received_message)); 380 381 System.out.println("\nDecrypt for recipient 2:\n"); 382 // user2 means index 1 (hardcoded for this demo) 383 received_message = getEnvelopedData(encodedEnvelopedData, user2_pk, 1); 384 System.out.print("\nDecrypted content: "); 385 System.out.println(new String(received_message)); 386 387 System.out.println("Ready!"); 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 IOException 400 * if an I/O error occurs when reading required keys 401 * and certificates from the keystore file 402 */ 403 public static void main(String argv[]) throws Exception { 404 405 DemoUtil.initDemos(); 406 (new OaepEnvelopedDataDemo()).start(); 407 408 DemoUtil.waitKey(); 409 } 410 }