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/SHA2withDSASignedDataDemo.java 5 12.02.25 17:58 Dbratko $
059 // $Revision: 5 $
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.CMSException;
068 import iaik.cms.IssuerAndSerialNumber;
069 import iaik.cms.SignedData;
070 import iaik.cms.SignedDataStream;
071 import iaik.cms.SignerInfo;
072 import iaik.cms.attributes.CMSContentType;
073 import iaik.cms.attributes.SigningTime;
074 import iaik.utils.Util;
075 import iaik.x509.X509Certificate;
076
077 import java.io.ByteArrayInputStream;
078 import java.io.ByteArrayOutputStream;
079 import java.io.IOException;
080 import java.io.InputStream;
081 import java.security.NoSuchAlgorithmException;
082 import java.security.PrivateKey;
083 import java.security.SignatureException;
084
085 import demo.DemoUtil;
086 import demo.keystore.CMSKeyStore;
087
088
089 /**
090 * Demonstrates the usage of class {@link iaik.cms.SignedDataStream} and
091 * {@link iaik.cms.SignedData} for signing some data using the CMS type
092 * SignedData with SHA2withDSA signature algorithm according to FIPS 186-3.
093 */
094 public class SHA2withDSASignedDataDemo {
095
096 // The private key of the signer.
097 PrivateKey signerKey_;
098
099 // The certificate chain of the signer.
100 X509Certificate[] signerCertificates_;
101
102
103
104 /**
105 * Setups the demo certificate chains.
106 *
107 * Keys and certificate are retrieved from the demo KeyStore.
108 *
109 * @throws IOException if an file read error occurs
110 */
111 public SHA2withDSASignedDataDemo() throws IOException {
112
113 System.out.println();
114 System.out.println("**********************************************************************************");
115 System.out.println("* SHA2withDSASignedDataDemo *");
116 System.out.println("* (shows the usage of the CMS SignedData type with SHA2withDSA) *");
117 System.out.println("**********************************************************************************");
118 System.out.println();
119
120 // add all certificates to the list
121 signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
122 signerKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
123 }
124
125 /**
126 * Creates a CMS <code>SignedData</code> object.
127 * <p>
128 *
129 * @param message the message to be signed, as byte representation
130 * @param mode the transmission mode, either IMPLICIT or EXPLICIT
131 * @return the BER encoding of the <code>SignedData</code> object just created
132 * @throws CMSException if the <code>SignedData</code> object cannot
133 * be created
134 * @throws IOException if some stream I/O error occurs
135 */
136 public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException {
137
138 System.out.println("Create a new message signed by " + signerCertificates_[0].getSubjectDN());
139
140 // we are testing the stream interface
141 ByteArrayInputStream is = new ByteArrayInputStream(message);
142 // create a new SignedData object which includes the data
143 SignedDataStream signed_data = new SignedDataStream(is, mode);
144
145 // SignedData shall include the certificate chain for verifying
146 signed_data.setCertificates(signerCertificates_);
147
148 // cert at index 0 is the signer certificate
149 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(signerCertificates_[0]);
150
151 // create a new SignerInfo
152 SignerInfo signer_info = new SignerInfo(issuer,
153 (AlgorithmID)AlgorithmID.sha256.clone(),
154 (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
155 signerKey_);
156 // create some authenticated attributes
157 // the message digest attribute is automatically added
158 Attribute[] attributes = new Attribute[2];
159 try {
160 // content type is data
161 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
162 attributes[0] = new Attribute(contentType);
163 // signing time is now
164 SigningTime signingTime = new SigningTime();
165 attributes[1] = new Attribute(signingTime);
166
167 } catch (Exception ex) {
168 throw new CMSException("Error creating attribute: " + ex.toString());
169 }
170 // set the attributes
171 signer_info.setSignedAttributes(attributes);
172 // finish the creation of SignerInfo by calling method addSigner
173 try {
174 signed_data.addSignerInfo(signer_info);
175 } catch (NoSuchAlgorithmException ex) {
176 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
177 }
178
179 // write the data through SignedData to any out-of-band place
180 if (mode == SignedDataStream.EXPLICIT) {
181 InputStream data_is = signed_data.getInputStream();
182 byte[] buf = new byte[1024];
183 int r;
184 while ((r = data_is.read(buf)) > 0) {
185 ; // skip data
186 }
187 }
188
189 // return the SignedData as DER encoded byte array with block size 2048
190 ByteArrayOutputStream os = new ByteArrayOutputStream();
191 signed_data.writeTo(os, 2048);
192 return os.toByteArray();
193 }
194
195
196 /**
197 * Parses a CMS <code>SignedData</code> object and verifies the signatures
198 * for all participated signers.
199 *
200 * @param signedData <code>SignedData</code> object as BER encoded byte array
201 * @param message the the message which was transmitted out-of-band (explicit signed)
202 *
203 * @return the inherent message as byte array
204 * @throws CMSException if any signature does not verify
205 * @throws IOException if some stream I/O error occurs
206 */
207 public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException {
208
209 // we are testing the stream interface
210 ByteArrayInputStream is = new ByteArrayInputStream(signedData);
211 // create the SignedData object
212 SignedDataStream signed_data = new SignedDataStream(is);
213
214 if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
215 // in explicit mode explicitly supply the content for hash computation
216 signed_data.setInputStream(new ByteArrayInputStream(message));
217 }
218
219 // get an InputStream for reading the signed content
220 InputStream data = signed_data.getInputStream();
221 ByteArrayOutputStream os = new ByteArrayOutputStream();
222 Util.copyStream(data, os, null);
223
224 System.out.println("SignedData contains the following signer information:");
225 SignerInfo[] signer_infos = signed_data.getSignerInfos();
226
227 int numberOfSignerInfos = signer_infos.length;
228 if (numberOfSignerInfos == 0) {
229 String warning = "Warning: Unsigned message (no SignerInfo included)!";
230 System.err.println(warning);
231 throw new CMSException(warning);
232 } else {
233 for (int i = 0; i < numberOfSignerInfos; i++) {
234 AlgorithmID signatureAlgorithm = signer_infos[i].getSignatureAlgorithm();
235 try {
236 // verify the signed data using the SignerInfo at index i
237 X509Certificate signer_cert = signed_data.verify(i);
238 // if the signature is OK the certificate of the signer is returned
239 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signer_cert.getSubjectDN());
240 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
241 if (signingTime != null) {
242 System.out.println("This message has been signed at " + signingTime.get());
243 }
244 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
245 if (contentType != null) {
246 System.out.println("The content has CMS content type " + contentType.get().getName());
247 }
248 } catch (SignatureException ex) {
249 // if the signature is not OK a SignatureException is thrown
250 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
251 throw new CMSException(ex.toString());
252 }
253 }
254
255 // now check alternative signature verification
256 System.out.println("Now check the signature assuming that no certs have been included:");
257 try {
258 SignerInfo signer_info = signed_data.verify(signerCertificates_[0]);
259 AlgorithmID signatureAlgorithm = signer_info.getSignatureAlgorithm();
260 // if the signature is OK the certificate of the signer is returned
261 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signerCertificates_[0].getSubjectDN());
262
263 } catch (SignatureException ex) {
264 // if the signature is not OK a SignatureException is thrown
265 System.out.println("Signature ERROR from signer: "+signerCertificates_[0].getSubjectDN());
266 throw new CMSException(ex.toString());
267 }
268 // in practice we also would validate the signer certificate(s)
269 }
270 return os.toByteArray();
271 }
272
273
274
275 /**
276 * Creates a CMS <code>SignedData</code> object.
277 * <p>
278 *
279 * @param message the message to be signed, as byte representation
280 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT
281 * @return the DER encoded <code>SignedData</code> object
282 * @throws CMSException if the <code>SignedData</code> object cannot
283 * be created
284 */
285 public byte[] createSignedData(byte[] message, int mode) throws CMSException {
286
287 System.out.println("Create a new message signed by " + signerCertificates_[0].getSubjectDN());
288
289 // create a new SignedData object which includes the data
290 SignedData signed_data = new SignedData(message, mode);
291
292 // SignedData shall include the certificate chain for verifying
293 signed_data.setCertificates(signerCertificates_);
294
295 // cert at index 0 is the signer certificate
296 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(signerCertificates_[0]);
297
298 // create a new SignerInfo
299 SignerInfo signer_info = new SignerInfo(issuer,
300 (AlgorithmID)AlgorithmID.sha256.clone(),
301 (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(),
302 signerKey_);
303 // create some authenticated attributes
304 // the message digest attribute is automatically added
305 Attribute[] attributes = new Attribute[2];
306 try {
307 // content type is data
308 CMSContentType contentType = new CMSContentType(ObjectID.cms_data);
309 attributes[0] = new Attribute(contentType);
310 // signing time is now
311 SigningTime signingTime = new SigningTime();
312 attributes[1] = new Attribute(signingTime);
313 // signing certificate
314 } catch (Exception ex) {
315 throw new CMSException("Error creating attribute: " + ex.toString());
316 }
317 // set the attributes
318 signer_info.setSignedAttributes(attributes);
319 // finish the creation of SignerInfo by calling method addSigner
320 try {
321 signed_data.addSignerInfo(signer_info);
322 } catch (NoSuchAlgorithmException ex) {
323 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
324 }
325 return signed_data.getEncoded();
326 }
327
328
329 /**
330 * Parses a CMS <code>SignedData</code> object and verifies the signatures
331 * for all participated signers.
332 *
333 * @param encoding the DER encoded <code>SignedData</code> object
334 * @param message the the message which was transmitted out-of-band (explicit signed)
335 *
336 * @return the inherent message as byte array
337 * @throws CMSException if any signature does not verify
338 * @throws IOException if some stream I/O error occurs
339 */
340 public byte[] getSignedData(byte[] encoding, byte[] message) throws CMSException, IOException {
341
342 ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding);
343 // create the SignedData object
344 SignedData signed_data = new SignedData(encodedStream);
345
346 if (signed_data.getMode() == SignedData.EXPLICIT) {
347 // in explcit mode explictly supply the content data to do the hash calculation
348 signed_data.setContent(message);
349 }
350
351 System.out.println("SignedData contains the following signer information:");
352 SignerInfo[] signer_infos = signed_data.getSignerInfos();
353
354 int numberOfSignerInfos = signer_infos.length;
355 if (numberOfSignerInfos == 0) {
356 String warning = "Warning: Unsigned message (no SignerInfo included)!";
357 System.err.println(warning);
358 throw new CMSException(warning);
359 } else {
360 for (int i = 0; i < numberOfSignerInfos; i++) {
361 AlgorithmID signatureAlgorithm = signer_infos[i].getSignatureAlgorithm();
362 try {
363 // verify the signed data using the SignerInfo at index i
364 X509Certificate signer_cert = signed_data.verify(i);
365 // if the signature is OK the certificate of the signer is returned
366 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signer_cert.getSubjectDN());
367 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
368 if (signingTime != null) {
369 System.out.println("This message has been signed at " + signingTime.get());
370 }
371 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
372 if (contentType != null) {
373 System.out.println("The content has CMS content type " + contentType.get().getName());
374 }
375 } catch (SignatureException ex) {
376 // if the signature is not OK a SignatureException is thrown
377 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
378 throw new CMSException(ex.toString());
379 }
380 }
381
382 // now check alternative signature verification
383 System.out.println("Now check the signature assuming that no certs have been included:");
384 try {
385 SignerInfo signer_info = signed_data.verify(signerCertificates_[0]);
386 AlgorithmID signatureAlgorithm = signer_info.getSignatureAlgorithm();
387 // if the signature is OK the certificate of the signer is returned
388 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
389
390 } catch (SignatureException ex) {
391 // if the signature is not OK a SignatureException is thrown
392 System.out.println("Signature ERROR from signer: "+signerCertificates_[0].getSubjectDN());
393 throw new CMSException(ex.toString());
394 }
395 // in practice we also would validate the signer certificate(s)
396 }
397
398 return signed_data.getContent();
399 }
400
401 /**
402 * Tests the CMS SignedData implementation.
403 */
404 public void start() {
405 // the test message
406 String m = "This is the test message.";
407 System.out.println("Test message: \""+m+"\"");
408 System.out.println();
409 byte[] message = m.getBytes();
410
411 try {
412 byte[] encoding;
413 byte[] received_message = null;
414 System.out.println("Stream implementation demos");
415 System.out.println("===========================");
416 //
417 // test CMS Implicit SignedDataStream
418 //
419 System.out.println("\nImplicit SignedDataStream demo [create]:\n");
420 encoding = createSignedDataStream(message, SignedDataStream.IMPLICIT);
421 // transmit data
422 System.out.println("\nImplicit SignedDataStream demo [parse]:\n");
423 received_message = getSignedDataStream(encoding, null);
424 System.out.print("\nSigned content: ");
425 System.out.println(new String(received_message));
426
427 //
428 // test CMS Explicit SignedDataStream
429 //
430 System.out.println("\nExplicit SignedDataStream demo [create]:\n");
431 encoding = createSignedDataStream(message, SignedDataStream.EXPLICIT);
432 // transmit data
433 System.out.println("\nExplicit SignedDataStream demo [parse]:\n");
434 received_message = getSignedDataStream(encoding, message);
435 System.out.print("\nSigned content: ");
436 System.out.println(new String(received_message));
437
438 // the non-stream implementation
439 System.out.println("\nNon-stream implementation demos");
440 System.out.println("===============================");
441
442 //
443 // test CMS Implicit SignedData
444 //
445 System.out.println("\nImplicit CMS SignedData demo [create]:\n");
446 encoding = createSignedData(message, SignedData.IMPLICIT);
447 // transmit data
448 System.out.println("\nImplicit CMS SignedData demo [parse]:\n");
449 received_message = getSignedData(encoding, null);
450 System.out.print("\nSigned content: ");
451 System.out.println(new String(received_message));
452
453 //
454 // test CMS Explicit SignedData
455 //
456 System.out.println("\nExplicit CMS SignedData demo [create]:\n");
457 encoding = createSignedData(message, SignedData.EXPLICIT);
458 // transmit data
459 System.out.println("\nExplicit CMS SignedData demo [parse]:\n");
460 received_message = getSignedData(encoding, message);
461 System.out.print("\nSigned content: ");
462 System.out.println(new String(received_message));
463
464 } catch (Exception ex) {
465 ex.printStackTrace();
466 throw new RuntimeException(ex.toString());
467 }
468 }
469
470 /**
471 * The main method.
472 *
473 * @throws IOException
474 * if an I/O error occurs when reading required keys
475 * and certificates from files
476 */
477 public static void main(String argv[]) throws Exception {
478
479 DemoUtil.initDemos();
480 (new SHA2withDSASignedDataDemo()).start();
481 System.out.println("\nReady!");
482 DemoUtil.waitKey();
483 }
484 }