001// Copyright (C) 2002 IAIK
002// https://sic.tech/
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// This source is provided for inspection purposes and recompilation only,
011// unless specified differently in a contract with IAIK. This source has to
012// be kept in strict confidence and must not be disclosed to any third party
013// under any circumstances. Redistribution in source and binary forms, with
014// or without modification, are <not> permitted in any case!
015//
016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
026// SUCH DAMAGE.
027//
028// $Header: /IAIK-CMS/current/src/demo/cms/signedAndEnvelopedData/SignedAndEnvelopedDataOutputStreamDemo.java 13    12.02.25 17:58 Dbratko $
029// $Revision: 13 $
030//
031
032package demo.cms.signedAndEnvelopedData;
033
034import iaik.asn1.ObjectID;
035import iaik.asn1.structures.AlgorithmID;
036import iaik.asn1.structures.Attribute;
037import iaik.cms.CMSException;
038import iaik.cms.ContentInfoOutputStream;
039import iaik.cms.EnvelopedDataOutputStream;
040import iaik.cms.EnvelopedDataStream;
041import iaik.cms.IssuerAndSerialNumber;
042import iaik.cms.KeyTransRecipientInfo;
043import iaik.cms.RecipientInfo;
044import iaik.cms.SignedDataOutputStream;
045import iaik.cms.SignedDataStream;
046import iaik.cms.SignerInfo;
047import iaik.cms.attributes.CMSContentType;
048import iaik.cms.attributes.SigningTime;
049import iaik.security.random.SecRandom;
050import iaik.utils.Util;
051import iaik.x509.X509Certificate;
052
053import java.io.ByteArrayInputStream;
054import java.io.ByteArrayOutputStream;
055import java.io.IOException;
056import java.io.InputStream;
057import java.security.NoSuchAlgorithmException;
058import java.security.PrivateKey;
059import java.security.SecureRandom;
060
061import demo.DemoUtil;
062import demo.keystore.CMSKeyStore;
063
064/**
065 * This class shows the sequential combination of the SignedDataOutputStream
066 * and EnvelopedDataOutputStream implementations.
067 * <p>
068 * All keys and certificates are read from a keystore created by the
069 * SetupCMSKeyStore program.
070 */
071public class SignedAndEnvelopedDataOutputStreamDemo {
072
073  // signing certificate of user 1
074  X509Certificate user1_sign;
075  // signing private key of user 1
076  PrivateKey user1_sign_pk;
077  // encryption certificate of user 1
078  X509Certificate user1_crypt;
079  // encryption private key of user 1
080  PrivateKey user1_crypt_pk;
081  // encryption certificate of user 2
082  X509Certificate user2_crypt;
083  // encryption private key of user 2
084  PrivateKey user2_crypt_pk;
085  // a certificate chain containing the user certs + CA
086  X509Certificate[] certificates;
087  // secure random number generator
088  SecureRandom random;
089
090  /**
091   * Setup the demo certificate chains.
092   *
093   * Keys and certificate are retrieved from the demo KeyStore.
094   *
095   * @throws IOException if an file read error occurs
096   */
097  public SignedAndEnvelopedDataOutputStreamDemo() throws IOException {
098    
099    System.out.println();
100    System.out.println("*********************************************************************************************************");
101    System.out.println("*                                   SignedAndEnvelopedDataOutputStream demo                             *");
102    System.out.println("* (shows the usage of the combined SignedDataOutputStream and EnvelopedDataOutputStream implementation) *");
103    System.out.println("*********************************************************************************************************");
104    System.out.println();
105    
106    // add all certificates to the list
107    X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
108    user1_sign = certs[0];
109    user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1);
110    user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0];
111    user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1);
112    user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0];
113    user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2);
114    // user1 also includes her/his encryption certificate
115    certificates = new X509Certificate[certs.length+1];
116    System.arraycopy(certs, 0, certificates, 0, certs.length);
117    certificates[certs.length] = user1_crypt;
118
119    random = SecRandom.getDefault();
120  }
121
122  /**
123   * Signs and envelopes the given message, encodes the CMS object, decodes it 
124   * again, removes the envlope and verifies the signature.
125   * 
126   * @param message the message to be signed and enveloped
127   * @throws Exception if an error occurs
128   */
129  public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception {
130    
131    //  a stream from which to read the data to be signed and encrypted
132    ByteArrayInputStream is = new ByteArrayInputStream(message);
133    
134    // the stream to which to write the Signed and EnvelopedData 
135    ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
136
137    EnvelopedDataOutputStream envelopedData;
138
139    //  wrap into a ContentInfo 
140    ContentInfoOutputStream envContentInfoStream = 
141      new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream);
142    // create a new EnvelopedData object encrypted with AES
143    try {
144      envelopedData = new EnvelopedDataOutputStream(envContentInfoStream, 
145                                                    (AlgorithmID)AlgorithmID.aes256_CBC.clone());
146    } catch (NoSuchAlgorithmException ex) {
147      throw new CMSException(ex.toString());
148    }
149
150    // create the recipient infos
151    RecipientInfo[] recipients = new RecipientInfo[2];
152    // user1 is the first receiver
153    recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
154    // user2 is the second receiver
155    recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone());
156
157    // specify the recipients of the encrypted message
158    envelopedData.setRecipientInfos(recipients);
159    
160    //  wrap SignedData into a ContentInfo 
161    ContentInfoOutputStream signedContentInfoStream = 
162      new ContentInfoOutputStream(ObjectID.cms_signedData, envelopedData);
163
164    SignedDataOutputStream signedData = new SignedDataOutputStream(signedContentInfoStream, 
165                                                                   SignedDataOutputStream.IMPLICIT);
166    // these certificates are sent within the signature
167    signedData.setCertificates(certificates);
168
169    // add one signer
170    // cert at index 0 is the user certificate
171    IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign);
172
173    // create a new SignerInfo
174    SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk);
175      
176    // create some signed attributes
177    // the message digest attribute is automatically added
178    Attribute[] attributes = new Attribute[2];
179    // content type is data
180    CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
181    attributes[0] = new Attribute(contentType);
182    // signing time is now
183    SigningTime signingTime = new SigningTime();
184    attributes[1] = new Attribute(signingTime);
185  
186    // set the attributes
187    signer_info.setSignedAttributes(attributes);
188    // finish the creation of SignerInfo by calling method addSigner
189    signedData.addSignerInfo(signer_info);
190
191    int blockSize = 4; // in real world we would use a block size like 2048
192    //  write in the data to be signed
193    byte[] buffer = new byte[blockSize];
194    int bytesRead;
195    while ((bytesRead = is.read(buffer)) != -1) {
196      signedData.write(buffer, 0, bytesRead);
197    }
198    
199    // closing the stream finished the cryptographic processing and closes the underlying stream
200    signedData.close();  
201    
202    // get the signed and encrypted message from the ByteArrayOutputStream
203    byte[]  signed_enveloped_message = resultStream.toByteArray();
204    System.out.println("Message created, now doing the parsing...");
205    // and now decrypt the data and verify the signature
206    is = new ByteArrayInputStream(signed_enveloped_message);
207    EnvelopedDataStream enveloped = new EnvelopedDataStream(is);
208    // use this private key to decrypt the symmetric key of recipient 0
209    enveloped.setupCipher(user1_crypt_pk, 0);
210    // get the InputStream with the decrypted data
211    InputStream data_dec = enveloped.getInputStream();
212    System.out.println("Message decrypted!");
213    // read the signed data from the decrypted InputStream
214    SignedDataStream  signed = new SignedDataStream(data_dec);
215    // get the InputStream with the signed, plain data
216    InputStream data = signed.getInputStream();
217    // reset our output stream
218    ByteArrayOutputStream os = new ByteArrayOutputStream();
219    // copy the data
220    Util.copyStream(data, os, null);
221    os.close();
222    is.close();
223    data_dec.close();
224
225    // now verify the signature of the one and only signer and print the certificate of the signer
226    X509Certificate cert = signed.verify(0);
227    System.out.println("Signature OK from: "+cert.getSubjectDN());
228
229    System.out.println("Received message: \"" + new String(os.toByteArray())+"\"");
230  }
231
232
233
234  /**
235   * Starts the test.
236   */
237  public void start() {
238     // the test message
239    String m = "This demo message will be signed and/or encrypted.";
240    System.out.println("Test message: \""+m+"\"");
241    System.out.println();
242    byte[] message = m.getBytes();
243
244    try {
245      testSignedAndEnvelopedDataStream(message);
246      System.out.println();
247
248      System.out.println("Ready!");
249        } catch (Exception ex) {
250          ex.printStackTrace();
251
252          throw new RuntimeException(ex.toString());
253        }
254  }
255
256  /**
257   * Main method.
258   *
259   * @throws IOException
260   *            if an I/O error occurs when reading required keys
261   *            and certificates from files
262   */
263  public static void main(String argv[]) throws Exception {
264
265        demo.DemoUtil.initDemos();
266
267    (new SignedAndEnvelopedDataOutputStreamDemo()).start();
268    DemoUtil.waitKey();
269  }
270
271}