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 }