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/signedData/SignedDataDemoWithAdditionalSignerInfo.java 20    12.02.25 17:58 Dbratko $
059    // $Revision: 20 $
060    //
061    
062    package demo.cms.signedData;
063    
064    import iaik.asn1.ObjectID;
065    import iaik.asn1.structures.AlgorithmID;
066    import iaik.asn1.structures.Attribute;
067    import iaik.cms.IssuerAndSerialNumber;
068    import iaik.cms.SignedData;
069    import iaik.cms.SignerInfo;
070    import iaik.cms.attributes.CMSContentType;
071    import iaik.cms.attributes.SigningTime;
072    import iaik.x509.X509Certificate;
073    
074    import java.io.ByteArrayInputStream;
075    import java.io.ByteArrayOutputStream;
076    import java.io.IOException;
077    import java.io.InputStream;
078    import java.io.OutputStream;
079    import java.security.PrivateKey;
080    import java.security.cert.Certificate;
081    
082    import demo.DemoUtil;
083    import demo.keystore.CMSKeyStore;
084    
085    /**
086     * This class shows how to use the non-stream based {@link iaik.cms.SignedData SignedData} 
087     * implementation to add a new SignerInfo to an existing, parsed SignedData object.
088     */
089    public class SignedDataDemoWithAdditionalSignerInfo {
090    
091      String testMessage;
092    
093      public SignedDataDemoWithAdditionalSignerInfo() {
094        
095        System.out.println();
096        System.out.println("**********************************************************************************");
097        System.out.println("*                      SignedDataDemoWithAdditionalSignerInfo                    *");
098        System.out.println("*    (shows how to add a new signer to an already existing SignedData object)    *");
099        System.out.println("**********************************************************************************");
100        System.out.println();
101        
102        testMessage = "This is a test of the CMS implementation!";
103      }
104    
105      /**
106       * Verifies a given SignedData object which is read from the given input stream.
107       *
108       * @param is an input stream holding the SignedData object
109       *
110       * @throws Exception if an error occurs when verifying the SignedData
111       */
112      private void verify(InputStream is) throws Exception {
113    
114        System.out.println("\nVerify the SignedData...");
115        // read the SignedData from the given input stream
116        SignedData signedData = new SignedData(is);
117        // get the content
118        byte[] content = signedData.getContent();
119        // and show it
120        System.out.println("Content of SignedData: "+new String(content));
121    
122        // print the certificates included
123        System.out.println("Certificates included:");
124        Certificate[] certs = signedData.getCertificates();
125        try {
126          for (int i = 0; i < certs.length; i++)
127            System.out.println(certs[i]);
128        } catch (Exception ex) {
129          ex.printStackTrace();
130        }
131                    
132        // print the signers included
133        System.out.println("Signers included:");
134        SignerInfo[] signerInfos = signedData.getSignerInfos();
135    
136        int numberOfSignerInfos = signerInfos.length;
137        if (numberOfSignerInfos == 0) {
138          String warning = "Warning: Unsigned message (no SignerInfo included)!";  
139          System.err.println(warning);
140          throw new Exception(warning);
141        } else {
142          for (int i = 0; i < numberOfSignerInfos; i++) {
143            X509Certificate cert = signedData.verify(i);
144            System.out.println("Signer: " + cert.getSubjectDN());
145            System.out.println(signerInfos[i].toString(true));
146          }
147          // in practice we also would validate the signer certificate(s)  
148        }
149      }
150        
151      /**
152       * Creates a new SignedData object.
153       *
154       * @param os the output stream where the created object shall be written to
155       * @param message the content for the SignedData
156       * @param size specifies which private key/certificate to use from the KeyStore
157       *
158       * @throws Exception if an error occurs when creating the SignedData object
159       */
160      private void create(OutputStream os, String message, int size) throws Exception {
161    
162        System.out.println("\nCreate a new SignedData with content: "+message);
163        // get the certificate chain from teh KeyStore
164        X509Certificate[] certificates = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, size);
165        // create the SignedData
166        SignedData signedData = new SignedData(message.getBytes(), iaik.cms.SignedData.IMPLICIT);
167        // set teh certificates
168        signedData.setCertificates(certificates);
169        // add a signer
170        addSigner(signedData, size);
171        // and write it the given output stream
172        signedData.writeTo(os);
173        os.close();
174      }
175    
176      /**
177       * Adds a new Signer to a given SignedData.
178       *
179       * @param is the input stream holding the existing SignedData object
180       * @param size specifies which private key/certificate to use from the KeyStore
181       */
182      private ByteArrayOutputStream add(InputStream is, int size) throws Exception {
183    
184        System.out.println("Adding a signature to an existing SignedData...");
185        // read the existing SignedData from the given InputStream
186        SignedData signedData = new SignedData(is);
187        // print the content
188        byte[] content = signedData.getContent();
189        System.out.println("Existing content is: " + new String(content));
190        
191        // add another signer
192        addSigner(signedData, size);
193    
194        // create a new output stream and save it
195        ByteArrayOutputStream os = new ByteArrayOutputStream();
196        signedData.writeTo(os);
197    
198        is.close();
199        os.close();
200    
201        // return the output stream which contains the new SignedData
202        return os;
203      }
204    
205      /**
206       * Adds the new signer.
207       *
208       * @param signedData the SignedData where the new signer shall be added
209       * @param size specifies which private key/certificate to use from the KeyStore
210       */
211      private void addSigner(SignedData signedData, int size) throws Exception {
212            
213        // get new certificate and private key
214        X509Certificate cert = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, size)[0];
215        PrivateKey privateKey = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, size);
216            
217        // add to the existing list of certificates
218        Certificate[] certs = signedData.getCertificates();
219        Certificate[] newCerts = new Certificate [certs.length + 1];
220        System.arraycopy(certs, 0, newCerts, 0, certs.length);
221        newCerts[certs.length] = cert;
222        // set the new certificate list
223        signedData.setCertificates(newCerts);
224            
225        // create a new SignerInfo
226        SignerInfo signerInfo = new SignerInfo(new IssuerAndSerialNumber(cert), 
227                                               (AlgorithmID)AlgorithmID.sha256.clone(),
228                                               privateKey);
229        // define some attributes
230        Attribute[] attributes = { 
231          new Attribute(new CMSContentType(ObjectID.cms_data)),
232          new Attribute(new SigningTime())
233        };
234        // set the attributes
235        signerInfo.setSignedAttributes(attributes);
236        // and add the new signer
237        signedData.addSignerInfo(signerInfo);
238      }
239      
240      /**
241       * Starts the test.
242       */
243      public void start() {
244    
245        try {
246          // output stream for storing the signed data
247          ByteArrayOutputStream os = new ByteArrayOutputStream();
248          // create a new SignedData
249          create(os, testMessage, CMSKeyStore.SZ_2048_SIGN_1);
250          // verify it      
251          verify(new ByteArrayInputStream(os.toByteArray()));
252          // add another signer
253          os = add(new ByteArrayInputStream(os.toByteArray()), CMSKeyStore.SZ_2048_SIGN_1);
254          // and verify it again
255          verify(new ByteArrayInputStream(os.toByteArray()));
256    
257            } catch (Exception ex) {
258              ex.printStackTrace();
259              throw new RuntimeException(ex.toString());
260            }
261      }
262        
263      /**
264       * Main method.
265       * 
266       * @throws IOException 
267       *            if an I/O error occurs when reading required keys
268       *            and certificates from files
269       */
270      public static void main(String argv[]) throws IOException {
271    
272        DemoUtil.initDemos();
273        (new SignedDataDemoWithAdditionalSignerInfo()).start();
274           
275        System.out.println("\nReady!");
276        DemoUtil.waitKey();
277      }
278    }