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