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/EdDSASignedDataOutputStreamDemo.java 6 12.02.25 17:58 Dbratko $ 059 // $Revision: 6 $ 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 EdDSA (Ed25519, Ed448) 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 EdDSASignedDataOutputStreamDemo { 111 112 /** 113 * Default Constructor. 114 */ 115 public EdDSASignedDataOutputStreamDemo() throws Exception { 116 System.out.println(); 117 System.out.println("**********************************************************************************"); 118 System.out.println("* EdDSASignedData demo *"); 119 System.out.println("* (shows how to use the SignedData(Stream) implementation with EdDSA) *"); 120 System.out.println("**********************************************************************************"); 121 System.out.println(); 122 123 } 124 125 /** 126 * Creates an EdDSA signed CMS <code>SignedDataOutputStream</code> object and wraps it by a 127 * CMS <code>ContentInfoOutputStream</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 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 167 // cert at index 0 is the user certificate 168 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(certificates[0]); 169 170 // create a new SignerInfo 171 AlgorithmID eddsaSig = (AlgorithmID)signatureAlgorithm.clone(); 172 eddsaSig.encodeAbsentParametersAsNull(false); 173 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)hashAlgorithm.clone(), eddsaSig, 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 * Tests the CMS SignedData implementation with the ECDSA signature 359 * algorithm and several hash algorithms. 360 */ 361 public void start() throws Exception { 362 363 // the test message 364 String m = "This is the test message."; 365 System.out.println("Test message: \""+m+"\""); 366 System.out.println(); 367 byte[] message = m.getBytes(); 368 369 AlgorithmID[][] algorithms = new AlgorithmID[][] { 370 { CMSAlgorithmID.sha512, CMSAlgorithmID.ed25519}, 371 { CMSAlgorithmID.shake256Len, CMSAlgorithmID.ed448 }, 372 }; 373 374 // get signer key and certs 375 KeyAndCertificate[] keyAndCerts = { 376 // ed25519 377 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519), 378 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED25519)), 379 // ed448 380 new KeyAndCertificate(CMSEccKeyStore.getPrivateKey(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448), 381 CMSEccKeyStore.getCertificateChain(CMSEccKeyStore.ECDSA, CMSEccKeyStore.SZ_ED448)), 382 383 }; 384 385 final int HASH_ALG = 0; 386 final int SIGNATURE_ALG = 1; 387 for (int i = 0; i < algorithms.length; i++) { 388 runDemo(message, algorithms[i][HASH_ALG], algorithms[i][SIGNATURE_ALG], keyAndCerts[i]); 389 } 390 391 } 392 393 /** 394 * Starts the demo. 395 * 396 * @throws Exception 397 * if an error occurs 398 */ 399 public static void main(String argv[]) throws Exception { 400 401 DemoUtil.initDemos(); 402 ECCDemoUtil.installIaikEccelerateProvider(); 403 (new EdDSASignedDataOutputStreamDemo()).start(); 404 System.out.println("\nReady!"); 405 System.in.read(); 406 } 407 408 }