001// Copyright (C) 2002 IAIK 002// https://sic.tech/ 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// This source is provided for inspection purposes and recompilation only, 011// unless specified differently in a contract with IAIK. This source has to 012// be kept in strict confidence and must not be disclosed to any third party 013// under any circumstances. Redistribution in source and binary forms, with 014// or without modification, are <not> permitted in any case! 015// 016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 019// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 026// SUCH DAMAGE. 027// 028// $Header: /IAIK-CMS/current/src/demo/cms/ecc/ECDSASignedDataOutputStreamDemo.java 5 12.02.25 17:58 Dbratko $ 029// $Revision: 5 $ 030// 031 032package demo.cms.ecc; 033 034import java.io.ByteArrayInputStream; 035import java.io.ByteArrayOutputStream; 036import java.io.IOException; 037import java.io.InputStream; 038import java.security.NoSuchAlgorithmException; 039import java.security.PrivateKey; 040import java.security.SignatureException; 041 042import demo.DemoUtil; 043import demo.cms.ecc.keystore.CMSEccKeyStore; 044import iaik.asn1.ObjectID; 045import iaik.asn1.structures.AlgorithmID; 046import iaik.asn1.structures.Attribute; 047import iaik.cms.CMSAlgorithmID; 048import iaik.cms.CMSException; 049import iaik.cms.ContentInfoOutputStream; 050import iaik.cms.IssuerAndSerialNumber; 051import iaik.cms.SignedDataOutputStream; 052import iaik.cms.SignedDataStream; 053import iaik.cms.SignerInfo; 054import iaik.cms.attributes.CMSContentType; 055import iaik.cms.attributes.SigningTime; 056import iaik.utils.KeyAndCertificate; 057import iaik.utils.Util; 058import iaik.x509.X509Certificate; 059 060 061/** 062 * This class demonstrates the IAIK-CMS SignedDataOutputStream implementation 063 * with the ECDSA (with SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD160) signature algorithm. 064 * <p> 065 * Any keys/certificates required for this demo are read from a keystore 066 * file "cmsecc.keystore" located in your current working directory. If 067 * the keystore file does not exist you can create it by running the 068 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore} 069 * program. 070 * <p> 071 * Additionally to <code>iaik_cms.jar</code> you also must have 072 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href = 073 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank"> 074 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>), 075 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href = 076 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank"> 077 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>) 078 * in your classpath. 079 */ 080public class ECDSASignedDataOutputStreamDemo { 081 082 /** 083 * Default Constructor. 084 */ 085 public ECDSASignedDataOutputStreamDemo() throws Exception { 086 System.out.println(); 087 System.out.println("**********************************************************************************"); 088 System.out.println("* ECDSASignedData demo *"); 089 System.out.println("* (shows how to use the SignedData(Stream) implementation with ECDSA) *"); 090 System.out.println("**********************************************************************************"); 091 System.out.println(); 092 093 } 094 095 /** 096 * Creates an ECDSA signed CMS <code>SignedDataStream</code> object and wraps it by a 097 * CMS <code>ContentInfoStream</code>. 098 * 099 * @param message the message to be signed, as byte representation 100 * @param mode the transmission mode, either IMPLICIT or EXPLICIT 101 * @param hashAlgorithm the hash algorithm to be used 102 * @param signatureAlgorithm the signature algorithm to be used 103 * @param signerKey the private key of the signer 104 * @param certificates the certificate chain of the signer 105 * 106 * @return the DER encoding of the <code>ContentInfo</code> object just created 107 * 108 * @throws CMSException if the <code>SignedData</code>, <code>ContentInfo</code> 109 * object cannot be created 110 * @throws IOException if an I/O related error occurs 111 */ 112 public byte[] createSignedDataStream(byte[] message, 113 int mode, 114 AlgorithmID hashAlgorithm, 115 AlgorithmID signatureAlgorithm, 116 PrivateKey signerKey, 117 X509Certificate[] certificates) 118 throws CMSException, IOException { 119 120 System.out.print("Create a new message signed with " + signatureAlgorithm.getName()); 121 122 // we are testing the stream interface 123 ByteArrayInputStream is = new ByteArrayInputStream(message); 124 125 // the stream to which to write the SignedData 126 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 127 128 // wrap SignedData into a ContentInfo 129 ContentInfoOutputStream contentInfoStream = 130 new ContentInfoOutputStream(ObjectID.cms_signedData, resultStream); 131 SignedDataOutputStream signedData = new SignedDataOutputStream(contentInfoStream, mode); 132 133 // add the certificates 134 signedData.addCertificates(certificates); 135 136 // cert at index 0 is the user certificate 137 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]); 138 139 // create a new SignerInfo 140 AlgorithmID ecdsaSig = (AlgorithmID)signatureAlgorithm.clone(); 141 // CMS-ECDSA requires to encode the parameter field as NULL (see RFC 3278) 142 ecdsaSig.encodeAbsentParametersAsNull(true); 143 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), ecdsaSig, signerKey); 144 145 try { 146 // create some signed attributes 147 // the message digest attribute is automatically added 148 Attribute[] attributes = new Attribute[2]; 149 // content type is data 150 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 151 attributes[0] = new Attribute(contentType); 152 // signing time is now 153 SigningTime signingTime = new SigningTime(); 154 attributes[1] = new Attribute(signingTime); 155 156 // set the attributes 157 signer_info.setSignedAttributes(attributes); 158 } catch (Exception ex) { 159 throw new CMSException("Error adding attributes: " + ex.toString()); 160 } 161 162 // finish the creation of SignerInfo by calling method addSigner 163 try { 164 signedData.addSignerInfo(signer_info); 165 } catch (NoSuchAlgorithmException ex) { 166 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 167 } 168 169 int blockSize = 4; // in real world we would use a block size like 2048 170 // write in the data to be signed 171 byte[] buffer = new byte[blockSize]; 172 int bytesRead; 173 while ((bytesRead = is.read(buffer)) != -1) { 174 signedData.write(buffer, 0, bytesRead); 175 } 176 177 // closing the stream add the signer infos and closes the underlying stream 178 signedData.close(); 179 return resultStream.toByteArray(); 180 } 181 182 183 /** 184 * Parses a CMS <code>ContentInfo</code> object holding a <code>SignedData</code> 185 * object and verifies the signature. 186 * 187 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 188 * object as BER encoded byte array 189 * @param message the the message which was transmitted out-of-band (explicit signed) 190 * @param certificates the certificate of the signer (used for alternative signature verification) 191 * 192 * @return the inherent message as byte array 193 * 194 * @throws CMSException if any signature does not verify 195 * @throws IOException if an I/O related error occurs 196 */ 197 public byte[] getSignedDataStream(byte[] signedData, byte[] message, X509Certificate[] certificates) 198 throws CMSException, IOException { 199 200 // we are testing the stream interface 201 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 202 203 SignedDataStream signed_data = new SignedDataStream(is); 204 205 if (signed_data.getMode() == SignedDataStream.EXPLICIT) { 206 // in explicit mode explicitly supply the content for hash computation 207 signed_data.setInputStream(new ByteArrayInputStream(message)); 208 } 209 210 // get an InputStream for reading the signed content and update hash computation 211 InputStream data = signed_data.getInputStream(); 212 ByteArrayOutputStream os = new ByteArrayOutputStream(); 213 Util.copyStream(data, os, null); 214 215 System.out.println("SignedData contains the following signer information:"); 216 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 217 218 int numberOfSignerInfos = signer_infos.length; 219 if (numberOfSignerInfos == 0) { 220 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 221 System.err.println(warning); 222 throw new CMSException(warning); 223 } else { 224 for (int i = 0; i < numberOfSignerInfos; i++) { 225 try { 226 // verify the signed data using the SignerInfo at index i 227 X509Certificate signer_cert = signed_data.verify(i); 228 // if the signature is OK the certificate of the signer is returned 229 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 230 // check for some included attributes 231 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 232 if (signingTime != null) { 233 System.out.println("This message has been signed at " + signingTime.get()); 234 } 235 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 236 if (contentType != null) { 237 System.out.println("The content has CMS content type " + contentType.get().getName()); 238 } 239 } catch (SignatureException ex) { 240 // if the signature is not OK a SignatureException is thrown 241 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 242 throw new CMSException(ex.toString()); 243 } 244 } 245 // now check alternative signature verification 246 System.out.println("Now check the signature assuming that no certs have been included:"); 247 try { 248 SignerInfo signer_info = signed_data.verify(certificates[0]); 249 // if the signature is OK the certificate of the signer is returned 250 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 251 } catch (SignatureException ex) { 252 // if the signature is not OK a SignatureException is thrown 253 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN()); 254 throw new CMSException(ex.toString()); 255 } 256 // in practice we also would validate the signer certificate(s) 257 } 258 return os.toByteArray(); 259 } 260 261 262 263 /** 264 * Runs the signing - verifying demo. 265 * 266 * @param message the message to be signed 267 * @param hashAlgorithm the hash algorithm to be used 268 * @param signatureAlgorithm the signature algorithm to be used 269 * @param signerKeyAndCert private key and certificate chain of the signer 270 */ 271 public void runDemo(byte[] message, 272 AlgorithmID hashAlgorithm, 273 AlgorithmID signatureAlgorithm, 274 KeyAndCertificate signerKeyAndCert) 275 throws Exception { 276 277 PrivateKey signerKey = signerKeyAndCert.getPrivateKey(); 278 X509Certificate[] signerCerts = signerKeyAndCert.getCertificateChain(); 279 280 byte[] encodedSignedData; 281 byte[] received_message = null; 282 283 System.out.println("\nRun demos for " + hashAlgorithm.getName() + " / " + signatureAlgorithm.getName() + "\n"); 284 285 System.out.println("Stream implementation demos"); 286 System.out.println("==========================="); 287 // 288 // test CMS Implicit SignedDataStream 289 // 290 System.out.println("\nImplicit SignedDataStream demo [create]:\n"); 291 encodedSignedData = createSignedDataStream(message, 292 SignedDataStream.IMPLICIT, 293 hashAlgorithm, 294 signatureAlgorithm, 295 signerKey, 296 signerCerts); 297 System.out.println(); 298 299 // transmit data 300 System.out.println("\nImplicit SignedDataStream demo [parse]:\n"); 301 received_message = getSignedDataStream(encodedSignedData, null, signerCerts); 302 System.out.print("\nSigned content: "); 303 System.out.println(new String(received_message)); 304 305 // 306 // test CMS Explicit SignedDataStream 307 // 308 System.out.println("\nExplicit SignedDataStream demo [create]:\n"); 309 encodedSignedData = createSignedDataStream(message, 310 SignedDataStream.EXPLICIT, 311 hashAlgorithm, 312 signatureAlgorithm, 313 signerKey, 314 signerCerts); 315 // transmit data 316 System.out.println("\nExplicit SignedDataStream demo [parse]:\n"); 317 received_message = getSignedDataStream(encodedSignedData, message, signerCerts); 318 System.out.print("\nSigned content: "); 319 System.out.println(new String(received_message)); 320 321 // the non-stream implementation 322 System.out.println("\nNon-stream implementation demos"); 323 System.out.println("==============================="); 324 325 326 } 327 328 /** 329 * Tests the CMS SignedData implementation with the ECDSA signature 330 * algorithm and several hash algorithms. 331 */ 332 public void start() throws Exception { 333 334 // the test message 335 String m = "This is the test message."; 336 System.out.println("Test message: \""+m+"\""); 337 System.out.println(); 338 byte[] message = m.getBytes(); 339 340 AlgorithmID[][] algorithms = new AlgorithmID[][] { 341 { CMSAlgorithmID.sha1, CMSAlgorithmID.ecdsa_With_SHA1 }, 342 { CMSAlgorithmID.sha224, CMSAlgorithmID.ecdsa_With_SHA224 }, 343 { CMSAlgorithmID.sha256, CMSAlgorithmID.ecdsa_With_SHA256 }, 344 { CMSAlgorithmID.sha384, CMSAlgorithmID.ecdsa_With_SHA384 }, 345 { CMSAlgorithmID.sha512, CMSAlgorithmID.ecdsa_With_SHA512 }, 346 // ECDSA with RIPEMD-160 in plain format (BSI) 347 { CMSAlgorithmID.ripeMd160, CMSAlgorithmID.ecdsa_plain_With_RIPEMD160 }, 348 }; 349 350 // get signer key and certs 351 KeyAndCertificate[] keyAndCerts = { 352 // P-192 353 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN), 354 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN)), 355 // P-224 356 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_224_SIGN), 357 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_224_SIGN)), 358 // P-256 359 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN), 360 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_256_SIGN)), 361 // P-384 362 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN), 363 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_384_SIGN)), 364 // P-521 365 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN), 366 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_521_SIGN)), 367 // P-192 (for ECDSA with RIPEMD-160 in plain format (BSI) 368 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN), 369 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_192_SIGN)), 370 371 372 }; 373 374 final int HASH_ALG = 0; 375 final int SIGNATURE_ALG = 1; 376 for (int i = 0; i < algorithms.length; i++) { 377 runDemo(message, algorithms[i][HASH_ALG], algorithms[i][SIGNATURE_ALG], keyAndCerts[i]); 378 } 379 380 } 381 382 /** 383 * Starts the demo. 384 * 385 * @throws Exception 386 * if an error occurs 387 */ 388 public static void main(String argv[]) throws Exception { 389 390 DemoUtil.initDemos(); 391 ECCDemoUtil.installIaikEccProvider(); 392 (new ECDSASignedDataOutputStreamDemo()).start(); 393 System.out.println("\nReady!"); 394 System.in.read(); 395 } 396 397}