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/signedAndEnvelopedData/SignedAndEnvelopedDataDemo.java 26 12.02.25 17:58 Dbratko $ 059 // $Revision: 26 $ 060 // 061 062 package demo.cms.signedAndEnvelopedData; 063 064 import iaik.asn1.ObjectID; 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.asn1.structures.Attribute; 067 import iaik.cms.EnvelopedData; 068 import iaik.cms.EnvelopedDataStream; 069 import iaik.cms.IssuerAndSerialNumber; 070 import iaik.cms.KeyTransRecipientInfo; 071 import iaik.cms.RecipientInfo; 072 import iaik.cms.SignedData; 073 import iaik.cms.SignedDataStream; 074 import iaik.cms.SignerInfo; 075 import iaik.cms.attributes.CMSContentType; 076 import iaik.cms.attributes.SigningTime; 077 import iaik.utils.Util; 078 import iaik.x509.X509Certificate; 079 080 import java.io.BufferedInputStream; 081 import java.io.BufferedOutputStream; 082 import java.io.ByteArrayInputStream; 083 import java.io.ByteArrayOutputStream; 084 import java.io.IOException; 085 import java.io.InputStream; 086 import java.io.OutputStream; 087 import java.io.PipedInputStream; 088 import java.io.PipedOutputStream; 089 import java.security.PrivateKey; 090 091 import demo.DemoUtil; 092 import demo.keystore.CMSKeyStore; 093 094 /** 095 * This class shows the sequential combination of the SignedData and EnvelopedData 096 * implementations. 097 * <p> 098 * All keys and certificates are read from a keystore created by the 099 * SetupCMSKeyStore program. 100 */ 101 public class SignedAndEnvelopedDataDemo { 102 103 // signing certificate of user 1 104 X509Certificate user1_sign; 105 // signing private key of user 1 106 PrivateKey user1_sign_pk; 107 // encryption certificate of user 1 108 X509Certificate user1_crypt; 109 // encryption private key of user 1 110 PrivateKey user1_crypt_pk; 111 // encryption certificate of user 2 112 X509Certificate user2_crypt; 113 // encryption private key of user 2 114 PrivateKey user2_crypt_pk; 115 // a certificate chain containing the user certs + CA 116 X509Certificate[] certificates; 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 SignedAndEnvelopedDataDemo() throws IOException { 126 127 System.out.println(); 128 System.out.println("*************************************************************************************************"); 129 System.out.println("* SignedAndEnvelopedDataDemo *"); 130 System.out.println("* (shows the usage of the combined SignedData(Stream) and EnvelopedData(Stream) implementation) *"); 131 System.out.println("*************************************************************************************************"); 132 System.out.println(); 133 134 // add all certificates to the list 135 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 136 user1_sign = certs[0]; 137 user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 138 user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 139 user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 140 user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 141 user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 142 // user1 also includes her/his encryption certificate 143 certificates = new X509Certificate[certs.length+1]; 144 System.arraycopy(certs, 0, certificates, 0, certs.length); 145 certificates[certs.length] = user1_crypt; 146 147 } 148 149 /** 150 * Uses the stream based SignedData and EnvelopedData implementations 151 * (<code>SignedDataStream</code>, <code>EnvelopedDataStream</code> to 152 * sign and envelope the given message, encode the CMS object, decodes it 153 * again, removes the envlope and verifies the signature. 154 * 155 * @param message the message to be signed and enveloped 156 * @throws Exception if an error occurs 157 */ 158 public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception { 159 // repository for the signed and enveloped message 160 byte[] signed_enveloped_message; 161 // the InputStream containing the data to sign and encrypt 162 InputStream is = new BufferedInputStream(new ByteArrayInputStream(message)); 163 // the OutputStream where the data shall be written to 164 ByteArrayOutputStream out = new ByteArrayOutputStream(); 165 OutputStream os = new BufferedOutputStream(out); 166 167 168 // create an implicit signed message (signature contains message) 169 SignedDataStream signed = new SignedDataStream(is, SignedDataStream.IMPLICIT); 170 171 // these certificates are sent within the signature 172 signed.setCertificates(certificates); 173 174 // add one signer 175 // cert at index 0 is the user certificate 176 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign); 177 178 // create a new SignerInfo 179 SignerInfo signer_info = new SignerInfo(issuer, AlgorithmID.sha256, user1_sign_pk); 180 181 // create some signed attributes 182 // the message digest attribute is automatically added 183 Attribute[] attributes = new Attribute[2]; 184 // content type is data 185 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 186 attributes[0] = new Attribute(contentType); 187 // signing time is now 188 SigningTime signingTime = new SigningTime(); 189 attributes[1] = new Attribute(signingTime); 190 191 // set the attributes 192 signer_info.setSignedAttributes(attributes); 193 // finish the creation of SignerInfo by calling method addSigner 194 signed.addSignerInfo(signer_info); 195 196 // we have to sign and encrypt => connect 2 streams 197 PipedOutputStream piped_out = new PipedOutputStream(); 198 PipedInputStream piped_in = new PipedInputStream(piped_out); 199 // a new Thread between the 2 streams 200 Writer writer = new Writer(signed, piped_out); 201 writer.start(); 202 203 // encrypt with AES 204 EnvelopedDataStream enveloped = new EnvelopedDataStream(piped_in, (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 205 // add recipients where the symmetric key is encrypted with RSA 206 // create the recipient infos 207 RecipientInfo[] recipients = new RecipientInfo[2]; 208 // user1 is the first receiver 209 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 210 // user2 is the second receiver 211 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 212 213 // specify the recipients of the encrypted message 214 enveloped.setRecipientInfos(recipients); 215 216 // encrypt and write the data to the output stream 217 enveloped.writeTo(os,2048); 218 // finished 219 os.close(); 220 is.close(); 221 // get the signed and encrypted message from the ByteArrayOutputStream 222 signed_enveloped_message = out.toByteArray(); 223 System.out.println("Message created, now doing the parsing..."); 224 // and now decrypt the data and verify the signature 225 is = new BufferedInputStream(new ByteArrayInputStream( signed_enveloped_message)); 226 enveloped = new EnvelopedDataStream(is); 227 // use this private key to decrypt the symmetric key of recipient 0 228 enveloped.setupCipher(user1_crypt_pk, 0); 229 // get the InputStream with the decrypted data 230 InputStream data_dec = enveloped.getInputStream(); 231 System.out.println("Message decrypted!"); 232 // read the signed data from the decrypted InputStream 233 signed = new SignedDataStream(data_dec); 234 // get the InputStream with the signed, plain data 235 InputStream data = signed.getInputStream(); 236 237 // reset our output stream 238 out.reset(); 239 // write the decrypted and verified data to the output stream 240 os = new BufferedOutputStream(out); 241 // copy the data 242 Util.copyStream(data, os, null); 243 os.close(); 244 out.close(); 245 is.close(); 246 data_dec.close(); 247 248 // now verify the signature of the one and only signer and print the certificate of the signer 249 X509Certificate cert = signed.verify(0); 250 System.out.println("Signature OK from: "+cert.getSubjectDN()); 251 252 253 System.out.println("Received message: \"" + new String(out.toByteArray())+"\""); 254 } 255 256 /** 257 * Uses the non-stream based SignedData and EnvelopedData implementations 258 * (<code>SignedData</code>, <code>EnvelopedData</code> to 259 * sign and envelope the given message, encode the CMS object, decodes it 260 * again, removes the envlope and verifies the signature. 261 * 262 * @param message the message to be signed and enveloped 263 * @throws Exception if an error occurs 264 */ 265 public void testSignedAndEnvelopedData(byte[] message) throws Exception { 266 267 // create an implicit signed message (signature contains message) 268 SignedData signed = new SignedData(message, SignedData.IMPLICIT); 269 270 // these certificates are sent within the signature 271 signed.setCertificates(certificates); 272 273 // add one signer 274 // cert at index 0 is the user certificate 275 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign); 276 277 // create a new SignerInfo 278 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk); 279 280 // create some signed attributes 281 // the message digest attribute is automatically added 282 Attribute[] attributes = new Attribute[2]; 283 // content type is data 284 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 285 attributes[0] = new Attribute(contentType); 286 // signing time is now 287 SigningTime signingTime = new SigningTime(); 288 attributes[1] = new Attribute(signingTime); 289 290 // set the attributes 291 signer_info.setSignedAttributes(attributes); 292 // finish the creation of SignerInfo by calling method addSigner 293 signed.addSignerInfo(signer_info); 294 295 // encode SignedData to a byte array 296 byte[] signed_message = signed.getEncoded(); 297 298 // encrypt with AES 299 EnvelopedData enveloped = new EnvelopedData(signed_message, (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 300 // add recipients where the symmetric key is encrypted with RSA 301 // create the recipient infos 302 RecipientInfo[] recipients = new RecipientInfo[2]; 303 // user1 is the first receiver 304 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 305 // user2 is the second receiver 306 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 307 308 // specify the recipients of the encrypted message 309 enveloped.setRecipientInfos(recipients); 310 311 // encrypt and write the data to a byte array 312 byte[] signed_enveloped_message = enveloped.getEncoded(); 313 314 System.out.println("Message created, now doing the parsing..."); 315 // and now decrypt the data and verify the signature 316 InputStream is = new BufferedInputStream(new ByteArrayInputStream(signed_enveloped_message)); 317 enveloped = new EnvelopedData(is); 318 // use this private key to decrypt the symmetric key of recipient 0 319 enveloped.setupCipher(user1_crypt_pk, 0); 320 // get the InputStream with the decrypted data 321 InputStream data_dec = enveloped.getInputStream(); 322 System.out.println("Message decrypted!"); 323 // read the signed data from the decrypted InputStream 324 signed = new SignedData(data_dec); 325 // get the content with the signed, plain data 326 byte[] data = signed.getContent(); 327 328 // now verify the signature of the one and only signer and print the certificate of the signer 329 X509Certificate cert = signed.verify(0); 330 System.out.println("Signature OK from: "+cert.getSubjectDN()); 331 332 333 System.out.println("Received message: \"" + new String(data)+"\""); 334 } 335 336 337 338 339 /** 340 * Starts the test. 341 */ 342 public void start() { 343 // the test message 344 String m = "This demo message will be signed and/or encrypted."; 345 System.out.println("Test message: \""+m+"\""); 346 System.out.println(); 347 byte[] message = m.getBytes(); 348 349 try { 350 System.out.println("Signed and enveloped data - stream based demo."); 351 testSignedAndEnvelopedDataStream(message); 352 System.out.println(); 353 354 System.out.println("Signed and enveloped data - non stream based demo."); 355 testSignedAndEnvelopedData(message); 356 System.out.println(); 357 358 System.out.println("Ready!"); 359 } catch (Exception ex) { 360 ex.printStackTrace(); 361 362 throw new RuntimeException(ex.toString()); 363 } 364 } 365 366 /** 367 * Main method. 368 * 369 * @throws IOException 370 * if an I/O error occurs when reading required keys 371 * and certificates from files 372 */ 373 public static void main(String argv[]) throws Exception { 374 375 demo.DemoUtil.initDemos(); 376 377 (new SignedAndEnvelopedDataDemo()).start(); 378 DemoUtil.waitKey(); 379 } 380 381 /** 382 * Inner class for copying data between the 2 streams. 383 */ 384 static class Writer extends Thread { 385 386 SignedDataStream signed; 387 OutputStream os; 388 Exception exception; 389 390 public Writer(SignedDataStream signed, OutputStream os) { 391 super("Writer"); 392 this.signed = signed; 393 this.os = os; 394 } 395 396 /** 397 * Writes the SMimeSinged to the OutputStream. 398 */ 399 public void run() { 400 try { 401 signed.writeTo(os,2048); 402 os.close(); 403 } catch (Exception ex) { 404 exception = ex; 405 System.out.println("Writer exception: "+exception); 406 } 407 } 408 409 /** 410 * Returns a possible exception. 411 */ 412 public Exception getException() { 413 return exception; 414 } 415 } 416 417 }