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/SignedAndEnvelopedDataOutputStreamDemo.java 13 12.02.25 17:58 Dbratko $ 059 // $Revision: 13 $ 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.CMSException; 068 import iaik.cms.ContentInfoOutputStream; 069 import iaik.cms.EnvelopedDataOutputStream; 070 import iaik.cms.EnvelopedDataStream; 071 import iaik.cms.IssuerAndSerialNumber; 072 import iaik.cms.KeyTransRecipientInfo; 073 import iaik.cms.RecipientInfo; 074 import iaik.cms.SignedDataOutputStream; 075 import iaik.cms.SignedDataStream; 076 import iaik.cms.SignerInfo; 077 import iaik.cms.attributes.CMSContentType; 078 import iaik.cms.attributes.SigningTime; 079 import iaik.security.random.SecRandom; 080 import iaik.utils.Util; 081 import iaik.x509.X509Certificate; 082 083 import java.io.ByteArrayInputStream; 084 import java.io.ByteArrayOutputStream; 085 import java.io.IOException; 086 import java.io.InputStream; 087 import java.security.NoSuchAlgorithmException; 088 import java.security.PrivateKey; 089 import java.security.SecureRandom; 090 091 import demo.DemoUtil; 092 import demo.keystore.CMSKeyStore; 093 094 /** 095 * This class shows the sequential combination of the SignedDataOutputStream 096 * and EnvelopedDataOutputStream implementations. 097 * <p> 098 * All keys and certificates are read from a keystore created by the 099 * SetupCMSKeyStore program. 100 */ 101 public class SignedAndEnvelopedDataOutputStreamDemo { 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 // secure random number generator 118 SecureRandom random; 119 120 /** 121 * Setup the demo certificate chains. 122 * 123 * Keys and certificate are retrieved from the demo KeyStore. 124 * 125 * @throws IOException if an file read error occurs 126 */ 127 public SignedAndEnvelopedDataOutputStreamDemo() throws IOException { 128 129 System.out.println(); 130 System.out.println("*********************************************************************************************************"); 131 System.out.println("* SignedAndEnvelopedDataOutputStream demo *"); 132 System.out.println("* (shows the usage of the combined SignedDataOutputStream and EnvelopedDataOutputStream implementation) *"); 133 System.out.println("*********************************************************************************************************"); 134 System.out.println(); 135 136 // add all certificates to the list 137 X509Certificate[] certs = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 138 user1_sign = certs[0]; 139 user1_sign_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_SIGN_1); 140 user1_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1)[0]; 141 user1_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_1); 142 user2_crypt = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2)[0]; 143 user2_crypt_pk = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, CMSKeyStore.SZ_2048_CRYPT_2); 144 // user1 also includes her/his encryption certificate 145 certificates = new X509Certificate[certs.length+1]; 146 System.arraycopy(certs, 0, certificates, 0, certs.length); 147 certificates[certs.length] = user1_crypt; 148 149 random = SecRandom.getDefault(); 150 } 151 152 /** 153 * Signs and envelopes the given message, encodes the CMS object, decodes it 154 * again, removes the envlope and verifies the signature. 155 * 156 * @param message the message to be signed and enveloped 157 * @throws Exception if an error occurs 158 */ 159 public void testSignedAndEnvelopedDataStream(byte[] message) throws Exception { 160 161 // a stream from which to read the data to be signed and encrypted 162 ByteArrayInputStream is = new ByteArrayInputStream(message); 163 164 // the stream to which to write the Signed and EnvelopedData 165 ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); 166 167 EnvelopedDataOutputStream envelopedData; 168 169 // wrap into a ContentInfo 170 ContentInfoOutputStream envContentInfoStream = 171 new ContentInfoOutputStream(ObjectID.cms_envelopedData, resultStream); 172 // create a new EnvelopedData object encrypted with AES 173 try { 174 envelopedData = new EnvelopedDataOutputStream(envContentInfoStream, 175 (AlgorithmID)AlgorithmID.aes256_CBC.clone()); 176 } catch (NoSuchAlgorithmException ex) { 177 throw new CMSException(ex.toString()); 178 } 179 180 // create the recipient infos 181 RecipientInfo[] recipients = new RecipientInfo[2]; 182 // user1 is the first receiver 183 recipients[0] = new KeyTransRecipientInfo(user1_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 184 // user2 is the second receiver 185 recipients[1] = new KeyTransRecipientInfo(user2_crypt, (AlgorithmID)AlgorithmID.rsaEncryption.clone()); 186 187 // specify the recipients of the encrypted message 188 envelopedData.setRecipientInfos(recipients); 189 190 // wrap SignedData into a ContentInfo 191 ContentInfoOutputStream signedContentInfoStream = 192 new ContentInfoOutputStream(ObjectID.cms_signedData, envelopedData); 193 194 SignedDataOutputStream signedData = new SignedDataOutputStream(signedContentInfoStream, 195 SignedDataOutputStream.IMPLICIT); 196 // these certificates are sent within the signature 197 signedData.setCertificates(certificates); 198 199 // add one signer 200 // cert at index 0 is the user certificate 201 IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(user1_sign); 202 203 // create a new SignerInfo 204 SignerInfo signer_info = new SignerInfo(issuer, (AlgorithmID)AlgorithmID.sha256.clone(), user1_sign_pk); 205 206 // create some signed attributes 207 // the message digest attribute is automatically added 208 Attribute[] attributes = new Attribute[2]; 209 // content type is data 210 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 211 attributes[0] = new Attribute(contentType); 212 // signing time is now 213 SigningTime signingTime = new SigningTime(); 214 attributes[1] = new Attribute(signingTime); 215 216 // set the attributes 217 signer_info.setSignedAttributes(attributes); 218 // finish the creation of SignerInfo by calling method addSigner 219 signedData.addSignerInfo(signer_info); 220 221 int blockSize = 4; // in real world we would use a block size like 2048 222 // write in the data to be signed 223 byte[] buffer = new byte[blockSize]; 224 int bytesRead; 225 while ((bytesRead = is.read(buffer)) != -1) { 226 signedData.write(buffer, 0, bytesRead); 227 } 228 229 // closing the stream finished the cryptographic processing and closes the underlying stream 230 signedData.close(); 231 232 // get the signed and encrypted message from the ByteArrayOutputStream 233 byte[] signed_enveloped_message = resultStream.toByteArray(); 234 System.out.println("Message created, now doing the parsing..."); 235 // and now decrypt the data and verify the signature 236 is = new ByteArrayInputStream(signed_enveloped_message); 237 EnvelopedDataStream enveloped = new EnvelopedDataStream(is); 238 // use this private key to decrypt the symmetric key of recipient 0 239 enveloped.setupCipher(user1_crypt_pk, 0); 240 // get the InputStream with the decrypted data 241 InputStream data_dec = enveloped.getInputStream(); 242 System.out.println("Message decrypted!"); 243 // read the signed data from the decrypted InputStream 244 SignedDataStream signed = new SignedDataStream(data_dec); 245 // get the InputStream with the signed, plain data 246 InputStream data = signed.getInputStream(); 247 // reset our output stream 248 ByteArrayOutputStream os = new ByteArrayOutputStream(); 249 // copy the data 250 Util.copyStream(data, os, null); 251 os.close(); 252 is.close(); 253 data_dec.close(); 254 255 // now verify the signature of the one and only signer and print the certificate of the signer 256 X509Certificate cert = signed.verify(0); 257 System.out.println("Signature OK from: "+cert.getSubjectDN()); 258 259 System.out.println("Received message: \"" + new String(os.toByteArray())+"\""); 260 } 261 262 263 264 /** 265 * Starts the test. 266 */ 267 public void start() { 268 // the test message 269 String m = "This demo message will be signed and/or encrypted."; 270 System.out.println("Test message: \""+m+"\""); 271 System.out.println(); 272 byte[] message = m.getBytes(); 273 274 try { 275 testSignedAndEnvelopedDataStream(message); 276 System.out.println(); 277 278 System.out.println("Ready!"); 279 } catch (Exception ex) { 280 ex.printStackTrace(); 281 282 throw new RuntimeException(ex.toString()); 283 } 284 } 285 286 /** 287 * Main method. 288 * 289 * @throws IOException 290 * if an I/O error occurs when reading required keys 291 * and certificates from files 292 */ 293 public static void main(String argv[]) throws Exception { 294 295 demo.DemoUtil.initDemos(); 296 297 (new SignedAndEnvelopedDataOutputStreamDemo()).start(); 298 DemoUtil.waitKey(); 299 } 300 301 }