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/pkcs11/SignedDataStreamDemo.java 16 12.02.25 17:58 Dbratko $ 029// $Revision: 16 $ 030// 031 032package demo.cms.pkcs11; 033 034import java.io.ByteArrayInputStream; 035import java.io.ByteArrayOutputStream; 036import java.io.IOException; 037import java.io.InputStream; 038import java.security.GeneralSecurityException; 039import java.security.Key; 040import java.security.NoSuchAlgorithmException; 041import java.security.PrivateKey; 042import java.security.SignatureException; 043import java.security.cert.Certificate; 044import java.security.cert.X509Certificate; 045import java.util.Enumeration; 046 047import demo.DemoUtil; 048// class and interface imports 049import iaik.asn1.ObjectID; 050import iaik.asn1.structures.AlgorithmID; 051import iaik.asn1.structures.Attribute; 052import iaik.cms.CMSException; 053import iaik.cms.ContentInfoStream; 054import iaik.cms.IssuerAndSerialNumber; 055import iaik.cms.SignedDataStream; 056import iaik.cms.SignerInfo; 057import iaik.cms.attributes.CMSContentType; 058import iaik.cms.attributes.SigningTime; 059 060 061/** 062 * Base class of SignedDataStream demos using PKCS#11 for 063 * accessing the signer key on a smart card. 064 */ 065public abstract class SignedDataStreamDemo extends PKCS11Demo { 066 067 /** 068 * The private key of the signer. In this case only a proxy object, but the 069 * application cannot see this. 070 */ 071 protected PrivateKey signerKey_; 072 073 /** 074 * This is the certificate used for verifying the signature. In contrast to the 075 * private signer key, the certificate holds the actual public keying material. 076 */ 077 protected X509Certificate signerCertificate_; 078 079 /** 080 * Creates a SignedDataStreamDemo object that has to be explicitly 081 * {@link PKCS11Demo#init(String, char[]) initialized} with a module name. 082 */ 083 protected SignedDataStreamDemo() { 084 // install provider in super class 085 super(); 086 } 087 088 /** 089 * This method gets the key stores of all inserted (compatible) smart 090 * cards and simply takes the first key-entry. From this key entry it 091 * takes the private key and the certificate to retrieve the public key 092 * from. The keys are stored in the member variables <code>signerKey_ 093 * </code> and <code>signerCertificate_</code>. 094 * 095 * @throws GeneralSecurityException If anything with the provider fails. 096 * @throws IOException If loading the key store fails. 097 */ 098 protected void getSignatureKey() throws GeneralSecurityException, IOException 099 { 100 getSignatureKey(null); 101 } 102 103 /** 104 * This method gets the key stores of all inserted (compatible) smart 105 * cards and simply takes the first key-entry. From this key entry it 106 * takes the private key and the certificate to retrieve the public key 107 * from. The keys are stored in the member variables <code>signerKey_ 108 * </code> and <code>signerCertificate_</code>. 109 * <br> 110 * If <code>algorithm</code> is not <code>null</code> only those keys 111 * are considered that match the given algorithm. 112 * 113 * @param algorithm the key algorithm; maybe <code>null</code> to take 114 * the first signing key regardless of its algorithm 115 * 116 * @throws GeneralSecurityException If anything with the provider fails. 117 * @throws IOException If loading the key store fails. 118 */ 119 protected void getSignatureKey(String algorithm) throws GeneralSecurityException, IOException 120 { 121 // we simply take the first keystore, if there are serveral 122 Enumeration aliases = tokenKeyStore_.aliases(); 123 124 // and we take the first signature (private) key for simplicity 125 while (aliases.hasMoreElements()) { 126 String keyAlias = aliases.nextElement().toString(); 127 Key key = null; 128 try { 129 key = tokenKeyStore_.getKey(keyAlias, null); 130 } catch (NoSuchAlgorithmException ex) { 131 throw new GeneralSecurityException(ex.toString()); 132 } 133 134 if (key instanceof PrivateKey) { 135 if ((algorithm != null) && (!algorithm.equals(key.getAlgorithm()))) { 136 continue; 137 } 138 Certificate[] certificateChain = tokenKeyStore_.getCertificateChain(keyAlias); 139 if ((certificateChain != null) && (certificateChain.length > 0)) { 140 X509Certificate signerCertificate = (X509Certificate) certificateChain[0]; 141 boolean[] keyUsage = signerCertificate.getKeyUsage(); 142 if ((keyUsage == null) || keyUsage[0] || keyUsage[1]) { // check for digital signature or non-repudiation, but also accept if none set 143 System.out.println("##########"); 144 System.out.println("The signer key is: " + key ); 145 System.out.println("##########"); 146 // get the corresponding certificate for this signer key 147 System.out.println("##########"); 148 System.out.println("The signer certificate is:"); 149 System.out.println(signerCertificate.toString()); 150 System.out.println("##########"); 151 signerKey_ = (PrivateKey) key; 152 signerCertificate_ = signerCertificate; 153 break; 154 } 155 } 156 } 157 } 158 159 if (signerKey_ == null) { 160 System.out.println("Found no signature key. Ensure that a valid card is inserted and contains a key that is suitable for signing."); 161 System.exit(0); 162 } 163 } 164 165 /** 166 * This method creates a SignerInfo for the given signer certificate. 167 * 168 * @param signerCertificate the certificate of the signer 169 * 170 * @return the SignerInfo 171 */ 172 protected SignerInfo createSignerInfo(iaik.x509.X509Certificate signerCertificate) 173 { 174 IssuerAndSerialNumber issuerAndSerialNumber = new IssuerAndSerialNumber(signerCertificate); 175 return new SignerInfo(issuerAndSerialNumber, 176 (AlgorithmID)AlgorithmID.sha256.clone(), 177 signerKey_); 178 } 179 180 /** 181 * This method signs the data in the byte array <code>DATA</code> with 182 * <code>signatureKey_</code>. Normally the data would be read from file. 183 * The created signature is stored in <code>signature_</code>. 184 * 185 * @param data the data to be signed 186 * @param implicit whether to include the data (implicit mode) 187 * or to not include it (explicit mode) 188 * 189 * @return the encoded SignedData 190 * 191 * @throws GeneralSecurityException 192 * If anything with the provider fails. 193 * @throws IOException 194 * If the data file could not be found or writing to it failed. 195 * @throws CMSException 196 * If an error occurs when creating/encoding the SignedData 197 */ 198 public byte[] sign(byte[] data, boolean implicit) 199 throws GeneralSecurityException, IOException, CMSException 200 { 201 System.out.println("##########"); 202 System.out.println("Signing data... "); 203 204 InputStream dataStream = new ByteArrayInputStream(data); // the raw data supplying input stream 205 int mode = (implicit == true) ? SignedDataStream.IMPLICIT : SignedDataStream.EXPLICIT; 206 SignedDataStream signedData = new SignedDataStream(dataStream, mode); 207 iaik.x509.X509Certificate iaikSignerCertificate = (signerCertificate_ instanceof iaik.x509.X509Certificate) 208 ? (iaik.x509.X509Certificate) signerCertificate_ 209 : new iaik.x509.X509Certificate(signerCertificate_.getEncoded()); 210 signedData.setCertificates(new iaik.x509.X509Certificate[] { iaikSignerCertificate } ); 211 SignerInfo signerInfo = createSignerInfo(iaikSignerCertificate); 212 System.out.println("Digest algorithm: " + signerInfo.getDigestAlgorithm()); 213 System.out.println("Signature algorithm: " + signerInfo.getSignatureAlgorithm()); 214 215 // create some signed attributes 216 // the message digest attribute is automatically added 217 Attribute[] attributes = new Attribute[2]; 218 try { 219 // content type is data 220 CMSContentType contentType = new CMSContentType(ObjectID.cms_data); 221 attributes[0] = new Attribute(contentType); 222 // signing time is now 223 SigningTime signingTime = new SigningTime(); 224 attributes[1] = new Attribute(signingTime); 225 } catch (Exception ex) { 226 throw new CMSException("Error creating attribute: " + ex.toString()); 227 } 228 229 // set the attributes 230 signerInfo.setSignedAttributes(attributes); 231 232 try { 233 signedData.addSignerInfo(signerInfo); 234 } catch (NoSuchAlgorithmException ex) { 235 throw new GeneralSecurityException(ex.toString()); 236 } 237 238 if (implicit == false) { 239 // in explicit mode read "away" content data (to be transmitted out-of-band) 240 InputStream contentIs = signedData.getInputStream(); 241 byte[] buffer = new byte[2048]; 242 int bytesRead; 243 while ((bytesRead = contentIs.read(buffer)) >= 0) { 244 ; // skip data 245 } 246 } 247 248 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 249 ContentInfoStream cos = new ContentInfoStream(signedData); 250 cos.writeTo(baos); 251 252 return baos.toByteArray(); 253 } 254 255 /** 256 * This method verifies the signature stored in <code>signatureKey_ 257 * </code>. The verification key used is <code>verificationKey_</code>. 258 * The implementation for the signature algorithm is taken from an 259 * other provider. Here IAIK is used, IAIK is pure software. 260 * 261 * @param encodedSignedData the encoded SignedData object 262 * @param contentData the contentData (in explicit mode required for signature verification) 263 * 264 * @return the content data 265 * 266 * @throws GeneralSecurityException 267 * If anything with the provider fails. 268 * @throws IOException 269 * If reading the CMS file fails. 270 * @throws CMSException 271 * If handling the CMS structure fails. 272 * @throws SignatureException 273 * If the signature verification fails 274 */ 275 public byte[] verify(byte[] encodedSignedData, byte[] contentData) 276 throws GeneralSecurityException, CMSException, IOException, SignatureException 277 { 278 System.out.println("##########"); 279 System.out.println("Verifying signature"); 280 281 InputStream inputStream = new ByteArrayInputStream(encodedSignedData); 282 SignedDataStream signedData = new SignedDataStream(inputStream); 283 284 if (signedData.getMode() == SignedDataStream.EXPLICIT) { 285 // explicitly set the data received by other means 286 signedData.setInputStream(new ByteArrayInputStream(contentData)); 287 } 288 289 // read data 290 InputStream signedDataInputStream = signedData.getInputStream(); 291 292 ByteArrayOutputStream contentOs = new ByteArrayOutputStream(); 293 byte[] buffer = new byte[2048]; 294 int bytesRead; 295 while ((bytesRead = signedDataInputStream.read(buffer)) >= 0) { 296 contentOs.write(buffer, 0, bytesRead); 297 } 298 299 // get the signer infos 300 SignerInfo[] signerInfos = signedData.getSignerInfos(); 301 // verify the signatures 302 int numberOfSignerInfos = signerInfos.length; 303 if (numberOfSignerInfos == 0) { 304 String warning = "Warning: Unsigned message (no SignerInfo included)!"; 305 System.err.println(warning); 306 throw new CMSException(warning); 307 } else { 308 for (int i = 0; i < numberOfSignerInfos; i++) { 309 try { 310 // verify the signature for SignerInfo at index i 311 X509Certificate signerCertificate = signedData.verify(i); 312 // if the signature is OK the certificate of the signer is returned 313 System.out.println("Signature OK from signer: "+ signerCertificate.getSubjectDN()); 314 } catch (SignatureException ex) { 315 // if the signature is not OK a SignatureException is thrown 316 throw new SignatureException("Signature ERROR: " + ex.getMessage()); 317 } 318 // in practice we also would validate the signer certificate(s) 319 } 320 } 321 System.out.println("##########"); 322 // return the content 323 return contentOs.toByteArray(); 324 } 325 326 /** 327 * Starts the demo. 328 * 329 * @param implicit whether the implicit or explicit mode is used (data included in signature or not) 330 */ 331 public void start(boolean implicit) { 332 try { 333 byte[] testMessage = "This is the test message to be signed!".getBytes("ASCII"); 334 getKeyStore(); 335 getSignatureKey(); 336 byte[] signedData = sign(testMessage, implicit); 337 // verify 338 byte[] content = verify(signedData, implicit ? null : testMessage); 339 System.out.println("##########"); 340 // we know that we had a text content, thus we can convert into a String 341 System.out.println("Content: " + new String(content, "ASCII")); 342 System.out.println("##########"); 343 System.out.println("\nReady!"); 344 } catch (Throwable ex) { 345 ex.printStackTrace(); 346 throw new RuntimeException(ex.toString()); 347 } 348 } 349 350 /** 351 * This method starts the demo based on the given command line arguments. 352 * 353 * @param args These are the command line arguments. 354 */ 355 public void init(String[] args) { 356 357 if (args.length == 0) { 358 System.out.println("Missing pkcs11 module name.\n"); 359 printUsage(); 360 } 361 362 String moduleName = args[0]; 363 char[] userPin = (args.length == 2) ? args[1].toCharArray() : null; 364 365 if (args.length > 2) { 366 System.out.println("Too many arguments.\n"); 367 printUsage(); 368 } 369 370 init(moduleName, userPin); 371 372 DemoUtil.initDemos(); 373 374 } 375 376 /** 377 * Print usage information. 378 */ 379 private final void printUsage() { 380 String demo = getClass().getName(); 381 System.out.println("Usage:\n"); 382 System.out.println("java " + demo + " <pkcs11 module name>\n"); 383 System.out.println("e.g.:"); 384 System.out.println("java " + demo + " aetpkss1.dll"); 385 System.out.println("java " + demo + " aetpkss1.so"); 386 DemoUtil.waitKey(); 387 System.exit(0); 388 } 389 390}