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 }