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