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/pkcs7cms/PKCS7CMSSignedDataDemo.java 24 12.02.25 17:58 Dbratko $ 059 // $Revision: 24 $ 060 // 061 062 package demo.cms.pkcs7cms; 063 064 import iaik.asn1.ASN1Object; 065 import iaik.asn1.CodingException; 066 import iaik.asn1.ObjectID; 067 import iaik.asn1.structures.AlgorithmID; 068 import iaik.asn1.structures.Attribute; 069 import iaik.asn1.structures.ChoiceOfTime; 070 import iaik.asn1.structures.PolicyInformation; 071 import iaik.asn1.structures.PolicyQualifierInfo; 072 import iaik.cms.CMSException; 073 import iaik.cms.IssuerAndSerialNumber; 074 import iaik.cms.SignedData; 075 import iaik.cms.SignedDataStream; 076 import iaik.cms.SignerInfo; 077 import iaik.cms.Utils; 078 import iaik.pkcs.PKCSException; 079 import iaik.smime.ess.SigningCertificate; 080 import iaik.utils.CryptoUtils; 081 import iaik.utils.Util; 082 import iaik.x509.X509Certificate; 083 import iaik.x509.attr.AttributeCertificate; 084 085 import java.io.ByteArrayInputStream; 086 import java.io.ByteArrayOutputStream; 087 import java.io.IOException; 088 import java.io.InputStream; 089 import java.security.NoSuchAlgorithmException; 090 import java.security.PrivateKey; 091 import java.security.SignatureException; 092 import java.security.cert.Certificate; 093 094 import demo.DemoUtil; 095 import demo.keystore.CMSKeyStore; 096 097 098 /** 099 * This class demonstrates the CMS SignedData implementation and checks it 100 * against the IAIK PKCS#7 library. 101 */ 102 public class PKCS7CMSSignedDataDemo { 103 104 // certificate of user 1 105 X509Certificate user1Cert_; 106 // private key of user 1 107 PrivateKey user1PrivKey_; 108 // certificate of user 2 109 X509Certificate user2Cert_; 110 // private key of user 2 111 PrivateKey user2PrivKey_; 112 // a certificate array containing the user certs + CA 113 X509Certificate[] certificates_; 114 // certificates of user1 115 X509Certificate[] user1Certs_; 116 117 118 /** 119 * Setup the demo certificate chains. 120 * 121 * Keys and certificate are retrieved from the demo KeyStore. 122 * 123 * @throws IOException if an file read error occurs 124 */ 125 public PKCS7CMSSignedDataDemo() throws IOException { 126 127 System.out.println(); 128 System.out.println("***********************************************************************************************"); 129 System.out.println("* PKCS7CMSSignedDataDemo *"); 130 System.out.println("* (tests the CMS SignedData against the IAIK-JCE PKCS#7 Signedata type implementation) *"); 131 System.out.println("***********************************************************************************************"); 132 System.out.println(); 133 134 // add all certificates to the list 135 user1Certs_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 136 user1Cert_ = (X509Certificate)user1Certs_[0]; 137 user1PrivKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 138 user2Cert_ = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1)[0]; 139 user2PrivKey_ = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 140 141 certificates_ = new X509Certificate[user1Certs_.length + 1]; 142 System.arraycopy(user1Certs_, 0, certificates_, 0, user1Certs_.length); 143 certificates_[user1Certs_.length] = user2Cert_; 144 145 } 146 147 148 149 /** 150 * Creates a CMS <code>SignedData</code> object. 151 * <p> 152 * 153 * @param message the message to be signed, as byte representation 154 * @param mode the transmission mode, either IMPLICIT or EXPLICIT 155 * 156 * @return the DER encoding of the <code>SignedData</code> object just created 157 * @throws CMSException if the <code>SignedData</code> object cannot 158 * be created 159 * @throws IOException if an I/O error occurs 160 */ 161 public byte[] createSignedDataStream(byte[] message, int mode) throws CMSException, IOException { 162 163 System.out.print("Create a new message signed by user 1 :"); 164 165 // we are testing the stream interface 166 ByteArrayInputStream is = new ByteArrayInputStream(message); 167 // create a new SignedData object which includes the data 168 SignedDataStream signed_data = new SignedDataStream(is, mode); 169 170 // SignedData shall include the certificate chain for verifying 171 signed_data.setCertificates(certificates_); 172 173 // cert at index 0 is the user certificate 174 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1Cert_); 175 176 // create a new SignerInfo 177 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_); 178 // create some authenticated attributes 179 // the message digest attribute is automatically added 180 Attribute[] attributes = new Attribute[3]; 181 // content type is data 182 attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data}); 183 // signing time is now 184 attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()}); 185 // signing certificate 186 try { 187 SigningCertificate signingCertificate = new SigningCertificate(user1Certs_, true); 188 String explicitText = "This certificate only may be used for test purposes"; 189 PolicyQualifierInfo policyQualifier = new PolicyQualifierInfo(null, null, explicitText); 190 PolicyInformation[] policyInformations = 191 { new PolicyInformation(new ObjectID("1.3.6.1.4.1.2706.17.0.11.1.1"), 192 new PolicyQualifierInfo[] { policyQualifier }) }; 193 signingCertificate.setPolicies(policyInformations); 194 System.out.println("Include signingCertificate attribute:"); 195 System.out.println(signingCertificate); 196 attributes[2] = new Attribute(ObjectID.signingCertificate, new ASN1Object[] {signingCertificate.toASN1Object()}); 197 } catch (Exception ex) { 198 throw new CMSException("Cannot create SigningCertificate attribute: " + ex.getMessage()); 199 } 200 // set the attributes 201 signer_info.setSignedAttributes(attributes); 202 // finish the creation of SignerInfo by calling method addSigner 203 try { 204 signed_data.addSignerInfo(signer_info); 205 // another SignerInfo without signed attributes 206 signer_info = new SignerInfo(new IssuerAndSerialNumber(user2Cert_), 207 (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_); 208 209 210 // the message digest itself is protected 211 signed_data.addSignerInfo(signer_info); 212 213 } catch (NoSuchAlgorithmException ex) { 214 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 215 } 216 217 // write the data through SignedData to any out-of-band place 218 if (mode == SignedDataStream.EXPLICIT) { 219 InputStream data_is = signed_data.getInputStream(); 220 byte[] buf = new byte[1024]; 221 int r; 222 while ((r = data_is.read(buf)) > 0) { 223 ; // skip data 224 } 225 } 226 227 // return the SignedData as DER encoded byte array with block size 2048 228 ByteArrayOutputStream os = new ByteArrayOutputStream(); 229 signed_data.writeTo(os, 2048); 230 return os.toByteArray(); 231 } 232 233 /** 234 * Creates a PKCS#7 <code>SignedData</code> object. 235 * <p> 236 * 237 * @param message the message to be signed, as byte representation 238 * @param mode the transmission mode, either IMPLICIT or EXPLICIT 239 * @return the DER encoding of the <code>SignedData</code> object just created 240 * @throws PKCSException if the <code>SignedData</code> object cannot 241 * be created 242 * @throws IOException if an I/O error occurs 243 */ 244 public byte[] createPKCS7SignedDataStream(byte[] message, int mode) throws iaik.pkcs.PKCSException, IOException { 245 246 // we are testing the stream interface 247 ByteArrayInputStream is = new ByteArrayInputStream(message); 248 // create a new SignedData object which includes the data 249 iaik.pkcs.pkcs7.SignedDataStream signed_data = new iaik.pkcs.pkcs7.SignedDataStream(is, mode); 250 // SignedData shall include the certificate chain for verifying 251 signed_data.setCertificates(certificates_); 252 253 // cert at index 0 is the user certificate 254 iaik.pkcs.pkcs7.IssuerAndSerialNumber issuer = new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user1Cert_); 255 256 // create a new SignerInfo 257 iaik.pkcs.pkcs7.SignerInfo signer_info = new iaik.pkcs.pkcs7.SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_); 258 // create some authenticated attributes 259 // the message digest attribute is automatically added 260 Attribute[] attributes = new Attribute[2]; 261 // content type is data 262 attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data}); 263 // signing time is now 264 attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()}); 265 // set the attributes 266 signer_info.setAuthenticatedAttributes(attributes); 267 // finish the creation of SignerInfo by calling method addSigner 268 try { 269 signed_data.addSignerInfo(signer_info); 270 271 // another SignerInfo without authenticated attributes 272 signer_info = new iaik.pkcs.pkcs7.SignerInfo(new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user2Cert_), 273 (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_); 274 // the message digest itself is protected 275 signed_data.addSignerInfo(signer_info); 276 277 } catch (NoSuchAlgorithmException ex) { 278 throw new iaik.pkcs.PKCSException("No implementation for signature algorithm: "+ex.getMessage()); 279 } 280 281 // write the data through SignedData to any out-of-band place 282 if (mode == iaik.pkcs.pkcs7.SignedDataStream.EXPLICIT) { 283 InputStream data_is = signed_data.getInputStream(); 284 byte[] buf = new byte[1024]; 285 int r; 286 while ((r = data_is.read(buf)) > 0) { 287 ; // skip data 288 } 289 } 290 291 // return the SignedData as DER encoded byte array with block size 2048 292 ByteArrayOutputStream os = new ByteArrayOutputStream(); 293 signed_data.writeTo(os, 2048); 294 return os.toByteArray(); 295 } 296 297 /** 298 * Parses a CMS or PKCS#7 <code>SignedData</code> object and verifies the signatures 299 * for all participated signers. 300 * 301 * @param signedData <code>SignedData</code> object as DER encoded byte array 302 * @param message the the message which was transmitted out-of-band (explicit signed) 303 * 304 * @return the inherent message as byte array 305 * @throws CMSException if any signature does not verify 306 * @throws IOException if an I/O error occurs 307 */ 308 public byte[] getSignedDataStream(byte[] signedData, byte[] message) throws CMSException, IOException { 309 310 // we are testing the stream interface 311 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 312 // create the SignedData object 313 SignedDataStream signed_data = null; 314 if (message == null) { 315 // implicitly signed; read the DER encoded object 316 signed_data = new SignedDataStream(is); 317 } 318 else { 319 // explicitly signed; set the data stream for digesting the message 320 AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() }; 321 signed_data = new SignedDataStream(new ByteArrayInputStream(message), algIDs); 322 323 } 324 325 // get an InputStream for reading the signed content 326 InputStream data = signed_data.getInputStream(); 327 ByteArrayOutputStream os = new ByteArrayOutputStream(); 328 Util.copyStream(data, os, null); 329 330 if (message != null) { 331 // if explicitly signed read now the DER encoded object 332 // an explicit S/MIME signed message also consits of message|signature 333 signed_data.decode(is); 334 } 335 336 System.out.println("SignedData contains the following signer information:"); 337 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 338 339 int numberOfSignerInfos = signer_infos.length; 340 if (numberOfSignerInfos == 0) { 341 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 342 System.err.println(warning); 343 throw new CMSException(warning); 344 } else { 345 for (int i = 0; i < numberOfSignerInfos; i++) { 346 347 try { 348 // verify the signed data using the SignerInfo at index i 349 X509Certificate signer_cert = signed_data.verify(i); 350 // if the signature is OK the certificate of the signer is returned 351 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 352 Attribute signingTime = signer_infos[i].getSignedAttribute(ObjectID.signingTime); 353 if (signingTime != null) { 354 ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]); 355 System.out.println("This message has been signed at " + cot.getDate()); 356 } 357 Attribute contentType = signer_infos[i].getSignedAttribute(ObjectID.contentType); 358 if (contentType != null) { 359 System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]); 360 } 361 // check SigningCertificate attribute 362 try { 363 SigningCertificate signingCertificate = signer_infos[i].getSigningCertificateAttribute(); 364 if (signingCertificate != null) { 365 System.out.println("SigningCertificate attribute included!"); 366 if (!signingCertificate.isSignerCertificate(signer_cert)) { 367 throw new CMSException("Cert ERROR!!! The certificate used for signing is not the one " + 368 "identified by the SignerCertificate attribute!"); 369 } else { 370 System.out.println("SigningCertificate attribute: Signer cert ok!"); 371 } 372 // get the authorization certs for this signerInfo 373 Certificate[] authCerts = 374 signingCertificate.getAuthorizedCertificates(signed_data.getCertificates()); 375 if (authCerts != null) { 376 System.out.println("SignedData contains the following authorization certs for SignerInfo No " + (i+1) +":"); 377 for (int j = 0; j < authCerts.length; j++) { 378 if (authCerts[j].getType().equalsIgnoreCase("X.509")) { 379 System.out.println("X.509 public key cert: " + ((X509Certificate)authCerts[j]).getSubjectDN()); 380 } else { 381 System.out.println("X.509 attribute cert: " + ((AttributeCertificate)authCerts[j]).getHolder()); 382 } 383 } 384 } 385 if (signingCertificate.countPolicies() > 0) { 386 // get the certs with PolicyInformations according to the SigningCertificate attribute: 387 Certificate[] policyCerts = 388 signingCertificate.getPolicyInformationCerts(signed_data.getCertificates()); 389 if (policyCerts != null) { 390 System.out.println("SignedData contains the following certs corresponding to policy informations of SignerInfo No " + (i+1) +":"); 391 for (int j = 0; j < policyCerts.length; j++) { 392 if (policyCerts[j].getType().equalsIgnoreCase("X.509")) { 393 System.out.println("X.509 public key cert: " + ((X509Certificate)policyCerts[j]).getSubjectDN()); 394 } else { 395 System.out.println("X.509 attribute cert: " + ((AttributeCertificate)policyCerts[j]).getHolder()); 396 } 397 } 398 } 399 } 400 } 401 } catch (NoSuchAlgorithmException ex) { 402 throw new CMSException("Cannot check SigningCertificate attribute: Algorithm SHA not implemented!"); 403 } catch (CMSException ex) { 404 throw new CMSException("Error parsing SigningCertificate attribute: " + ex.getMessage()); 405 } 406 407 } catch (SignatureException ex) { 408 // if the signature is not OK a SignatureException is thrown 409 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 410 throw new CMSException(ex.toString()); 411 } catch (CodingException ex) { 412 throw new CMSException("Attribute decoding error: " + ex.toString()); 413 } 414 } 415 416 // now check alternative signature verification 417 System.out.println("Now check the signature assuming that no certs have been included:"); 418 try { 419 SignerInfo signer_info = signed_data.verify(user1Cert_); 420 // if the signature is OK the certificate of the signer is returned 421 System.out.println("Signature OK from signer: "+user1Cert_.getSubjectDN()); 422 423 } catch (SignatureException ex) { 424 // if the signature is not OK a SignatureException is thrown 425 System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN()); 426 throw new CMSException(ex.toString()); 427 } 428 429 430 try { 431 SignerInfo signer_info = signed_data.verify(user2Cert_); 432 // if the signature is OK the certificate of the signer is returned 433 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 434 435 } catch (SignatureException ex) { 436 // if the signature is not OK a SignatureException is thrown 437 System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN()); 438 throw new CMSException(ex.toString()); 439 } 440 // in practice we also would validate the signer certificate(s) 441 } 442 443 return os.toByteArray(); 444 } 445 446 447 /** 448 * Parses a PKCS#7 <code>SignedData</code> object and verifies the signatures 449 * for all participated signers. 450 * 451 * @param signedData <code>SignedData</code> object as DER encoded byte array 452 * @param message the the message which was transmitted out-of-band (explicit signed) 453 * 454 * @return the inherent message as byte array 455 * @throws iaik.pkcs.PKCSException if any signature does not verify 456 * @throws IOException if an I/O error occurs 457 */ 458 public byte[] getPKCS7SignedDataStream(byte[] signedData, byte[] message) throws iaik.pkcs.PKCSException, IOException { 459 460 // we are testing the stream interface 461 ByteArrayInputStream is = new ByteArrayInputStream(signedData); 462 // create the SignedData object 463 iaik.pkcs.pkcs7.SignedDataStream signed_data = null; 464 if (message == null) { 465 // implicitly signed; read the DER encoded object 466 signed_data = new iaik.pkcs.pkcs7.SignedDataStream(is); 467 } 468 else { 469 // explicitly signed; set the data stream for digesting the message 470 AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() }; 471 signed_data = new iaik.pkcs.pkcs7.SignedDataStream(new ByteArrayInputStream(message), algIDs); 472 473 } 474 475 // get an InputStream for reading the signed content 476 InputStream data = signed_data.getInputStream(); 477 ByteArrayOutputStream os = new ByteArrayOutputStream(); 478 Util.copyStream(data, os, null); 479 480 if (message != null) { 481 // if explicitly signed read now the DER encoded object 482 // an explicit S/MIME signed message also consits of message|signature 483 signed_data.decode(is); 484 } 485 486 System.out.println("SignedData contains the following signer information:"); 487 iaik.pkcs.pkcs7.SignerInfo[] signer_infos = signed_data.getSignerInfos(); 488 489 int numberOfSignerInfos = signer_infos.length; 490 if (numberOfSignerInfos == 0) { 491 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 492 System.err.println(warning); 493 throw new PKCSException(warning); 494 } else { 495 for (int i = 0; i < numberOfSignerInfos; i++) { 496 try { 497 // verify the signed data using the SignerInfo at index i 498 X509Certificate signer_cert = signed_data.verify(i); 499 // if the signature is OK the certificate of the signer is returned 500 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 501 Attribute signingTime = signer_infos[i].getAuthenticatedAttribute(ObjectID.signingTime); 502 if (signingTime != null) { 503 ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]); 504 System.out.println("This message has been signed at " + cot.getDate()); 505 } 506 Attribute contentType = signer_infos[i].getAuthenticatedAttribute(ObjectID.contentType); 507 if (contentType != null) { 508 System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]); 509 } 510 511 } catch (SignatureException ex) { 512 // if the signature is not OK a SignatureException is thrown 513 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN()); 514 throw new iaik.pkcs.PKCSException(ex.toString()); 515 } catch (CodingException ex) { 516 throw new iaik.pkcs.PKCSException("Attribute decoding error: " + ex.toString()); 517 } 518 } 519 // now check alternative signature verification 520 System.out.println("Now check the signature assuming that no certs have been included:"); 521 try { 522 iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user1Cert_); 523 // if the signature is OK the certificate of the signer is returned 524 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN()); 525 526 } catch (SignatureException ex) { 527 // if the signature is not OK a SignatureException is thrown 528 System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN()); 529 throw new iaik.pkcs.PKCSException(ex.toString()); 530 } 531 532 try { 533 iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user2Cert_); 534 // if the signature is OK the certificate of the signer is returned 535 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN()); 536 537 } catch (SignatureException ex) { 538 // if the signature is not OK a SignatureException is thrown 539 System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN()); 540 throw new iaik.pkcs.PKCSException(ex.toString()); 541 } 542 // in practice we also would validate the signer certificate(s) 543 } 544 545 return os.toByteArray(); 546 } 547 548 549 550 /** 551 * Creates a CMS <code>SignedData</code> object. 552 * <p> 553 * 554 * @param message the message to be signed, as byte representation 555 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT 556 * 557 * @return the <code>SignedData</code> object as ASN.1 object 558 * @throws CMSException if the <code>SignedData</code> object cannot 559 * be created 560 * @throws IOException if an I/O error occurs 561 */ 562 public ASN1Object createSignedData(byte[] message, int mode) throws CMSException, IOException { 563 564 System.out.println("Create a new message signed by user 1 :"); 565 566 // create a new SignedData object which includes the data 567 SignedData signed_data = new SignedData(message, mode); 568 569 // SignedData shall include the certificate chain for verifying 570 signed_data.setCertificates(certificates_); 571 572 // cert at index 0 is the user certificate 573 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1Cert_); 574 575 // create a new SignerInfo 576 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_); 577 // create some authenticated attributes 578 // the message digest attribute is automatically added 579 Attribute[] attributes = new Attribute[3]; 580 // content type is data 581 attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data}); 582 // signing time is now 583 attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()}); 584 // signing certificate 585 SigningCertificate signingCertificate = Utils.makeSigningCertificate(user1Certs_, null, true); 586 System.out.println("Include signingCertificate attribute:"); 587 System.out.println(signingCertificate); 588 attributes[2] = new Attribute(ObjectID.signingCertificate, new ASN1Object[] {signingCertificate.toASN1Object()}); 589 // set the attributes 590 signer_info.setSignedAttributes(attributes); 591 // finish the creation of SignerInfo by calling method addSigner 592 try { 593 signed_data.addSignerInfo(signer_info); 594 595 // another SignerInfo without signed attributes 596 signer_info = new SignerInfo(new IssuerAndSerialNumber(user2Cert_), 597 (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_); 598 signed_data.addSignerInfo(signer_info); 599 600 } catch (NoSuchAlgorithmException ex) { 601 throw new CMSException("No implementation for signature algorithm: "+ex.getMessage()); 602 } 603 604 605 return signed_data.toASN1Object(); 606 } 607 608 609 /** 610 * Creates a PKCS#7 <code>SignedData</code> object. 611 * <p> 612 * 613 * @param message the message to be signed, as byte representation 614 * @param mode the mode, either SignedData.IMPLICIT or SignedData.EXPLICIT 615 * @return the <code>SignedData</code> object as ASN.1 object 616 * @throws iaik.pkcs.PKCSException if the <code>SignedData</code> object cannot 617 * be created 618 * @throws IOException if an I/O error occurs 619 */ 620 public ASN1Object createPKCS7SignedData(byte[] message, int mode) throws iaik.pkcs.PKCSException, IOException { 621 622 System.out.println("Create a new message signed by user 1 and 2:"); 623 624 625 // create a new SignedData object which includes the data 626 iaik.pkcs.pkcs7.SignedData signed_data = new iaik.pkcs.pkcs7.SignedData(message, mode); 627 // SignedData shall include the certificate chain for verifying 628 signed_data.setCertificates(certificates_); 629 630 // cert at index 0 is the user certificate 631 iaik.pkcs.pkcs7.IssuerAndSerialNumber issuer = new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user1Cert_); 632 633 // create a new SignerInfo 634 iaik.pkcs.pkcs7.SignerInfo signer_info = new iaik.pkcs.pkcs7.SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1PrivKey_); 635 // create some authenticated attributes 636 // the message digest attribute is automatically added 637 Attribute[] attributes = new Attribute[2]; 638 // content type is data 639 attributes[0] = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.pkcs7_data}); 640 // signing time is now 641 attributes[1] = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime().toASN1Object()}); 642 // set the attributes 643 signer_info.setAuthenticatedAttributes(attributes); 644 // finish the creation of SignerInfo by calling method addSigner 645 try { 646 signed_data.addSignerInfo(signer_info); 647 648 // another SignerInfo without authenticated attributes and 649 signer_info = new iaik.pkcs.pkcs7.SignerInfo(new iaik.pkcs.pkcs7.IssuerAndSerialNumber(user2Cert_), 650 (AlgorithmID)AlgorithmID.sha256.clone(), user2PrivKey_); 651 // the message digest itself is protected 652 signed_data.addSignerInfo(signer_info); 653 654 } catch (NoSuchAlgorithmException ex) { 655 throw new iaik.pkcs.PKCSException("No implementation for signature algorithm: "+ex.getMessage()); 656 } 657 658 659 return signed_data.toASN1Object(); 660 } 661 662 663 /** 664 * Parses a CMS or PKCS#7 <code>SignedData</code> object and verifies the signatures 665 * for all participated signers. 666 * 667 * @param obj <code>SignedData</code> object in ASN.1 representation 668 * @param message the the message which was transmitted out-of-band (explicit signed) 669 * 670 * @return the inherent message as byte array 671 * @throws CMSException if any signature does not verify 672 * @throws IOException if an I/O error occurs 673 */ 674 public byte[] getSignedData(ASN1Object obj, byte[] message) throws CMSException, IOException { 675 676 // create the SignedData object 677 SignedData signed_data = null; 678 if (message == null) { 679 // implicitly signed; read the DER encoded object 680 signed_data = new SignedData(obj); 681 } 682 else { 683 // explicitly signed; set the data stream for digesting the message 684 AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() }; 685 try { 686 signed_data = new SignedData(message, algIDs); 687 } catch (NoSuchAlgorithmException ex) { 688 throw new CMSException(ex.getMessage()); 689 } 690 } 691 692 // get an InputStream for reading the signed content 693 InputStream data = signed_data.getInputStream(); 694 ByteArrayOutputStream os = new ByteArrayOutputStream(); 695 Util.copyStream(data, os, null); 696 697 if (message != null) { 698 // if explicitly signed read now the DER encoded object 699 // an explicit S/MIME signed message also consists of message|signature 700 signed_data.decode(obj); 701 } 702 703 System.out.println("SignedData contains the following signer information:"); 704 SignerInfo[] signer_infos = signed_data.getSignerInfos(); 705 706 int numberOfSignerInfos = signer_infos.length; 707 if (numberOfSignerInfos == 0) { 708 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 709 System.err.println(warning); 710 throw new CMSException(warning); 711 } else { 712 for (int i = 0; i < numberOfSignerInfos; i++) { 713 try { 714 // verify the signed data using the SignerInfo at index i 715 X509Certificate signer_cert = signed_data.verify(i); 716 // if the signature is OK the certificate of the signer is returned 717 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 718 Attribute signingTime = signer_infos[i].getSignedAttribute(ObjectID.signingTime); 719 if (signingTime != null) { 720 ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]); 721 System.out.println("This message has been signed at " + cot.getDate()); 722 } 723 Attribute contentType = signer_infos[i].getSignedAttribute(ObjectID.contentType); 724 if (contentType != null) { 725 System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]); 726 } 727 Attribute signingCertificateAttr = signer_infos[i].getSignedAttribute(ObjectID.signingCertificate); 728 if (signingCertificateAttr != null) { 729 System.out.println("SigningCertificate attribute included in this SignerInfo."); 730 SigningCertificate signingCertificate = new SigningCertificate(signingCertificateAttr.getValue()[0]); 731 byte[] certHash; 732 try { 733 certHash = signer_cert.getFingerprint("SHA"); 734 if (!CryptoUtils.equalsBlock(certHash, signingCertificate.getESSCertIDs()[0].getCertHash())) { 735 System.out.println("Cert ERROR!!! The certificate used for signing is not the one " + 736 "identified by the SignerCertificate attribute!"); 737 738 } else { 739 System.out.println("SigningCertificate cert hash of Signer cert ok!"); 740 } 741 } catch (NoSuchAlgorithmException ex) { 742 throw new CMSException("Cannot check SigningCertificate: Algorithm SHA not implemented!"); 743 } 744 } 745 } catch (SignatureException ex) { 746 // if the signature is not OK a SignatureException is thrown 747 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getSignerIdentifier()).getSubjectDN()); 748 throw new CMSException(ex.toString()); 749 } catch (CodingException ex) { 750 throw new CMSException("Attribute decoding error: " + ex.toString()); 751 } 752 } 753 754 // now check alternative signature verification 755 System.out.println("Now check the signature assuming that no certs have been included:"); 756 try { 757 SignerInfo signer_info = signed_data.verify(user1Cert_); 758 // if the signature is OK the certificate of the signer is returned 759 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 760 761 } catch (SignatureException ex) { 762 // if the signature is not OK a SignatureException is thrown 763 System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN()); 764 throw new CMSException(ex.toString()); 765 } 766 767 try { 768 SignerInfo signer_info = signed_data.verify(user2Cert_); 769 // if the signature is OK the certificate of the signer is returned 770 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getSignerIdentifier()).getSubjectDN()); 771 772 } catch (SignatureException ex) { 773 // if the signature is not OK a SignatureException is thrown 774 System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN()); 775 throw new CMSException(ex.toString()); 776 } 777 778 // in practice we also would validate the signer certificate(s) 779 } 780 return signed_data.getContent(); 781 } 782 783 /** 784 * Parses a PKCS#7 <code>SignedData</code> object and verifies the signatures 785 * for all participated signers. 786 * 787 * @param obj <code>SignedData</code> object in ASN.1 representation 788 * @param message the the message which was transmitted out-of-band (explicit signed) 789 * 790 * @return the inherent message as byte array 791 * @throws PKCSException if any signature does not verify 792 * @throws IOException if an I/O error occurs 793 */ 794 public byte[] getPKCS7SignedData(ASN1Object obj, byte[] message) throws iaik.pkcs.PKCSException, IOException { 795 796 // create the SignedData object 797 iaik.pkcs.pkcs7.SignedData signed_data = null; 798 if (message == null) { 799 // implicitly signed; read the DER encoded object 800 signed_data = new iaik.pkcs.pkcs7.SignedData(obj); 801 } 802 else { 803 // explicitly signed; set the data stream for digesting the message 804 AlgorithmID[] algIDs = { (AlgorithmID)AlgorithmID.sha256.clone() }; 805 try { 806 signed_data = new iaik.pkcs.pkcs7.SignedData(message, algIDs); 807 } catch (NoSuchAlgorithmException ex) { 808 throw new iaik.pkcs.PKCSException(ex.toString()); 809 } 810 } 811 812 // get an InputStream for reading the signed content 813 InputStream data = signed_data.getInputStream(); 814 ByteArrayOutputStream os = new ByteArrayOutputStream(); 815 Util.copyStream(data, os, null); 816 817 if (message != null) { 818 // if explicitly signed read now the DER encoded object 819 // an explicit S/MIME signed message also consits of message|signature 820 signed_data.decode(obj); 821 } 822 823 System.out.println("SignedData contains the following signer information:"); 824 iaik.pkcs.pkcs7.SignerInfo[] signer_infos = signed_data.getSignerInfos(); 825 826 int numberOfSignerInfos = signer_infos.length; 827 if (numberOfSignerInfos == 0) { 828 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 829 System.err.println(warning); 830 throw new PKCSException(warning); 831 } else { 832 for (int i = 0; i < numberOfSignerInfos; i++) { 833 try { 834 // verify the signed data using the SignerInfo at index i 835 X509Certificate signer_cert = signed_data.verify(i); 836 // if the signature is OK the certificate of the signer is returned 837 System.out.println("Signature OK from signer: "+signer_cert.getSubjectDN()); 838 Attribute signingTime = signer_infos[i].getAuthenticatedAttribute(ObjectID.signingTime); 839 if (signingTime != null) { 840 ChoiceOfTime cot = new ChoiceOfTime(signingTime.getValue()[0]); 841 System.out.println("This message has been signed at " + cot.getDate()); 842 } 843 Attribute contentType = signer_infos[i].getAuthenticatedAttribute(ObjectID.contentType); 844 if (contentType != null) { 845 System.out.println("The content has PKCS#7 content type " + contentType.getValue()[0]); 846 } 847 } catch (SignatureException ex) { 848 // if the signature is not OK a SignatureException is thrown 849 System.out.println("Signature ERROR from signer: "+signed_data.getCertificate(signer_infos[i].getIssuerAndSerialNumber()).getSubjectDN()); 850 throw new iaik.pkcs.PKCSException(ex.toString()); 851 } catch (CodingException ex) { 852 throw new iaik.pkcs.PKCSException("Attribute decoding error: " + ex.toString()); 853 } 854 } 855 856 // now check alternative signature verification 857 System.out.println("Now check the signature assuming that no certs have been included:"); 858 try { 859 iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user1Cert_); 860 // if the signature is OK the certificate of the signer is returned 861 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN()); 862 863 } catch (SignatureException ex) { 864 // if the signature is not OK a SignatureException is thrown 865 System.out.println("Signature ERROR from signer: "+user1Cert_.getSubjectDN()); 866 throw new iaik.pkcs.PKCSException(ex.toString()); 867 } 868 869 try { 870 iaik.pkcs.pkcs7.SignerInfo signer_info = signed_data.verify(user2Cert_); 871 // if the signature is OK the certificate of the signer is returned 872 System.out.println("Signature OK from signer: "+signed_data.getCertificate(signer_info.getIssuerAndSerialNumber()).getSubjectDN()); 873 874 } catch (SignatureException ex) { 875 // if the signature is not OK a SignatureException is thrown 876 System.out.println("Signature ERROR from signer: "+user2Cert_.getSubjectDN()); 877 throw new iaik.pkcs.PKCSException(ex.toString()); 878 } 879 // in practice we also would validate the signer certificate(s) 880 } 881 882 return signed_data.getContent(); 883 } 884 885 886 /** 887 * Tests the CMS SignedData implementation and checks it against the 888 * IAIK PKCS#7 library. 889 */ 890 public void start() { 891 // the test message 892 String m = "This is the test message."; 893 System.out.println("Test message: \""+m+"\""); 894 System.out.println(); 895 byte[] message = m.getBytes(); 896 897 try { 898 byte[] data; 899 byte[] received_message = null; 900 System.out.println("Stream implementation demos"); 901 System.out.println("==========================="); 902 903 904 905 System.out.println("\nChecking against PKCS#7..."); 906 907 // 908 // Implicit SignedDataStream: CMS (create), PKCS#7 (parse) 909 // 910 System.out.println("\nCreating implicit CMS SignedDataStream: \n"); 911 data = createSignedDataStream(message, SignedDataStream.IMPLICIT); 912 // transmit data 913 System.out.println("\nParse implicit CMS SignedDataStream with PKCS#7:\n"); 914 received_message = getPKCS7SignedDataStream(data, null); 915 System.out.print("\nSigned content: "); 916 System.out.println(new String(received_message)); 917 918 // 919 // Explicit SignedDataStream: CMS (create), PKCS#7 (parse) 920 // 921 System.out.println("\nCreating explicit CMS SignedDataStream: \n"); 922 data = createSignedDataStream(message, SignedDataStream.EXPLICIT); 923 // transmit data 924 System.out.println("\nParse explicit CMS SignedDataStream SignedDataStream with PKCS#7:\n"); 925 received_message = getPKCS7SignedDataStream(data, message); 926 System.out.print("\nSigned content: "); 927 System.out.println(new String(received_message)); 928 929 // 930 // Implicit SignedDataStream: PKCS#7 (create), CMS (parse) 931 // 932 System.out.println("\nCreating implicit PKCS#7 SignedDataStream: \n"); 933 data = createPKCS7SignedDataStream(message, SignedDataStream.IMPLICIT); 934 // transmit data 935 System.out.println("\nParse implicit PKCS#7 SignedDataStream with CMS:\n"); 936 received_message = getSignedDataStream(data, null); 937 System.out.print("\nSigned content: "); 938 System.out.println(new String(received_message)); 939 940 // 941 // Explicit SignedDataStream: CMS (create), PKCS#7 (parse) 942 // 943 System.out.println("\nCreating explicit CMS SignedDataStream: \n"); 944 data = createSignedDataStream(message, SignedDataStream.EXPLICIT); 945 // transmit data 946 System.out.println("\nParse explicit SignedDataStream SignedDataStream with PKCS#7:\n"); 947 received_message = getPKCS7SignedDataStream(data, message); 948 System.out.print("\nSigned content: "); 949 System.out.println(new String(received_message)); 950 951 952 // the non-stream implementation 953 System.out.println("\nNon-stream implementation demos"); 954 System.out.println("==============================="); 955 956 // 957 // test PKCS#7 Data 958 // 959 ASN1Object obj = null; 960 961 System.out.println("\nChecking against PKCS#7..."); 962 963 // 964 // Implicit SignedData: CMS (create), PKCS#7 (parse) 965 // 966 System.out.println("\nCreating implicit CMS SignedData: \n"); 967 obj = createSignedData(message, SignedData.IMPLICIT); 968 // transmit data 969 System.out.println("\nParsing implicit CMS SignedData with PKCS#7:\n"); 970 received_message = getPKCS7SignedData(obj, null); 971 System.out.print("\nSigned content: "); 972 System.out.println(new String(received_message)); 973 974 // 975 // Explicit SignedData: CMS (create), PKCS#7 (parse) 976 // 977 System.out.println("\nCreating explicit CMS SignedData: \n"); 978 obj = createSignedData(message, SignedData.EXPLICIT); 979 // transmit data 980 System.out.println("\nParsing explicit CMS SignedData with PKCS#7:\n"); 981 received_message = getPKCS7SignedData(obj, message); 982 System.out.print("\nSigned content: "); 983 System.out.println(new String(received_message)); 984 985 986 // 987 // Implicit SignedData: PKCS#7 (create), CMS (parse) 988 // 989 System.out.println("\nCreating implicit PCSK#7 SignedData: \n"); 990 obj = createPKCS7SignedData(message, SignedData.IMPLICIT); 991 // transmit data 992 System.out.println("\nParsing implicit PKCS#7 SignedData with CMS:\n"); 993 received_message = getSignedData(obj, null); 994 System.out.print("\nSigned content: "); 995 System.out.println(new String(received_message)); 996 997 // 998 // Explicit SignedData: PKCS#7 (create), CMS (parse) 999 // 1000 System.out.println("\nCreating implicit PKCS#7 SignedData: \n"); 1001 obj = createPKCS7SignedData(message, SignedData.EXPLICIT); 1002 // transmit data 1003 System.out.println("\nParsing explicit PKCS#7 SignedData with CMS:\n"); 1004 received_message = getSignedData(obj, message); 1005 System.out.print("\nSigned content: "); 1006 System.out.println(new String(received_message)); 1007 1008 } catch (Exception ex) { 1009 ex.printStackTrace(); 1010 throw new RuntimeException(ex.toString()); 1011 } 1012 } 1013 1014 /** 1015 * Main method. 1016 * 1017 * @throws IOException 1018 * if an I/O error occurs when reading required keys 1019 * and certificates from files 1020 */ 1021 public static void main(String argv[]) throws Exception { 1022 1023 DemoUtil.initDemos(); 1024 1025 (new PKCS7CMSSignedDataDemo()).start(); 1026 System.out.println("\nReady!"); 1027 DemoUtil.waitKey(); 1028 } 1029 }