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 }