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