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