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