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 // $Header: /IAIK-CMS/current/src/demo/cms/envelopedData/EnvelopedDataOutputStreamDemo.java 20 12.02.25 17:58 Dbratko $
059 // $Revision: 20 $
060 //
061
062
063 package demo.cms.envelopedData;
064
065 import iaik.asn1.CodingException;
066 import iaik.asn1.ObjectID;
067 import iaik.asn1.structures.AlgorithmID;
068 import iaik.asn1.structures.Attribute;
069 import iaik.cms.CMSException;
070 import iaik.cms.ContentInfoOutputStream;
071 import iaik.cms.EncryptedContentInfoStream;
072 import iaik.cms.EnvelopedDataOutputStream;
073 import iaik.cms.EnvelopedDataStream;
074 import iaik.cms.KeyTransRecipientInfo;
075 import iaik.cms.RecipientInfo;
076 import iaik.cms.attributes.CMSContentType;
077 import iaik.security.random.SecRandom;
078 import iaik.utils.CryptoUtils;
079 import iaik.utils.Util;
080 import iaik.x509.X509Certificate;
081
082 import java.io.ByteArrayInputStream;
083 import java.io.ByteArrayOutputStream;
084 import java.io.IOException;
085 import java.io.InputStream;
086 import java.security.InvalidKeyException;
087 import java.security.NoSuchAlgorithmException;
088 import java.security.PrivateKey;
089 import java.security.SecureRandom;
090
091 import demo.DemoUtil;
092 import demo.keystore.CMSKeyStore;
093
094
095 /**
096 * Demonstrates the usage of class {@link iaik.cms.EnvelopedDataOutputStream} and
097 * for encrypting data using the CMS type EnvelopedData.
098 */
099 public class EnvelopedDataOutputStreamDemo {
100
101
102 // encryption certificate of user 1
103 X509Certificate user1_crypt;
104 // encryption private key of user 1
105 PrivateKey user1_crypt_pk;
106 // encryption certificate of user 2
107 X509Certificate user2_crypt;
108 // encryption private key of user 2
109 PrivateKey user2_crypt_pk;
110
111 // secure random number generator
112 SecureRandom random;
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 EnvelopedDataOutputStreamDemo() throws IOException {
122
123 System.out.println();
124 System.out.println("**********************************************************************************");
125 System.out.println("* EnvelopedDataOutputStream demo *");
126 System.out.println("* (shows the usage of the CMS EnvelopedDataOutputStream implementation) *");
127 System.out.println("**********************************************************************************");
128 System.out.println();
129
130 // encryption certs
131 user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
132 user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
133 user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
134 user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
135
136 random = SecRandom.getDefault();
137
138 }
139
140
141 /**
142 * Creates a CMS <code>EnvelopedData</code> and wraps it into a ContentInfo.
143 *
144 * @param message the message to be enveloped, as byte representation
145 * @return the encoded EnvelopedData object just created, wrapped into a ContentInfo
146 *
147 * @throws CMSException if the <code>EnvelopedData</code> object cannot
148 * be created
149 * @throws IOException if an I/O error occurs
150 */
151 public byte[] createEnvelopedDataStream(byte[] message) throws CMSException, IOException {
152
153
154 // a stream from which to read the data to be encrypted
155 ByteArrayInputStream is = new ByteArrayInputStream(message);
156
157 // the stream to which to write the EnvelopedData
158 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
159 EnvelopedDataOutputStream envelopedData;
160
161 // wrap EnvelopedData into a ContentInfo
162 ContentInfoOutputStream contentInfoStream =
163 new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
164 // create a new EnvelopedData object encrypted with AES
165 try {
166 envelopedData = new EnvelopedDataOutputStream(contentInfoStream,
167 (AlgorithmID)AlgorithmID.aes128_CBC.clone());
168 } catch (NoSuchAlgorithmException ex) {
169 throw new CMSException("No implementation for AES.");
170 }
171
172 // create the recipient infos
173 RecipientInfo[] recipients = new RecipientInfo[2];
174 // user1 is the first receiver
175 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
176 // user2 is the second receiver
177 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
178
179 // specify the recipients of the encrypted message
180 envelopedData.setRecipientInfos(recipients);
181
182 Attribute[] attributes = new Attribute[1];
183
184 try {
185 // just for testing: set some unprotected attribute
186 // content type is data
187 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
188 attributes[0] = new Attribute(contentType);
189 envelopedData.setUnprotectedAttributes(attributes);
190 } catch (Exception ex) {
191 throw new CMSException("Error creating attribute: " + ex.toString());
192 }
193
194 int blockSize = 16; // in real world we would use a block size like 2048
195 // write in the data to be encrypted
196 byte[] buffer = new byte[blockSize];
197 int bytesRead;
198 while ((bytesRead = is.read(buffer)) != -1) {
199 envelopedData.write(buffer, 0, bytesRead);
200 }
201
202 // closing the stream finishes encryption and closes the underlying stream
203 envelopedData.close();
204 return resultStream.toByteArray();
205 }
206
207 /**
208 * Decrypts the encrypted content of the given EnvelopedData object for the
209 * specified recipient and returns the decrypted (= original) message.
210 *
211 * @param encoding the encoded EnvelopedData object, wrapped in a ContentInfo
212 * @param privateKey the private key to decrypt the message
213 * @param recipientInfoIndex the index into the <code>RecipientInfo</code> array
214 * to which the specified private key belongs
215 *
216 * @return the recovered message, as byte array
217 * @throws CMSException if the message cannot be recovered
218 * @throws IOException if an I/O error occurs
219 */
220 public byte[] getEnvelopedDataStream(byte[] encoding, PrivateKey privateKey, int recipientInfoIndex) throws CMSException, IOException {
221
222 // create the EnvelopedData object from a DER encoded byte array
223 // we are testing the stream interface
224 ByteArrayInputStream is = new ByteArrayInputStream(encoding);
225
226 EnvelopedDataStream enveloped_data = new EnvelopedDataStream(is);
227
228 System.out.println("Information about the encrypted data:");
229 EncryptedContentInfoStream eci = (EncryptedContentInfoStream)enveloped_data.getEncryptedContentInfo();
230 System.out.println("Content type: "+eci.getContentType().getName());
231 System.out.println("Content encryption algorithm: "+eci.getContentEncryptionAlgorithm().getName());
232
233 System.out.println("\nThis message can be decrypted by the owners of the following certificates:");
234 RecipientInfo[] recipients = enveloped_data.getRecipientInfos();
235 for (int i=0; i<recipients.length; i++) {
236 System.out.println("Recipient "+(i+1)+":");
237 System.out.println(recipients[i].getRecipientIdentifiers()[0]);
238 }
239
240 // decrypt the message
241 try {
242 enveloped_data.setupCipher(privateKey, recipientInfoIndex);
243 InputStream decrypted = enveloped_data.getInputStream();
244 ByteArrayOutputStream os = new ByteArrayOutputStream();
245 Util.copyStream(decrypted, os, null);
246
247 // get any unprotected attributes:
248 Attribute[] attributes = enveloped_data.getUnprotectedAttributes();
249 if ((attributes != null) && (attributes.length > 0)) {
250 System.out.println("Attributes included: ");
251 // we know we have used content type
252 CMSContentType contentType = (CMSContentType)attributes[0].getAttributeValue();
253 System.out.println(contentType);
254 }
255
256 return os.toByteArray();
257
258 } catch (InvalidKeyException ex) {
259 throw new CMSException("Private key error: "+ex.toString());
260 } catch (NoSuchAlgorithmException ex) {
261 throw new CMSException("Content encryption algorithm not implemented: "+ex.getMessage());
262 } catch (CodingException ex) {
263 throw new CMSException("Cannot get unprotected attributes: "+ex.toString());
264 }
265
266 }
267
268 /**
269 * Starts the demo.
270 */
271 public void start() {
272 // the test message
273 String m = "This is the test message.";
274 System.out.println("Test message: \""+m+"\"");
275 System.out.println();
276 byte[] message = m.getBytes();
277
278 try {
279 byte[] encoding;
280 byte[] received_message = null;
281 System.out.println("EnvelopedDataOutputStream implementation demo");
282 System.out.println("=============================================");
283
284
285 //
286 // test CMS EnvelopedDataStream
287 //
288 System.out.println("\nEnvelopedData demo [create]:\n");
289 encoding = createEnvelopedDataStream(message);
290 // transmit data
291 System.out.println("\nEnvelopedData demo [parse]:\n");
292 // user1 means index 0 (hardcoded for this demo)
293 received_message = getEnvelopedDataStream(encoding, user1_crypt_pk, 0);
294 System.out.print("\nDecrypted content: ");
295 System.out.println(new String(received_message));
296
297 if (CryptoUtils.equalsBlock(received_message, message) == false) {
298 throw new Exception("Decrypted content not equal to original one!");
299 }
300
301 System.out.println("Ready!");
302
303 } catch (Exception ex) {
304 ex.printStackTrace();
305 throw new RuntimeException(ex.toString());
306 }
307 }
308
309
310 /**
311 * Main method.
312 *
313 * @throws Exception
314 * if an some error occurs
315 */
316 public static void main(String argv[]) throws Exception {
317
318 demo.DemoUtil.initDemos();
319
320 (new EnvelopedDataOutputStreamDemo()).start();
321
322 DemoUtil.waitKey();
323 }
324 }