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