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    }