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 }