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/EdDSASignedDataDemo.java 4 12.02.25 17:58 Dbratko $ 029// $Revision: 4 $ 030// 031 032package demo.cms.ecc; 033 034import iaik.asn1.ObjectID; 035import iaik.asn1.structures.AlgorithmID; 036import iaik.asn1.structures.Attribute; 037import iaik.cms.CMSAlgorithmID; 038import iaik.cms.CMSException; 039import iaik.cms.ContentInfo; 040import iaik.cms.ContentInfoStream; 041import iaik.cms.IssuerAndSerialNumber; 042import iaik.cms.SignedData; 043import iaik.cms.SignedDataStream; 044import iaik.cms.SignerInfo; 045import iaik.cms.attributes.CMSContentType; 046import iaik.cms.attributes.SigningTime; 047import iaik.utils.KeyAndCertificate; 048import iaik.utils.Util; 049import iaik.x509.X509Certificate; 050 051import java.io.ByteArrayInputStream; 052import java.io.ByteArrayOutputStream; 053import java.io.IOException; 054import java.io.InputStream; 055import java.security.NoSuchAlgorithmException; 056import java.security.PrivateKey; 057import java.security.SignatureException; 058 059import demo.DemoUtil; 060import demo.cms.ecc.keystore.CMSEccKeyStore; 061 062 063/** 064 * This class demonstrates the IAIK-CMS SignedData(Stream) implementation 065 * with the EdDSA (Ed25519, Ed448) signature algorithm. 066 * <p> 067 * Any keys/certificates required for this demo are read from a keystore 068 * file "cmsecc.keystore" located in your current working directory. If 069 * the keystore file does not exist you can create it by running the 070 * {@link demo.cms.ecc.keystore.SetupCMSEccKeyStore SetupCMSEccKeyStore} 071 * program. 072 * <p> 073 * Additionally to <code>iaik_cms.jar</code> you also must have 074 * <code>iaik_jce_(full).jar</code> (IAIK-JCE, <a href = 075 * "https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank"> 076 * https://sic.tech/products/core-crypto-toolkits/jca-jce/</a>), 077 * and <code>iaik_eccelarate.jar</code> (IAIK-ECCelerate<sup><small>TM</small></sup>, <a href = 078 * "https://sic.tech/products/core-crypto-toolkits/eccelerate/" target="_blank"> 079 * https://sic.tech/products/core-crypto-toolkits/eccelerate/</a>) 080 * in your classpath. 081 */ 082public class EdDSASignedDataDemo { 083 084 /** 085 * Default Constructor. 086 */ 087 public EdDSASignedDataDemo() throws Exception { 088 System.out.println(); 089 System.out.println("**********************************************************************************"); 090 System.out.println("* EdDSASignedData demo *"); 091 System.out.println("* (shows how to use the SignedData(Stream) implementation with EdDSA) *"); 092 System.out.println("**********************************************************************************"); 093 System.out.println(); 094 095 } 096 097 /** 098 * Creates an EdDSA signed CMS <code>SignedDataStream</code> object and wraps it by a 099 * CMS <code>ContentInfoStream</code>. 100 * 101 * @param message the message to be signed, as byte representation 102 * @param mode the transmission mode, either IMPLICIT or EXPLICIT 103 * @param hashAlgorithm the hash algorithm to be used 104 * @param signatureAlgorithm the signature algorithm to be used 105 * @param signerKey the private key of the signer 106 * @param certificates the certificate chain of the signer 107 * 108 * @return the DER encoding of the <code>ContentInfo</code> object just created 109 * 110 * @throws CMSException if the <code>SignedData</code>, <code>ContentInfo</code> 111 * object cannot be created 112 * @throws IOException if an I/O related error occurs 113 */ 114 public byte[] createSignedDataStream(byte[] message, 115 int mode, 116 AlgorithmID hashAlgorithm, 117 AlgorithmID signatureAlgorithm, 118 PrivateKey signerKey, 119 X509Certificate[] certificates) 120 throws CMSException, IOException { 121 122 System.out.print("Create a new message signed with " + signatureAlgorithm.getName()); 123 124 // we are testing the stream interface 125 ByteArrayInputStream is = new ByteArrayInputStream(message); 126 // create a new SignedData object which includes the data 127 SignedDataStream signed_data = new SignedDataStream(is, mode); 128 129 // SignedData shall include the certificate chain for verifying 130 signed_data.setCertificates(certificates); 131 132 // cert at index 0 is the user certificate 133 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]); 134 135 // create a new SignerInfo 136 AlgorithmID eddsaSig = (AlgorithmID)signatureAlgorithm.clone(); 137 eddsaSig.encodeAbsentParametersAsNull(false); 138 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), eddsaSig, signerKey); 139 140 try { 141 // create some signed attributes 142 // the message digest attribute is automatically added 143 Attribute[] attributes = new Attribute[2]; 144 // content type is data 145 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 146 attributes[0] = new Attribute(contentType); 147 // signing time is now 148 SigningTime signingTime = new SigningTime(); 149 attributes[1] = new Attribute(signingTime); 150 151 // set the attributes 152 signer_info.setSignedAttributes(attributes); 153 } catch (Exception ex) { 154 throw new CMSException("Error adding attributes: " + ex.toString()); 155 } 156 157 // finish the creation of SignerInfo by calling method addSigner 158 try { 159 signed_data.addSignerInfo(signer_info); 160 } catch (NoSuchAlgorithmException ex) { 161 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 162 } 163 164 // write the data through SignedData to any out-of-band place 165 if (mode == SignedDataStream.EXPLICIT) { 166 InputStream data_is = signed_data.getInputStream(); 167 byte[] buf = new byte[1024]; 168 int r; 169 while ((r = data_is.read(buf)) > 0) { 170 ; // skip data 171 } 172 } 173 174 signed_data.setBlockSize(2048); 175 // create the ContentInfo 176 ContentInfoStream cis = new ContentInfoStream(signed_data); 177 // return the SignedData as DER encoded byte array with block size 2048 178 ByteArrayOutputStream os = new ByteArrayOutputStream(); 179 cis.writeTo(os); 180 return os.toByteArray(); 181 } 182 183 184 /** 185 * Parses a CMS <code>ContentInfo</code> object holding a <code>SignedData</code> 186 * object and verifies the signature. 187 * 188 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 189 * object as BER encoded byte array 190 * @param message the the message which was transmitted out-of-band (explicit signed) 191 * @param certificates the certificate of the signer (used for alternative signature verification) 192 * 193 * @return the inherent message as byte array 194 * 195 * @throws CMSException if any signature does not verify 196 * @throws IOException if an I/O related error occurs 197 */ 198 public byte[] getSignedDataStream(byte[] signedData, byte[] message, X509Certificate[] certificates) 199 throws CMSException, IOException { 200 201 // we are testing the stream interface 202 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 203 204 SignedDataStream signed_data = new SignedDataStream(is); 205 206 if (signed_data.getMode() == SignedDataStream.EXPLICIT) { 207 // in explicit mode explicitly supply the content for hash computation 208 signed_data.setInputStream(new ByteArrayInputStream(message)); 209 } 210 211 // get an InputStream for reading the signed content and update hash computation 212 InputStream data = signed_data.getInputStream(); 213 ByteArrayOutputStream os = new ByteArrayOutputStream(); 214 Util.copyStream(data, os, null); 215 216 System.out.println("SignedData contains the following signer information:"); 217 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 218 219 int numberOfSignerInfos = signer_infos.length; 220 if (numberOfSignerInfos == 0) { 221 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 222 System.err.println(warning); 223 throw new CMSException(warning); 224 } else { 225 for (int i = 0; i < numberOfSignerInfos; i++) { 226 try { 227 // verify the signed data using the SignerInfo at index i 228 X509Certificate signer_cert = signed_data.verify(i); 229 // if the signature is OK the certificate of the signer is returned 230 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 231 // check for some included attributes 232 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 233 if (signingTime != null) { 234 System.out.println("This message has been signed at " + signingTime.get()); 235 } 236 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 237 if (contentType != null) { 238 System.out.println("The content has CMS content type " + contentType.get().getName()); 239 } 240 } catch (SignatureException ex) { 241 // if the signature is not OK a SignatureException is thrown 242 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 243 throw new CMSException(ex.toString()); 244 } 245 } 246 // now check alternative signature verification 247 System.out.println("Now check the signature assuming that no certs have been included:"); 248 try { 249 SignerInfo signer_info = signed_data.verify(certificates[0]); 250 // if the signature is OK the certificate of the signer is returned 251 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 252 } catch (SignatureException ex) { 253 // if the signature is not OK a SignatureException is thrown 254 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN()); 255 throw new CMSException(ex.toString()); 256 } 257 // in practice we also would validate the signer certificate(s) 258 } 259 return os.toByteArray(); 260 } 261 262 263 /** 264 * Creates an EdDSA signed CMS <code>SignedData</code> object and wraps it by a CMS 265 * <code>ContentInfo</code> object. 266 * <p> 267 * 268 * @param message the message to be signed, as byte representation 269 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT 270 * @param hashAlgorithm the hash algorithm to be used 271 * @param signatureAlgorithm the signature algorithm to be used 272 * @param signerKey the private key of the signer 273 * @param certificates the certificate chain of the signer 274 * 275 * @return the DER encoded <code>SignedData</code>-<code>ContentInfo</code> object 276 * 277 * @throws CMSException if the <code>SignedData</code>-<code>ContentInfo</code> object cannot 278 * be created 279 * @throws IOException if an I/O related error occurs 280 */ 281 public byte[] createSignedData(byte[] message, 282 int mode, 283 AlgorithmID hashAlgorithm, 284 AlgorithmID signatureAlgorithm, 285 PrivateKey signerKey, 286 X509Certificate[] certificates) 287 throws CMSException, IOException { 288 289 System.out.println("Create a new message signed with " + signatureAlgorithm.getName()); 290 291 // create a new SignedData object which includes the data 292 SignedData signed_data = new SignedData(message, mode); 293 294 // SignedData shall include the certificate chain for verifying 295 signed_data.setCertificates(certificates); 296 297 // cert at index 0 is the user certificate 298 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]); 299 300 // create a new SignerInfo 301 AlgorithmID eddsaSig = (AlgorithmID)signatureAlgorithm.clone(); 302 eddsaSig.encodeAbsentParametersAsNull(false); 303 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), eddsaSig, signerKey); 304 305 try { 306 // create some signed attributes 307 // the message digest attribute is automatically added 308 Attribute[] attributes = new Attribute[2]; 309 // content type is data 310 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 311 attributes[0] = new Attribute(contentType); 312 // signing time is now 313 SigningTime signingTime = new SigningTime(); 314 attributes[1] = new Attribute(signingTime); 315 316 // set the attributes 317 signer_info.setSignedAttributes(attributes); 318 } catch (Exception ex) { 319 throw new CMSException("Error adding attributes: " + ex.toString()); 320 } 321 322 // finish the creation of SignerInfo by calling method addSigner 323 try { 324 signed_data.addSignerInfo(signer_info); 325 } catch (NoSuchAlgorithmException ex) { 326 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 327 } 328 329 ContentInfo ci = new ContentInfo(signed_data); 330 return ci.getEncoded(); 331 } 332 333 334 /** 335 * Parses a CMS <code>ContentInfo</code> holding a <code>SignedData</code> 336 * object and verifies the signature. 337 * 338 * @param signedData the <code>ContentInfo</code> holding the <code>SignedData</code> 339 * object as DER encoded byte array 340 * @param message the message which was transmitted out-of-band (explicit signed) 341 * @param certificates the certificate of the signer (used for alternative signature verification) 342 * 343 * @return the inherent message as byte array 344 * 345 * @throws CMSException if any signature does not verify 346 * @throws IOException if an I/O related error occurs 347 */ 348 public byte[] getSignedData(byte[] signedData, byte[] message, X509Certificate[] certificates) 349 throws CMSException, IOException { 350 351 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 352 // create the SignedData object 353 SignedData signed_data = new SignedData(is); 354 355 if (signed_data.getMode() == SignedData.EXPLICIT) { 356 // in explcit mode explictly supply the content data to do the hash calculation 357 signed_data.setContent(message); 358 } 359 360 System.out.println("SignedData contains the following signer information:"); 361 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 362 363 int numberOfSignerInfos = signer_infos.length; 364 if (numberOfSignerInfos == 0) { 365 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 366 System.err.println(warning); 367 throw new CMSException(warning); 368 } else { 369 for (int i = 0; i < numberOfSignerInfos; i++) { 370 try { 371 // verify the signed data using the SignerInfo at index i 372 X509Certificate signer_cert = signed_data.verify(i); 373 // if the signature is OK the certificate of the signer is returned 374 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 375 // check some attributes 376 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 377 if (signingTime != null) { 378 System.out.println("This message has been signed at " + signingTime.get()); 379 } 380 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 381 if (contentType != null) { 382 System.out.println("The content has CMS content type " + contentType.get().getName()); 383 } 384 } catch (SignatureException ex) { 385 // if the signature is not OK a SignatureException is thrown 386 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 387 throw new CMSException(ex.toString()); 388 } 389 } 390 391 // now check alternative signature verification 392 System.out.println("Now check the signature assuming that no certs have been included:"); 393 try { 394 SignerInfo signer_info = signed_data.verify(certificates[0]); 395 // if the signature is OK the certificate of the signer is returned 396 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 397 } catch (SignatureException ex) { 398 // if the signature is not OK a SignatureException is thrown 399 System.out.println("Signature ERROR from signer: "+certificates[0].getSubjectDN()); 400 throw new CMSException(ex.toString()); 401 } 402 // in practice we also would validate the signer certificate(s) 403 } 404 return signed_data.getContent(); 405 } 406 407 /** 408 * Runs the signing - verifying demo. 409 * 410 * @param message the message to be signed 411 * @param hashAlgorithm the hash algorithm to be used 412 * @param signatureAlgorithm the signature algorithm to be used 413 * @param signerKeyAndCert private key and certificate chain of the signer 414 */ 415 public void runDemo(byte[] message, 416 AlgorithmID hashAlgorithm, 417 AlgorithmID signatureAlgorithm, 418 KeyAndCertificate signerKeyAndCert) 419 throws Exception { 420 421 PrivateKey signerKey = signerKeyAndCert.getPrivateKey(); 422 X509Certificate[] signerCerts = signerKeyAndCert.getCertificateChain(); 423 424 byte[] encodedSignedData; 425 byte[] received_message = null; 426 427 System.out.println("\nRun demos for " + hashAlgorithm.getName() + " / " + signatureAlgorithm.getName() + "\n"); 428 429 System.out.println("Stream implementation demos"); 430 System.out.println("==========================="); 431 // 432 // test CMS Implicit SignedDataStream 433 // 434 System.out.println("\nImplicit SignedDataStream demo [create]:\n"); 435 encodedSignedData = createSignedDataStream(message, 436 SignedDataStream.IMPLICIT, 437 hashAlgorithm, 438 signatureAlgorithm, 439 signerKey, 440 signerCerts); 441 System.out.println(); 442 // transmit data 443 System.out.println("\nImplicit SignedDataStream demo [parse]:\n"); 444 received_message = getSignedDataStream(encodedSignedData, null, signerCerts); 445 System.out.print("\nSigned content: "); 446 System.out.println(new String(received_message)); 447 448 // 449 // test CMS Explicit SignedDataStream 450 // 451 System.out.println("\nExplicit SignedDataStream demo [create]:\n"); 452 encodedSignedData = createSignedDataStream(message, 453 SignedDataStream.EXPLICIT, 454 hashAlgorithm, 455 signatureAlgorithm, 456 signerKey, 457 signerCerts); 458 // transmit data 459 System.out.println("\nExplicit SignedDataStream demo [parse]:\n"); 460 received_message = getSignedDataStream(encodedSignedData, message, signerCerts); 461 System.out.print("\nSigned content: "); 462 System.out.println(new String(received_message)); 463 464 // the non-stream implementation 465 System.out.println("\nNon-stream implementation demos"); 466 System.out.println("==============================="); 467 468 // 469 // test CMS Implicit SignedData 470 // 471 System.out.println("\nImplicit CMS SignedData demo [create]:\n"); 472 encodedSignedData = createSignedData(message, 473 SignedData.IMPLICIT, 474 hashAlgorithm, 475 signatureAlgorithm, 476 signerKey, 477 signerCerts); 478 // transmit data 479 System.out.println("\nImplicit CMS SignedData demo [parse]:\n"); 480 received_message = getSignedData(encodedSignedData, null, signerCerts); 481 System.out.print("\nSigned content: "); 482 System.out.println(new String(received_message)); 483 484 // 485 // test CMS Explicit SignedData 486 // 487 System.out.println("\nExplicit CMS SignedData demo [create]:\n"); 488 encodedSignedData = createSignedData(message, 489 SignedData.EXPLICIT, 490 hashAlgorithm, 491 signatureAlgorithm, 492 signerKey, 493 signerCerts); 494 // transmit data 495 System.out.println("\nExplicit CMS SignedData demo [parse]:\n"); 496 received_message = getSignedData(encodedSignedData, message, signerCerts); 497 System.out.print("\nSigned content: "); 498 System.out.println(new String(received_message)); 499 500 } 501 502 /** 503 * Tests the CMS SignedData implementation with the ECDSA signature 504 * algorithm and several hash algorithms. 505 */ 506 public void start() throws Exception { 507 508 // the test message 509 String m = "This is the test message."; 510 System.out.println("Test message: \""+m+"\""); 511 System.out.println(); 512 byte[] message = m.getBytes(); 513 514 AlgorithmID[][] algorithms = new AlgorithmID[][] { 515 { CMSAlgorithmID.sha512, CMSAlgorithmID.ed25519}, 516 { CMSAlgorithmID.shake256Len, CMSAlgorithmID.ed448 }, 517 }; 518 519 // get signer key and certs 520 KeyAndCertificate[] keyAndCerts = { 521 // ed25519 522 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519), 523 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519)), 524 // ed448 525 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448), 526 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448)), 527 528 }; 529 530 final int HASH_ALG = 0; 531 final int SIGNATURE_ALG = 1; 532 for (int i = 0; i < algorithms.length; i++) { 533 runDemo(message, algorithms[i][HASH_ALG], algorithms[i][SIGNATURE_ALG], keyAndCerts[i]); 534 } 535 536 } 537 538 /** 539 * Starts the demo. 540 * 541 * @throws Exception 542 * if an error occurs 543 */ 544 public static void main(String argv[]) throws Exception { 545 546 DemoUtil.initDemos(); 547 ECCDemoUtil.installIaikEccelerateProvider(); 548 (new EdDSASignedDataDemo()).start(); 549 System.out.println("\nReady!"); 550 System.in.read(); 551 } 552 553}