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/SignedAndEnvelopedDataOutputStreamDemo.java 13 12.02.25 17:58 Dbratko $
059 // $Revision: 13 $
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.CMSException;
068 import iaik.cms.ContentInfoOutputStream;
069 import iaik.cms.EnvelopedDataOutputStream;
070 import iaik.cms.EnvelopedDataStream;
071 import iaik.cms.IssuerAndSerialNumber;
072 import iaik.cms.KeyTransRecipientInfo;
073 import iaik.cms.RecipientInfo;
074 import iaik.cms.SignedDataOutputStream;
075 import iaik.cms.SignedDataStream;
076 import iaik.cms.SignerInfo;
077 import iaik.cms.attributes.CMSContentType;
078 import iaik.cms.attributes.SigningTime;
079 import iaik.security.random.SecRandom;
080 import iaik.utils.Util;
081 import iaik.x509.X509Certificate;
082
083 import java.io.ByteArrayInputStream;
084 import java.io.ByteArrayOutputStream;
085 import java.io.IOException;
086 import java.io.InputStream;
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 * This class shows the sequential combination of the SignedDataOutputStream
096 * and EnvelopedDataOutputStream implementations.
097 * <p>
098 * All keys and certificates are read from a keystore created by the
099 * SetupCMSKeyStore program.
100 */
101 public class SignedAndEnvelopedDataOutputStreamDemo {
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 // secure random number generator
118 SecureRandom random;
119
120 /**
121 * Setup the demo certificate chains.
122 *
123 * Keys and certificate are retrieved from the demo KeyStore.
124 *
125 * @throws IOException if an file read error occurs
126 */
127 public SignedAndEnvelopedDataOutputStreamDemo() throws IOException {
128
129 System.out.println();
130 System.out.println("*********************************************************************************************************");
131 System.out.println("* SignedAndEnvelopedDataOutputStream demo *");
132 System.out.println("* (shows the usage of the combined SignedDataOutputStream and EnvelopedDataOutputStream implementation) *");
133 System.out.println("*********************************************************************************************************");
134 System.out.println();
135
136 // add all certificates to the list
137 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
138 user1_sign = certs[0];
139 user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
140 user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
141 user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
142 user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
143 user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
144 // user1 also includes her/his encryption certificate
145 certificates = new X509Certificate[certs.length+1];
146 System.arraycopy(certs, 0, certificates, 0, certs.length);
147 certificates[certs.length] = user1_crypt;
148
149 random = SecRandom.getDefault();
150 }
151
152 /**
153 * Signs and envelopes the given message, encodes the CMS object, decodes it
154 * again, removes the envlope and verifies the signature.
155 *
156 * @param message the message to be signed and enveloped
157 * @throws Exception if an error occurs
158 */
159 public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception {
160
161 // a stream from which to read the data to be signed and encrypted
162 ByteArrayInputStream is = new ByteArrayInputStream(message);
163
164 // the stream to which to write the Signed and EnvelopedData
165 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
166
167 EnvelopedDataOutputStream envelopedData;
168
169 // wrap into a ContentInfo
170 ContentInfoOutputStream envContentInfoStream =
171 new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
172 // create a new EnvelopedData object encrypted with AES
173 try {
174 envelopedData = new EnvelopedDataOutputStream(envContentInfoStream,
175 (AlgorithmID)AlgorithmID.aes256_CBC.clone());
176 } catch (NoSuchAlgorithmException ex) {
177 throw new CMSException(ex.toString());
178 }
179
180 // create the recipient infos
181 RecipientInfo[] recipients = new RecipientInfo[2];
182 // user1 is the first receiver
183 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
184 // user2 is the second receiver
185 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
186
187 // specify the recipients of the encrypted message
188 envelopedData.setRecipientInfos(recipients);
189
190 // wrap SignedData into a ContentInfo
191 ContentInfoOutputStream signedContentInfoStream =
192 new ContentInfoOutputStream(ObjectID.cms_signedData, envelopedData);
193
194 SignedDataOutputStream signedData = new SignedDataOutputStream(signedContentInfoStream,
195 SignedDataOutputStream.IMPLICIT);
196 // these certificates are sent within the signature
197 signedData.setCertificates(certificates);
198
199 // add one signer
200 // cert at index 0 is the user certificate
201 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
202
203 // create a new SignerInfo
204 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
205
206 // create some signed attributes
207 // the message digest attribute is automatically added
208 Attribute[] attributes = new Attribute[2];
209 // content type is data
210 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
211 attributes[0] = new Attribute(contentType);
212 // signing time is now
213 SigningTime signingTime = new SigningTime();
214 attributes[1] = new Attribute(signingTime);
215
216 // set the attributes
217 signer_info.setSignedAttributes(attributes);
218 // finish the creation of SignerInfo by calling method addSigner
219 signedData.addSignerInfo(signer_info);
220
221 int blockSize = 4; // in real world we would use a block size like 2048
222 // write in the data to be signed
223 byte[] buffer = new byte[blockSize];
224 int bytesRead;
225 while ((bytesRead = is.read(buffer)) != -1) {
226 signedData.write(buffer, 0, bytesRead);
227 }
228
229 // closing the stream finished the cryptographic processing and closes the underlying stream
230 signedData.close();
231
232 // get the signed and encrypted message from the ByteArrayOutputStream
233 byte[] signed_enveloped_message = resultStream.toByteArray();
234 System.out.println("Message created, now doing the parsing...");
235 // and now decrypt the data and verify the signature
236 is = new ByteArrayInputStream(signed_enveloped_message);
237 EnvelopedDataStream enveloped = new EnvelopedDataStream(is);
238 // use this private key to decrypt the symmetric key of recipient 0
239 enveloped.setupCipher(user1_crypt_pk, 0);
240 // get the InputStream with the decrypted data
241 InputStream data_dec = enveloped.getInputStream();
242 System.out.println("Message decrypted!");
243 // read the signed data from the decrypted InputStream
244 SignedDataStream signed = new SignedDataStream(data_dec);
245 // get the InputStream with the signed, plain data
246 InputStream data = signed.getInputStream();
247 // reset our output stream
248 ByteArrayOutputStream os = new ByteArrayOutputStream();
249 // copy the data
250 Util.copyStream(data, os, null);
251 os.close();
252 is.close();
253 data_dec.close();
254
255 // now verify the signature of the one and only signer and print the certificate of the signer
256 X509Certificate cert = signed.verify(0);
257 System.out.println("Signature OK from: "+cert.getSubjectDN());
258
259 System.out.println("Received message: \"" + new String(os.toByteArray())+"\"");
260 }
261
262
263
264 /**
265 * Starts the test.
266 */
267 public void start() {
268 // the test message
269 String m = "This demo message will be signed and/or encrypted.";
270 System.out.println("Test message: \""+m+"\"");
271 System.out.println();
272 byte[] message = m.getBytes();
273
274 try {
275 testSignedAndEnvelopedDataStream(message);
276 System.out.println();
277
278 System.out.println("Ready!");
279 } catch (Exception ex) {
280 ex.printStackTrace();
281
282 throw new RuntimeException(ex.toString());
283 }
284 }
285
286 /**
287 * Main method.
288 *
289 * @throws IOException
290 * if an I/O error occurs when reading required keys
291 * and certificates from files
292 */
293 public static void main(String argv[]) throws Exception {
294
295 demo.DemoUtil.initDemos();
296
297 (new SignedAndEnvelopedDataOutputStreamDemo()).start();
298 DemoUtil.waitKey();
299 }
300
301 }