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