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/signedData/SHA2withDSASignedDataDemo.java 5 12.02.25 17:58 Dbratko $ 059 // $Revision: 5 $ 060 // 061 062 package demo.cms.signedData; 063 064 import iaik.asn1.ObjectID; 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.asn1.structures.Attribute; 067 import iaik.cms.CMSException; 068 import iaik.cms.IssuerAndSerialNumber; 069 import iaik.cms.SignedData; 070 import iaik.cms.SignedDataStream; 071 import iaik.cms.SignerInfo; 072 import iaik.cms.attributes.CMSContentType; 073 import iaik.cms.attributes.SigningTime; 074 import iaik.utils.Util; 075 import iaik.x509.X509Certificate; 076 077 import java.io.ByteArrayInputStream; 078 import java.io.ByteArrayOutputStream; 079 import java.io.IOException; 080 import java.io.InputStream; 081 import java.security.NoSuchAlgorithmException; 082 import java.security.PrivateKey; 083 import java.security.SignatureException; 084 085 import demo.DemoUtil; 086 import demo.keystore.CMSKeyStore; 087 088 089 /** 090 * Demonstrates the usage of class {@link iaik.cms.SignedDataStream} and 091 * {@link iaik.cms.SignedData} for signing some data using the CMS type 092 * SignedData with SHA2withDSA signature algorithm according to FIPS 186-3. 093 */ 094 public class SHA2withDSASignedDataDemo { 095 096 // The private key of the signer. 097 PrivateKey signerKey_; 098 099 // The certificate chain of the signer. 100 X509Certificate[] signerCertificates_; 101 102 103 104 /** 105 * Setups the demo certificate chains. 106 * 107 * Keys and certificate are retrieved from the demo KeyStore. 108 * 109 * @throws IOException if an file read error occurs 110 */ 111 public SHA2withDSASignedDataDemo() throws IOException { 112 113 System.out.println(); 114 System.out.println("**********************************************************************************"); 115 System.out.println("* SHA2withDSASignedDataDemo *"); 116 System.out.println("* (shows the usage of the CMS SignedData type with SHA2withDSA) *"); 117 System.out.println("**********************************************************************************"); 118 System.out.println(); 119 120 // add all certificates to the list 121 signerCertificates_ = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN); 122 signerKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN); 123 } 124 125 /** 126 * Creates a CMS <code>SignedData</code> object. 127 * <p> 128 * 129 * @param message the message to be signed, as byte representation 130 * @param mode the transmission mode, either IMPLICIT or EXPLICIT 131 * @return the BER encoding of the <code>SignedData</code> object just created 132 * @throws CMSException if the <code>SignedData</code> object cannot 133 * be created 134 * @throws IOException if some stream I/O error occurs 135 */ 136 public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException { 137 138 System.out.println("Create a new message signed by " + signerCertificates_[0].getSubjectDN()); 139 140 // we are testing the stream interface 141 ByteArrayInputStream is = new ByteArrayInputStream(message); 142 // create a new SignedData object which includes the data 143 SignedDataStream signed_data = new SignedDataStream(is, mode); 144 145 // SignedData shall include the certificate chain for verifying 146 signed_data.setCertificates(signerCertificates_); 147 148 // cert at index 0 is the signer certificate 149 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(signerCertificates_[0]); 150 151 // create a new SignerInfo 152 SignerInfo signer_info = new SignerInfo(issuer, 153 (AlgorithmID)AlgorithmID.sha256.clone(), 154 (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(), 155 signerKey_); 156 // create some authenticated attributes 157 // the message digest attribute is automatically added 158 Attribute[] attributes = new Attribute[2]; 159 try { 160 // content type is data 161 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 162 attributes[0] = new Attribute(contentType); 163 // signing time is now 164 SigningTime signingTime = new SigningTime(); 165 attributes[1] = new Attribute(signingTime); 166 167 } catch (Exception ex) { 168 throw new CMSException("Error creating attribute: " + ex.toString()); 169 } 170 // set the attributes 171 signer_info.setSignedAttributes(attributes); 172 // finish the creation of SignerInfo by calling method addSigner 173 try { 174 signed_data.addSignerInfo(signer_info); 175 } catch (NoSuchAlgorithmException ex) { 176 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 177 } 178 179 // write the data through SignedData to any out-of-band place 180 if (mode == SignedDataStream.EXPLICIT) { 181 InputStream data_is = signed_data.getInputStream(); 182 byte[] buf = new byte[1024]; 183 int r; 184 while ((r = data_is.read(buf)) > 0) { 185 ; // skip data 186 } 187 } 188 189 // return the SignedData as DER encoded byte array with block size 2048 190 ByteArrayOutputStream os = new ByteArrayOutputStream(); 191 signed_data.writeTo(os, 2048); 192 return os.toByteArray(); 193 } 194 195 196 /** 197 * Parses a CMS <code>SignedData</code> object and verifies the signatures 198 * for all participated signers. 199 * 200 * @param signedData <code>SignedData</code> object as BER encoded byte array 201 * @param message the the message which was transmitted out-of-band (explicit signed) 202 * 203 * @return the inherent message as byte array 204 * @throws CMSException if any signature does not verify 205 * @throws IOException if some stream I/O error occurs 206 */ 207 public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException { 208 209 // we are testing the stream interface 210 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 211 // create the SignedData object 212 SignedDataStream signed_data = new SignedDataStream(is); 213 214 if (signed_data.getMode() == SignedDataStream.EXPLICIT) { 215 // in explicit mode explicitly supply the content for hash computation 216 signed_data.setInputStream(new ByteArrayInputStream(message)); 217 } 218 219 // get an InputStream for reading the signed content 220 InputStream data = signed_data.getInputStream(); 221 ByteArrayOutputStream os = new ByteArrayOutputStream(); 222 Util.copyStream(data, os, null); 223 224 System.out.println("SignedData contains the following signer information:"); 225 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 226 227 int numberOfSignerInfos = signer_infos.length; 228 if (numberOfSignerInfos == 0) { 229 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 230 System.err.println(warning); 231 throw new CMSException(warning); 232 } else { 233 for (int i = 0; i < numberOfSignerInfos; i++) { 234 AlgorithmID signatureAlgorithm = signer_infos[i].getSignatureAlgorithm(); 235 try { 236 // verify the signed data using the SignerInfo at index i 237 X509Certificate signer_cert = signed_data.verify(i); 238 // if the signature is OK the certificate of the signer is returned 239 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signer_cert.getSubjectDN()); 240 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 241 if (signingTime != null) { 242 System.out.println("This message has been signed at " + signingTime.get()); 243 } 244 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 245 if (contentType != null) { 246 System.out.println("The content has CMS content type " + contentType.get().getName()); 247 } 248 } catch (SignatureException ex) { 249 // if the signature is not OK a SignatureException is thrown 250 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 251 throw new CMSException(ex.toString()); 252 } 253 } 254 255 // now check alternative signature verification 256 System.out.println("Now check the signature assuming that no certs have been included:"); 257 try { 258 SignerInfo signer_info = signed_data.verify(signerCertificates_[0]); 259 AlgorithmID signatureAlgorithm = signer_info.getSignatureAlgorithm(); 260 // if the signature is OK the certificate of the signer is returned 261 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signerCertificates_[0].getSubjectDN()); 262 263 } catch (SignatureException ex) { 264 // if the signature is not OK a SignatureException is thrown 265 System.out.println("Signature ERROR from signer: "+signerCertificates_[0].getSubjectDN()); 266 throw new CMSException(ex.toString()); 267 } 268 // in practice we also would validate the signer certificate(s) 269 } 270 return os.toByteArray(); 271 } 272 273 274 275 /** 276 * Creates a CMS <code>SignedData</code> object. 277 * <p> 278 * 279 * @param message the message to be signed, as byte representation 280 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT 281 * @return the DER encoded <code>SignedData</code> object 282 * @throws CMSException if the <code>SignedData</code> object cannot 283 * be created 284 */ 285 public byte[] createSignedData(byte[] message, int mode) throws CMSException { 286 287 System.out.println("Create a new message signed by " + signerCertificates_[0].getSubjectDN()); 288 289 // create a new SignedData object which includes the data 290 SignedData signed_data = new SignedData(message, mode); 291 292 // SignedData shall include the certificate chain for verifying 293 signed_data.setCertificates(signerCertificates_); 294 295 // cert at index 0 is the signer certificate 296 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(signerCertificates_[0]); 297 298 // create a new SignerInfo 299 SignerInfo signer_info = new SignerInfo(issuer, 300 (AlgorithmID)AlgorithmID.sha256.clone(), 301 (AlgorithmID)AlgorithmID.dsaWithSHA256.clone(), 302 signerKey_); 303 // create some authenticated attributes 304 // the message digest attribute is automatically added 305 Attribute[] attributes = new Attribute[2]; 306 try { 307 // content type is data 308 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 309 attributes[0] = new Attribute(contentType); 310 // signing time is now 311 SigningTime signingTime = new SigningTime(); 312 attributes[1] = new Attribute(signingTime); 313 // signing certificate 314 } catch (Exception ex) { 315 throw new CMSException("Error creating attribute: " + ex.toString()); 316 } 317 // set the attributes 318 signer_info.setSignedAttributes(attributes); 319 // finish the creation of SignerInfo by calling method addSigner 320 try { 321 signed_data.addSignerInfo(signer_info); 322 } catch (NoSuchAlgorithmException ex) { 323 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 324 } 325 return signed_data.getEncoded(); 326 } 327 328 329 /** 330 * Parses a CMS <code>SignedData</code> object and verifies the signatures 331 * for all participated signers. 332 * 333 * @param encoding the DER encoded <code>SignedData</code> object 334 * @param message the the message which was transmitted out-of-band (explicit signed) 335 * 336 * @return the inherent message as byte array 337 * @throws CMSException if any signature does not verify 338 * @throws IOException if some stream I/O error occurs 339 */ 340 public byte[] getSignedData(byte[] encoding, byte[] message) throws CMSException, IOException { 341 342 ByteArrayInputStream encodedStream = new ByteArrayInputStream(encoding); 343 // create the SignedData object 344 SignedData signed_data = new SignedData(encodedStream); 345 346 if (signed_data.getMode() == SignedData.EXPLICIT) { 347 // in explcit mode explictly supply the content data to do the hash calculation 348 signed_data.setContent(message); 349 } 350 351 System.out.println("SignedData contains the following signer information:"); 352 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 353 354 int numberOfSignerInfos = signer_infos.length; 355 if (numberOfSignerInfos == 0) { 356 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 357 System.err.println(warning); 358 throw new CMSException(warning); 359 } else { 360 for (int i = 0; i < numberOfSignerInfos; i++) { 361 AlgorithmID signatureAlgorithm = signer_infos[i].getSignatureAlgorithm(); 362 try { 363 // verify the signed data using the SignerInfo at index i 364 X509Certificate signer_cert = signed_data.verify(i); 365 // if the signature is OK the certificate of the signer is returned 366 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signer_cert.getSubjectDN()); 367 SigningTime signingTime = (SigningTime)signer_infos[i].getSignedAttributeValue(ObjectID.signingTime); 368 if (signingTime != null) { 369 System.out.println("This message has been signed at " + signingTime.get()); 370 } 371 CMSContentType contentType = (CMSContentType)signer_infos[i].getSignedAttributeValue(ObjectID.contentType); 372 if (contentType != null) { 373 System.out.println("The content has CMS content type " + contentType.get().getName()); 374 } 375 } catch (SignatureException ex) { 376 // if the signature is not OK a SignatureException is thrown 377 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 378 throw new CMSException(ex.toString()); 379 } 380 } 381 382 // now check alternative signature verification 383 System.out.println("Now check the signature assuming that no certs have been included:"); 384 try { 385 SignerInfo signer_info = signed_data.verify(signerCertificates_[0]); 386 AlgorithmID signatureAlgorithm = signer_info.getSignatureAlgorithm(); 387 // if the signature is OK the certificate of the signer is returned 388 System.out.println("Signature (" + signatureAlgorithm.getName() + ") OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 389 390 } catch (SignatureException ex) { 391 // if the signature is not OK a SignatureException is thrown 392 System.out.println("Signature ERROR from signer: "+signerCertificates_[0].getSubjectDN()); 393 throw new CMSException(ex.toString()); 394 } 395 // in practice we also would validate the signer certificate(s) 396 } 397 398 return signed_data.getContent(); 399 } 400 401 /** 402 * Tests the CMS SignedData implementation. 403 */ 404 public void start() { 405 // the test message 406 String m = "This is the test message."; 407 System.out.println("Test message: \""+m+"\""); 408 System.out.println(); 409 byte[] message = m.getBytes(); 410 411 try { 412 byte[] encoding; 413 byte[] received_message = null; 414 System.out.println("Stream implementation demos"); 415 System.out.println("==========================="); 416 // 417 // test CMS Implicit SignedDataStream 418 // 419 System.out.println("\nImplicit SignedDataStream demo [create]:\n"); 420 encoding = createSignedDataStream(message, SignedDataStream.IMPLICIT); 421 // transmit data 422 System.out.println("\nImplicit SignedDataStream demo [parse]:\n"); 423 received_message = getSignedDataStream(encoding, null); 424 System.out.print("\nSigned content: "); 425 System.out.println(new String(received_message)); 426 427 // 428 // test CMS Explicit SignedDataStream 429 // 430 System.out.println("\nExplicit SignedDataStream demo [create]:\n"); 431 encoding = createSignedDataStream(message, SignedDataStream.EXPLICIT); 432 // transmit data 433 System.out.println("\nExplicit SignedDataStream demo [parse]:\n"); 434 received_message = getSignedDataStream(encoding, message); 435 System.out.print("\nSigned content: "); 436 System.out.println(new String(received_message)); 437 438 // the non-stream implementation 439 System.out.println("\nNon-stream implementation demos"); 440 System.out.println("==============================="); 441 442 // 443 // test CMS Implicit SignedData 444 // 445 System.out.println("\nImplicit CMS SignedData demo [create]:\n"); 446 encoding = createSignedData(message, SignedData.IMPLICIT); 447 // transmit data 448 System.out.println("\nImplicit CMS SignedData demo [parse]:\n"); 449 received_message = getSignedData(encoding, null); 450 System.out.print("\nSigned content: "); 451 System.out.println(new String(received_message)); 452 453 // 454 // test CMS Explicit SignedData 455 // 456 System.out.println("\nExplicit CMS SignedData demo [create]:\n"); 457 encoding = createSignedData(message, SignedData.EXPLICIT); 458 // transmit data 459 System.out.println("\nExplicit CMS SignedData demo [parse]:\n"); 460 received_message = getSignedData(encoding, message); 461 System.out.print("\nSigned content: "); 462 System.out.println(new String(received_message)); 463 464 } catch (Exception ex) { 465 ex.printStackTrace(); 466 throw new RuntimeException(ex.toString()); 467 } 468 } 469 470 /** 471 * The main method. 472 * 473 * @throws IOException 474 * if an I/O error occurs when reading required keys 475 * and certificates from files 476 */ 477 public static void main(String argv[]) throws Exception { 478 479 DemoUtil.initDemos(); 480 (new SHA2withDSASignedDataDemo()).start(); 481 System.out.println("\nReady!"); 482 DemoUtil.waitKey(); 483 } 484 }