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/ecc/ECDSASignedDataOutputStreamDemo.java 5 12.02.25 17:58 Dbratko $
059 // $Revision: 5 $
060 //
061
062 package demo.cms.ecc;
063
064 import java.io.ByteArrayInputStream;
065 import java.io.ByteArrayOutputStream;
066 import java.io.IOException;
067 import java.io.InputStream;
068 import java.security.NoSuchAlgorithmException;
069 import java.security.PrivateKey;
070 import java.security.SignatureException;
071
072 import demo.DemoUtil;
073 import demo.cms.ecc.keystore.CMSEccKeyStore;
074 import iaik.asn1.ObjectID;
075 import iaik.asn1.structures.AlgorithmID;
076 import iaik.asn1.structures.Attribute;
077 import iaik.cms.CMSAlgorithmID;
078 import iaik.cms.CMSException;
079 import iaik.cms.ContentInfoOutputStream;
080 import iaik.cms.IssuerAndSerialNumber;
081 import iaik.cms.SignedDataOutputStream;
082 import iaik.cms.SignedDataStream;
083 import iaik.cms.SignerInfo;
084 import iaik.cms.attributes.CMSContentType;
085 import iaik.cms.attributes.SigningTime;
086 import iaik.utils.KeyAndCertificate;
087 import iaik.utils.Util;
088 import iaik.x509.X509Certificate;
089
090
091 /**
092 * This class demonstrates the IAIK-CMS SignedDataOutputStream implementation
093 * with the ECDSA (with SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD160) signature algorithm.
094 * <p>
095 * Any keys/certificates required for this demo are read from a keystore
096 * file "cmsecc.keystore" located in your current working directory. If
097 * the keystore file does not exist you can create it by running the
098 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore}
099 * program.
100 * <p>
101 * Additionally to <code>iaik_cms.jar</code> you also must have
102 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href =
103 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">
104 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>),
105 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href =
106 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank">
107 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>)
108 * in your classpath.
109 */
110 public class ECDSASignedDataOutputStreamDemo {
111
112 /**
113 * Default Constructor.
114 */
115 public ECDSASignedDataOutputStreamDemo() throws Exception {
116 System.out.println();
117 System.out.println("**********************************************************************************");
118 System.out.println("* ECDSASignedData demo *");
119 System.out.println("* (shows how to use the SignedData(Stream) implementation with ECDSA) *");
120 System.out.println("**********************************************************************************");
121 System.out.println();
122
123 }
124
125 /**
126 * Creates an ECDSA signed CMS <code>SignedDataStream</code> object and wraps it by a
127 * CMS <code>ContentInfoStream</code>.
128 *
129 * @param message the message to be signed, as byte representation
130 * @param mode the transmission mode, either IMPLICIT or EXPLICIT
131 * @param hashAlgorithm the hash algorithm to be used
132 * @param signatureAlgorithm the signature algorithm to be used
133 * @param signerKey the private key of the signer
134 * @param certificates the certificate chain of the signer
135 *
136 * @return the DER encoding of the <code>ContentInfo</code> object just created
137 *
138 * @throws CMSException if the <code>SignedData</code>, <code>ContentInfo</code>
139 * object cannot be created
140 * @throws IOException if an I/O related error occurs
141 */
142 public byte[] createSignedDataStream(byte[] message,
143 int mode,
144 AlgorithmID hashAlgorithm,
145 AlgorithmID signatureAlgorithm,
146 PrivateKey signerKey,
147 X509Certificate[] certificates)
148 throws CMSException, IOException {
149
150 System.out.print("Create a new message signed with " + signatureAlgorithm.getName());
151
152 // we are testing the stream interface
153 ByteArrayInputStream is = new ByteArrayInputStream(message);
154
155 // the stream to which to write the SignedData
156 ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
157
158 // wrap SignedData into a ContentInfo
159 ContentInfoOutputStream contentInfoStream =
160 new ContentInfoOutputStream(ObjectID.cms_signedData, resultStream);
161 SignedDataOutputStream signedData = new SignedDataOutputStream(contentInfoStream, mode);
162
163 // add the certificates
164 signedData.addCertificates(certificates);
165
166 // cert at index 0 is the user certificate
167 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]);
168
169 // create a new SignerInfo
170 AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone();
171 // CMS-ECDSA requires to encode the parameter field as NULL (see RFC 3278)
172 ecdsaSig.encodeAbsentParametersAsNull(true);
173 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey);
174
175 try {
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 } catch (Exception ex) {
189 throw new CMSException("Error adding attributes: " + ex.toString());
190 }
191
192 // finish the creation of SignerInfo by calling method addSigner
193 try {
194 signedData.addSignerInfo(signer_info);
195 } catch (NoSuchAlgorithmException ex) {
196 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage());
197 }
198
199 int blockSize = 4; // in real world we would use a block size like 2048
200 // write in the data to be signed
201 byte[] buffer = new byte[blockSize];
202 int bytesRead;
203 while ((bytesRead = is.read(buffer)) != -1) {
204 signedData.write(buffer, 0, bytesRead);
205 }
206
207 // closing the stream add the signer infos and closes the underlying stream
208 signedData.close();
209 return resultStream.toByteArray();
210 }
211
212
213 /**
214 * Parses a CMS <code>ContentInfo</code> object holding a <code>SignedData</code>
215 * object and verifies the signature.
216 *
217 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code>
218 * object as BER encoded byte array
219 * @param message the the message which was transmitted out-of-band (explicit signed)
220 * @param certificates the certificate of the signer (used for alternative signature verification)
221 *
222 * @return the inherent message as byte array
223 *
224 * @throws CMSException if any signature does not verify
225 * @throws IOException if an I/O related error occurs
226 */
227 public byte[] getSignedDataStream(byte[] signedData, byte[] message, X509Certificate[] certificates)
228 throws CMSException, IOException {
229
230 // we are testing the stream interface
231 ByteArrayInputStream is = new ByteArrayInputStream(signedData);
232
233 SignedDataStream signed_data = new SignedDataStream(is);
234
235 if (signed_data.getMode() == SignedDataStream.EXPLICIT) {
236 // in explicit mode explicitly supply the content for hash computation
237 signed_data.setInputStream(new ByteArrayInputStream(message));
238 }
239
240 // get an InputStream for reading the signed content and update hash computation
241 InputStream data = signed_data.getInputStream();
242 ByteArrayOutputStream os = new ByteArrayOutputStream();
243 Util.copyStream(data, os, null);
244
245 System.out.println("SignedData contains the following signer information:");
246 SignerInfo[] signer_infos = signed_data.getSignerInfos();
247
248 int numberOfSignerInfos = signer_infos.length;
249 if (numberOfSignerInfos == 0) {
250 String warning = "Warning: Unsigned message (no SignerInfo included)!";
251 System.err.println(warning);
252 throw new CMSException(warning);
253 } else {
254 for (int i = 0; i < numberOfSignerInfos; i++) {
255 try {
256 // verify the signed data using the SignerInfo at index i
257 X509Certificate signer_cert = signed_data.verify(i);
258 // if the signature is OK the certificate of the signer is returned
259 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN());
260 // check for some included attributes
261 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime);
262 if (signingTime != null) {
263 System.out.println("This message has been signed at " + signingTime.get());
264 }
265 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType);
266 if (contentType != null) {
267 System.out.println("The content has CMS content type " + contentType.get().getName());
268 }
269 } catch (SignatureException ex) {
270 // if the signature is not OK a SignatureException is thrown
271 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN());
272 throw new CMSException(ex.toString());
273 }
274 }
275 // now check alternative signature verification
276 System.out.println("Now check the signature assuming that no certs have been included:");
277 try {
278 SignerInfo signer_info = signed_data.verify(certificates[0]);
279 // if the signature is OK the certificate of the signer is returned
280 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN());
281 } catch (SignatureException ex) {
282 // if the signature is not OK a SignatureException is thrown
283 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN());
284 throw new CMSException(ex.toString());
285 }
286 // in practice we also would validate the signer certificate(s)
287 }
288 return os.toByteArray();
289 }
290
291
292
293 /**
294 * Runs the signing - verifying demo.
295 *
296 * @param message the message to be signed
297 * @param hashAlgorithm the hash algorithm to be used
298 * @param signatureAlgorithm the signature algorithm to be used
299 * @param signerKeyAndCert private key and certificate chain of the signer
300 */
301 public void runDemo(byte[] message,
302 AlgorithmID hashAlgorithm,
303 AlgorithmID signatureAlgorithm,
304 KeyAndCertificate signerKeyAndCert)
305 throws Exception {
306
307 PrivateKey signerKey = signerKeyAndCert.getPrivateKey();
308 X509Certificate[] signerCerts = signerKeyAndCert.getCertificateChain();
309
310 byte[] encodedSignedData;
311 byte[] received_message = null;
312
313 System.out.println("\nRun demos for " + hashAlgorithm.getName() + " / " + signatureAlgorithm.getName() + "\n");
314
315 System.out.println("Stream implementation demos");
316 System.out.println("===========================");
317 //
318 // test CMS Implicit SignedDataStream
319 //
320 System.out.println("\nImplicit SignedDataStream demo [create]:\n");
321 encodedSignedData = createSignedDataStream(message,
322 SignedDataStream.IMPLICIT,
323 hashAlgorithm,
324 signatureAlgorithm,
325 signerKey,
326 signerCerts);
327 System.out.println();
328
329 // transmit data
330 System.out.println("\nImplicit SignedDataStream demo [parse]:\n");
331 received_message = getSignedDataStream(encodedSignedData, null, signerCerts);
332 System.out.print("\nSigned content: ");
333 System.out.println(new String(received_message));
334
335 //
336 // test CMS Explicit SignedDataStream
337 //
338 System.out.println("\nExplicit SignedDataStream demo [create]:\n");
339 encodedSignedData = createSignedDataStream(message,
340 SignedDataStream.EXPLICIT,
341 hashAlgorithm,
342 signatureAlgorithm,
343 signerKey,
344 signerCerts);
345 // transmit data
346 System.out.println("\nExplicit SignedDataStream demo [parse]:\n");
347 received_message = getSignedDataStream(encodedSignedData, message, signerCerts);
348 System.out.print("\nSigned content: ");
349 System.out.println(new String(received_message));
350
351 // the non-stream implementation
352 System.out.println("\nNon-stream implementation demos");
353 System.out.println("===============================");
354
355
356 }
357
358 /**
359 * Tests the CMS SignedData implementation with the ECDSA signature
360 * algorithm and several hash algorithms.
361 */
362 public void start() throws Exception {
363
364 // the test message
365 String m = "This is the test message.";
366 System.out.println("Test message: \""+m+"\"");
367 System.out.println();
368 byte[] message = m.getBytes();
369
370 AlgorithmID[][] algorithms = new AlgorithmID[][] {
371 { CMSAlgorithmID.sha1, CMSAlgorithmID.ecdsa_With_SHA1 },
372 { CMSAlgorithmID.sha224, CMSAlgorithmID.ecdsa_With_SHA224 },
373 { CMSAlgorithmID.sha256, CMSAlgorithmID.ecdsa_With_SHA256 },
374 { CMSAlgorithmID.sha384, CMSAlgorithmID.ecdsa_With_SHA384 },
375 { CMSAlgorithmID.sha512, CMSAlgorithmID.ecdsa_With_SHA512 },
376 // ECDSA with RIPEMD-160 in plain format (BSI)
377 { CMSAlgorithmID.ripeMd160, CMSAlgorithmID.ecdsa_plain_With_RIPEMD160 },
378 };
379
380 // get signer key and certs
381 KeyAndCertificate[] keyAndCerts = {
382 // P-192
383 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN),
384 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN)),
385 // P-224
386 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_224_SIGN),
387 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_224_SIGN)),
388 // P-256
389 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN),
390 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN)),
391 // P-384
392 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN),
393 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN)),
394 // P-521
395 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN),
396 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN)),
397 // P-192 (for ECDSA with RIPEMD-160 in plain format (BSI)
398 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN),
399 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN)),
400
401
402 };
403
404 final int HASH_ALG = 0;
405 final int SIGNATURE_ALG = 1;
406 for (int i = 0; i < algorithms.length; i++) {
407 runDemo(message, algorithms[i][HASH_ALG], algorithms[i][SIGNATURE_ALG], keyAndCerts[i]);
408 }
409
410 }
411
412 /**
413 * Starts the demo.
414 *
415 * @throws Exception
416 * if an error occurs
417 */
418 public static void main(String argv[]) throws Exception {
419
420 DemoUtil.initDemos();
421 ECCDemoUtil.installIaikEccProvider();
422 (new ECDSASignedDataOutputStreamDemo()).start();
423 System.out.println("\nReady!");
424 System.in.read();
425 }
426
427 }