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/signedData/SignedDataDemoWithAdditionalSignerInfo.java 20 12.02.25 17:58 Dbratko $ 059 // $Revision: 20 $ 060 // 061 062 package demo.cms.signedData; 063 064 import iaik.asn1.ObjectID; 065 import iaik.asn1.structures.AlgorithmID; 066 import iaik.asn1.structures.Attribute; 067 import iaik.cms.IssuerAndSerialNumber; 068 import iaik.cms.SignedData; 069 import iaik.cms.SignerInfo; 070 import iaik.cms.attributes.CMSContentType; 071 import iaik.cms.attributes.SigningTime; 072 import iaik.x509.X509Certificate; 073 074 import java.io.ByteArrayInputStream; 075 import java.io.ByteArrayOutputStream; 076 import java.io.IOException; 077 import java.io.InputStream; 078 import java.io.OutputStream; 079 import java.security.PrivateKey; 080 import java.security.cert.Certificate; 081 082 import demo.DemoUtil; 083 import demo.keystore.CMSKeyStore; 084 085 /** 086 * This class shows how to use the non-stream based {@link iaik.cms.SignedData SignedData} 087 * implementation to add a new SignerInfo to an existing, parsed SignedData object. 088 */ 089 public class SignedDataDemoWithAdditionalSignerInfo { 090 091 String testMessage; 092 093 public SignedDataDemoWithAdditionalSignerInfo() { 094 095 System.out.println(); 096 System.out.println("**********************************************************************************"); 097 System.out.println("* SignedDataDemoWithAdditionalSignerInfo *"); 098 System.out.println("* (shows how to add a new signer to an already existing SignedData object) *"); 099 System.out.println("**********************************************************************************"); 100 System.out.println(); 101 102 testMessage = "This is a test of the CMS implementation!"; 103 } 104 105 /** 106 * Verifies a given SignedData object which is read from the given input stream. 107 * 108 * @param is an input stream holding the SignedData object 109 * 110 * @throws Exception if an error occurs when verifying the SignedData 111 */ 112 private void verify(InputStream is) throws Exception { 113 114 System.out.println("\nVerify the SignedData..."); 115 // read the SignedData from the given input stream 116 SignedData signedData = new SignedData(is); 117 // get the content 118 byte[] content = signedData.getContent(); 119 // and show it 120 System.out.println("Content of SignedData: "+new String(content)); 121 122 // print the certificates included 123 System.out.println("Certificates included:"); 124 Certificate[] certs = signedData.getCertificates(); 125 try { 126 for (int i = 0; i < certs.length; i++) 127 System.out.println(certs[i]); 128 } catch (Exception ex) { 129 ex.printStackTrace(); 130 } 131 132 // print the signers included 133 System.out.println("Signers included:"); 134 SignerInfo[] signerInfos = signedData.getSignerInfos(); 135 136 int numberOfSignerInfos = signerInfos.length; 137 if (numberOfSignerInfos == 0) { 138 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 139 System.err.println(warning); 140 throw new Exception(warning); 141 } else { 142 for (int i = 0; i < numberOfSignerInfos; i++) { 143 X509Certificate cert = signedData.verify(i); 144 System.out.println("Signer: " + cert.getSubjectDN()); 145 System.out.println(signerInfos[i].toString(true)); 146 } 147 // in practice we also would validate the signer certificate(s) 148 } 149 } 150 151 /** 152 * Creates a new SignedData object. 153 * 154 * @param os the output stream where the created object shall be written to 155 * @param message the content for the SignedData 156 * @param size specifies which private key/certificate to use from the KeyStore 157 * 158 * @throws Exception if an error occurs when creating the SignedData object 159 */ 160 private void create(OutputStream os, String message, int size) throws Exception { 161 162 System.out.println("\nCreate a new SignedData with content: "+message); 163 // get the certificate chain from teh KeyStore 164 X509Certificate[] certificates = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, size); 165 // create the SignedData 166 SignedData signedData = new SignedData(message.getBytes(), iaik.cms.SignedData.IMPLICIT); 167 // set teh certificates 168 signedData.setCertificates(certificates); 169 // add a signer 170 addSigner(signedData, size); 171 // and write it the given output stream 172 signedData.writeTo(os); 173 os.close(); 174 } 175 176 /** 177 * Adds a new Signer to a given SignedData. 178 * 179 * @param is the input stream holding the existing SignedData object 180 * @param size specifies which private key/certificate to use from the KeyStore 181 */ 182 private ByteArrayOutputStream add(InputStream is, int size) throws Exception { 183 184 System.out.println("Adding a signature to an existing SignedData..."); 185 // read the existing SignedData from the given InputStream 186 SignedData signedData = new SignedData(is); 187 // print the content 188 byte[] content = signedData.getContent(); 189 System.out.println("Existing content is: " + new String(content)); 190 191 // add another signer 192 addSigner(signedData, size); 193 194 // create a new output stream and save it 195 ByteArrayOutputStream os = new ByteArrayOutputStream(); 196 signedData.writeTo(os); 197 198 is.close(); 199 os.close(); 200 201 // return the output stream which contains the new SignedData 202 return os; 203 } 204 205 /** 206 * Adds the new signer. 207 * 208 * @param signedData the SignedData where the new signer shall be added 209 * @param size specifies which private key/certificate to use from the KeyStore 210 */ 211 private void addSigner(SignedData signedData, int size) throws Exception { 212 213 // get new certificate and private key 214 X509Certificate cert = CMSKeyStore.getCertificateChain(CMSKeyStore.RSA, size)[0]; 215 PrivateKey privateKey = CMSKeyStore.getPrivateKey(CMSKeyStore.RSA, size); 216 217 // add to the existing list of certificates 218 Certificate[] certs = signedData.getCertificates(); 219 Certificate[] newCerts = new Certificate [certs.length + 1]; 220 System.arraycopy(certs, 0, newCerts, 0, certs.length); 221 newCerts[certs.length] = cert; 222 // set the new certificate list 223 signedData.setCertificates(newCerts); 224 225 // create a new SignerInfo 226 SignerInfo signerInfo = new SignerInfo(new IssuerAndSerialNumber(cert), 227 (AlgorithmID)AlgorithmID.sha256.clone(), 228 privateKey); 229 // define some attributes 230 Attribute[] attributes = { 231 new Attribute(new CMSContentType(ObjectID.cms_data)), 232 new Attribute(new SigningTime()) 233 }; 234 // set the attributes 235 signerInfo.setSignedAttributes(attributes); 236 // and add the new signer 237 signedData.addSignerInfo(signerInfo); 238 } 239 240 /** 241 * Starts the test. 242 */ 243 public void start() { 244 245 try { 246 // output stream for storing the signed data 247 ByteArrayOutputStream os = new ByteArrayOutputStream(); 248 // create a new SignedData 249 create(os, testMessage, CMSKeyStore.SZ_2048_SIGN_1); 250 // verify it 251 verify(new ByteArrayInputStream(os.toByteArray())); 252 // add another signer 253 os = add(new ByteArrayInputStream(os.toByteArray()), CMSKeyStore.SZ_2048_SIGN_1); 254 // and verify it again 255 verify(new ByteArrayInputStream(os.toByteArray())); 256 257 } catch (Exception ex) { 258 ex.printStackTrace(); 259 throw new RuntimeException(ex.toString()); 260 } 261 } 262 263 /** 264 * Main method. 265 * 266 * @throws IOException 267 * if an I/O error occurs when reading required keys 268 * and certificates from files 269 */ 270 public static void main(String argv[]) throws IOException { 271 272 DemoUtil.initDemos(); 273 (new SignedDataDemoWithAdditionalSignerInfo()).start(); 274 275 System.out.println("\nReady!"); 276 DemoUtil.waitKey(); 277 } 278 }