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    }