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/signedAndEnvelopedData/SignedAndEnvelopedDataDemo.java 26 12.02.25 17:58 Dbratko $
059 // $Revision: 26 $
060 //
061
062 package demo.cms.signedAndEnvelopedData;
063
064 import iaik.asn1.ObjectID;
065 import iaik.asn1.structures.AlgorithmID;
066 import iaik.asn1.structures.Attribute;
067 import iaik.cms.EnvelopedData;
068 import iaik.cms.EnvelopedDataStream;
069 import iaik.cms.IssuerAndSerialNumber;
070 import iaik.cms.KeyTransRecipientInfo;
071 import iaik.cms.RecipientInfo;
072 import iaik.cms.SignedData;
073 import iaik.cms.SignedDataStream;
074 import iaik.cms.SignerInfo;
075 import iaik.cms.attributes.CMSContentType;
076 import iaik.cms.attributes.SigningTime;
077 import iaik.utils.Util;
078 import iaik.x509.X509Certificate;
079
080 import java.io.BufferedInputStream;
081 import java.io.BufferedOutputStream;
082 import java.io.ByteArrayInputStream;
083 import java.io.ByteArrayOutputStream;
084 import java.io.IOException;
085 import java.io.InputStream;
086 import java.io.OutputStream;
087 import java.io.PipedInputStream;
088 import java.io.PipedOutputStream;
089 import java.security.PrivateKey;
090
091 import demo.DemoUtil;
092 import demo.keystore.CMSKeyStore;
093
094 /**
095 * This class shows the sequential combination of the SignedData and EnvelopedData
096 * implementations.
097 * <p>
098 * All keys and certificates are read from a keystore created by the
099 * SetupCMSKeyStore program.
100 */
101 public class SignedAndEnvelopedDataDemo {
102
103 // signing certificate of user 1
104 X509Certificate user1_sign;
105 // signing private key of user 1
106 PrivateKey user1_sign_pk;
107 // encryption certificate of user 1
108 X509Certificate user1_crypt;
109 // encryption private key of user 1
110 PrivateKey user1_crypt_pk;
111 // encryption certificate of user 2
112 X509Certificate user2_crypt;
113 // encryption private key of user 2
114 PrivateKey user2_crypt_pk;
115 // a certificate chain containing the user certs + CA
116 X509Certificate[] certificates;
117
118 /**
119 * Setup the demo certificate chains.
120 *
121 * Keys and certificate are retrieved from the demo KeyStore.
122 *
123 * @throws IOException if an file read error occurs
124 */
125 public SignedAndEnvelopedDataDemo() throws IOException {
126
127 System.out.println();
128 System.out.println("*************************************************************************************************");
129 System.out.println("* SignedAndEnvelopedDataDemo *");
130 System.out.println("* (shows the usage of the combined SignedData(Stream) and EnvelopedData(Stream) implementation) *");
131 System.out.println("*************************************************************************************************");
132 System.out.println();
133
134 // add all certificates to the list
135 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
136 user1_sign = certs[0];
137 user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
138 user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
139 user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
140 user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
141 user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
142 // user1 also includes her/his encryption certificate
143 certificates = new X509Certificate[certs.length+1];
144 System.arraycopy(certs, 0, certificates, 0, certs.length);
145 certificates[certs.length] = user1_crypt;
146
147 }
148
149 /**
150 * Uses the stream based SignedData and EnvelopedData implementations
151 * (<code>SignedDataStream</code>, <code>EnvelopedDataStream</code> to
152 * sign and envelope the given message, encode the CMS object, decodes it
153 * again, removes the envlope and verifies the signature.
154 *
155 * @param message the message to be signed and enveloped
156 * @throws Exception if an error occurs
157 */
158 public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception {
159 // repository for the signed and enveloped message
160 byte[] signed_enveloped_message;
161 // the InputStream containing the data to sign and encrypt
162 InputStream is = new BufferedInputStream(new ByteArrayInputStream(message));
163 // the OutputStream where the data shall be written to
164 ByteArrayOutputStream out = new ByteArrayOutputStream();
165 OutputStream os = new BufferedOutputStream(out);
166
167
168 // create an implicit signed message (signature contains message)
169 SignedDataStream signed = new SignedDataStream(is, SignedDataStream.IMPLICIT);
170
171 // these certificates are sent within the signature
172 signed.setCertificates(certificates);
173
174 // add one signer
175 // cert at index 0 is the user certificate
176 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
177
178 // create a new SignerInfo
179 SignerInfo signer_info = new SignerInfo(issuer, AlgorithmID.sha256, user1_sign_pk);
180
181 // create some signed attributes
182 // the message digest attribute is automatically added
183 Attribute[] attributes = new Attribute[2];
184 // content type is data
185 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
186 attributes[0] = new Attribute(contentType);
187 // signing time is now
188 SigningTime signingTime = new SigningTime();
189 attributes[1] = new Attribute(signingTime);
190
191 // set the attributes
192 signer_info.setSignedAttributes(attributes);
193 // finish the creation of SignerInfo by calling method addSigner
194 signed.addSignerInfo(signer_info);
195
196 // we have to sign and encrypt => connect 2 streams
197 PipedOutputStream piped_out = new PipedOutputStream();
198 PipedInputStream piped_in = new PipedInputStream(piped_out);
199 // a new Thread between the 2 streams
200 Writer writer = new Writer(signed, piped_out);
201 writer.start();
202
203 // encrypt with AES
204 EnvelopedDataStream enveloped = new EnvelopedDataStream(piped_in, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
205 // add recipients where the symmetric key is encrypted with RSA
206 // create the recipient infos
207 RecipientInfo[] recipients = new RecipientInfo[2];
208 // user1 is the first receiver
209 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
210 // user2 is the second receiver
211 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
212
213 // specify the recipients of the encrypted message
214 enveloped.setRecipientInfos(recipients);
215
216 // encrypt and write the data to the output stream
217 enveloped.writeTo(os,2048);
218 // finished
219 os.close();
220 is.close();
221 // get the signed and encrypted message from the ByteArrayOutputStream
222 signed_enveloped_message = out.toByteArray();
223 System.out.println("Message created, now doing the parsing...");
224 // and now decrypt the data and verify the signature
225 is = new BufferedInputStream(new ByteArrayInputStream( signed_enveloped_message));
226 enveloped = new EnvelopedDataStream(is);
227 // use this private key to decrypt the symmetric key of recipient 0
228 enveloped.setupCipher(user1_crypt_pk, 0);
229 // get the InputStream with the decrypted data
230 InputStream data_dec = enveloped.getInputStream();
231 System.out.println("Message decrypted!");
232 // read the signed data from the decrypted InputStream
233 signed = new SignedDataStream(data_dec);
234 // get the InputStream with the signed, plain data
235 InputStream data = signed.getInputStream();
236
237 // reset our output stream
238 out.reset();
239 // write the decrypted and verified data to the output stream
240 os = new BufferedOutputStream(out);
241 // copy the data
242 Util.copyStream(data, os, null);
243 os.close();
244 out.close();
245 is.close();
246 data_dec.close();
247
248 // now verify the signature of the one and only signer and print the certificate of the signer
249 X509Certificate cert = signed.verify(0);
250 System.out.println("Signature OK from: "+cert.getSubjectDN());
251
252
253 System.out.println("Received message: \"" + new String(out.toByteArray())+"\"");
254 }
255
256 /**
257 * Uses the non-stream based SignedData and EnvelopedData implementations
258 * (<code>SignedData</code>, <code>EnvelopedData</code> to
259 * sign and envelope the given message, encode the CMS object, decodes it
260 * again, removes the envlope and verifies the signature.
261 *
262 * @param message the message to be signed and enveloped
263 * @throws Exception if an error occurs
264 */
265 public void testSignedAndEnvelopedData(byte[] message) throws Exception {
266
267 // create an implicit signed message (signature contains message)
268 SignedData signed = new SignedData(message, SignedData.IMPLICIT);
269
270 // these certificates are sent within the signature
271 signed.setCertificates(certificates);
272
273 // add one signer
274 // cert at index 0 is the user certificate
275 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
276
277 // create a new SignerInfo
278 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
279
280 // create some signed attributes
281 // the message digest attribute is automatically added
282 Attribute[] attributes = new Attribute[2];
283 // content type is data
284 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
285 attributes[0] = new Attribute(contentType);
286 // signing time is now
287 SigningTime signingTime = new SigningTime();
288 attributes[1] = new Attribute(signingTime);
289
290 // set the attributes
291 signer_info.setSignedAttributes(attributes);
292 // finish the creation of SignerInfo by calling method addSigner
293 signed.addSignerInfo(signer_info);
294
295 // encode SignedData to a byte array
296 byte[] signed_message = signed.getEncoded();
297
298 // encrypt with AES
299 EnvelopedData enveloped = new EnvelopedData(signed_message, (AlgorithmID)AlgorithmID.aes256_CBC.clone());
300 // add recipients where the symmetric key is encrypted with RSA
301 // create the recipient infos
302 RecipientInfo[] recipients = new RecipientInfo[2];
303 // user1 is the first receiver
304 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
305 // user2 is the second receiver
306 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
307
308 // specify the recipients of the encrypted message
309 enveloped.setRecipientInfos(recipients);
310
311 // encrypt and write the data to a byte array
312 byte[] signed_enveloped_message = enveloped.getEncoded();
313
314 System.out.println("Message created, now doing the parsing...");
315 // and now decrypt the data and verify the signature
316 InputStream is = new BufferedInputStream(new ByteArrayInputStream(signed_enveloped_message));
317 enveloped = new EnvelopedData(is);
318 // use this private key to decrypt the symmetric key of recipient 0
319 enveloped.setupCipher(user1_crypt_pk, 0);
320 // get the InputStream with the decrypted data
321 InputStream data_dec = enveloped.getInputStream();
322 System.out.println("Message decrypted!");
323 // read the signed data from the decrypted InputStream
324 signed = new SignedData(data_dec);
325 // get the content with the signed, plain data
326 byte[] data = signed.getContent();
327
328 // now verify the signature of the one and only signer and print the certificate of the signer
329 X509Certificate cert = signed.verify(0);
330 System.out.println("Signature OK from: "+cert.getSubjectDN());
331
332
333 System.out.println("Received message: \"" + new String(data)+"\"");
334 }
335
336
337
338
339 /**
340 * Starts the test.
341 */
342 public void start() {
343 // the test message
344 String m = "This demo message will be signed and/or encrypted.";
345 System.out.println("Test message: \""+m+"\"");
346 System.out.println();
347 byte[] message = m.getBytes();
348
349 try {
350 System.out.println("Signed and enveloped data - stream based demo.");
351 testSignedAndEnvelopedDataStream(message);
352 System.out.println();
353
354 System.out.println("Signed and enveloped data - non stream based demo.");
355 testSignedAndEnvelopedData(message);
356 System.out.println();
357
358 System.out.println("Ready!");
359 } catch (Exception ex) {
360 ex.printStackTrace();
361
362 throw new RuntimeException(ex.toString());
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 files
372 */
373 public static void main(String argv[]) throws Exception {
374
375 demo.DemoUtil.initDemos();
376
377 (new SignedAndEnvelopedDataDemo()).start();
378 DemoUtil.waitKey();
379 }
380
381 /**
382 * Inner class for copying data between the 2 streams.
383 */
384 static class Writer extends Thread {
385
386 SignedDataStream signed;
387 OutputStream os;
388 Exception exception;
389
390 public Writer(SignedDataStream signed, OutputStream os) {
391 super("Writer");
392 this.signed = signed;
393 this.os = os;
394 }
395
396 /**
397 * Writes the SMimeSinged to the OutputStream.
398 */
399 public void run() {
400 try {
401 signed.writeTo(os,2048);
402 os.close();
403 } catch (Exception ex) {
404 exception = ex;
405 System.out.println("Writer exception: "+exception);
406 }
407 }
408
409 /**
410 * Returns a possible exception.
411 */
412 public Exception getException() {
413 return exception;
414 }
415 }
416
417 }