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 }