/* DigiDoc4J library * * This software is released under either the GNU Library General Public * License (see LICENSE.LGPL). * * Note that the only valid version of the LGPL license as far as this * project is concerned is the original GNU Library General Public License * Version 2.1, February 1999 */ package org.digidoc4j.signers; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.List; import org.apache.commons.lang.ArrayUtils; import org.digidoc4j.DigestAlgorithm; import org.digidoc4j.SignatureToken; import org.digidoc4j.exceptions.TechnicalException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.esig.dss.DSSUtils; import eu.europa.esig.dss.EncryptionAlgorithm; import eu.europa.esig.dss.token.AbstractSignatureTokenConnection; import eu.europa.esig.dss.token.DSSPrivateKeyEntry; import eu.europa.esig.dss.token.KSPrivateKeyEntry; import eu.europa.esig.dss.token.PasswordInputCallback; import eu.europa.esig.dss.token.Pkcs11SignatureToken; /** * Implements PKCS#11 interface for Smart Cards and hardware tokens. * <p/> * It can be used for making digital signatures with Smart Cards (ID-Cards), USB tokens (Aladdin USB eToken), * HSM (Hardware Security Module) or other hardware tokens that use PKCS#11 API. * <p/> * PKCS#11 module path depends on your operating system and installed smart card or hardware token library. * <p/> * If you are using OpenSC (https://github.com/OpenSC/OpenSC/wiki), then <br/> * For Windows, it could be C:\Windows\SysWOW64\opensc-pkcs11.dll, <br/> * For Linux, it could be /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so, <br/> * For OSX, it could be /usr/local/lib/opensc-pkcs11.so <br/> */ public class PKCS11SignatureToken implements SignatureToken { private static final Logger logger = LoggerFactory.getLogger(PKCS11SignatureToken.class); private AbstractSignatureTokenConnection signatureTokenConnection; private DSSPrivateKeyEntry privateKeyEntry; /** * Initializes the PKCS#11 token. * * @param pkcs11ModulePath PKCS#11 module path, depends on your operating system and installed smart card or hardware token library. * @param password Secret pin code for digital signature. * @param slotIndex Token slot index, depends on the hardware token. */ public PKCS11SignatureToken(String pkcs11ModulePath, char[] password, int slotIndex) { logger.debug("Initializing PKCS#11 signature token from " + pkcs11ModulePath + " and slot " + slotIndex); signatureTokenConnection = new Pkcs11SignatureToken(pkcs11ModulePath, password, slotIndex); } /** * Initializes the PKCS#11 token with password callback. * <p/> * This Password Callback is used in order to retrieve the password from the user when accessing the Key Store. * * @param pkcs11ModulePath PKCS#11 module path, depends on your operating system and installed smart card or hardware token library. * @param passwordCallback callback for providing the password for the private key. * @param slotIndex Token slot index, depends on the hardware token. */ public PKCS11SignatureToken(String pkcs11ModulePath, PasswordInputCallback passwordCallback, int slotIndex) { logger.debug("Initializing PKCS#11 signature token with password callback from " + pkcs11ModulePath + " and slot " + slotIndex); signatureTokenConnection = new Pkcs11SignatureToken(pkcs11ModulePath, passwordCallback, slotIndex); } /** * Fetches the private key entries from the hardware token for information purposes. * The actual private key remains on the token and won't be accessible. * * @return list of private key entries. */ public List<DSSPrivateKeyEntry> getPrivateKeyEntries() { return signatureTokenConnection.getKeys(); } /** * For selecting a particular private key to be used for signing. * * @param privateKeyEntry */ public void usePrivateKeyEntry(DSSPrivateKeyEntry privateKeyEntry) { this.privateKeyEntry = privateKeyEntry; } @Override public X509Certificate getCertificate() { logger.debug("Fetching certificate"); return getPrivateKeyEntry().getCertificate().getCertificate(); } @Override public byte[] sign(DigestAlgorithm digestAlgorithm, byte[] dataToSign) { try { logger.debug("Signing with PKCS#11 and " + digestAlgorithm.name()); byte[] digestToSign = DSSUtils.digest(digestAlgorithm.getDssDigestAlgorithm(), dataToSign); byte[] digestWithPadding = addPadding(digestToSign, digestAlgorithm); return signDigest(digestWithPadding); } catch (Exception e) { logger.error("Failed to sign with PKCS#11: " + e.getMessage()); throw new TechnicalException("Failed to sign with PKCS#11: " + e.getMessage(), e); } } private byte[] signDigest(byte[] digestToSign) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException { logger.debug("Signing digest"); DSSPrivateKeyEntry privateKeyEntry = getPrivateKeyEntry(); PrivateKey privateKey = ((KSPrivateKeyEntry) privateKeyEntry).getPrivateKey(); EncryptionAlgorithm encryptionAlgorithm = privateKeyEntry.getEncryptionAlgorithm(); String signatureAlgorithm = "NONEwith" + encryptionAlgorithm.getName(); return invokeSigning(digestToSign, privateKey, signatureAlgorithm); } private byte[] invokeSigning(byte[] digestToSign, PrivateKey privateKey, String signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { logger.debug("Signing with signature algorithm " + signatureAlgorithm); java.security.Signature signer = java.security.Signature.getInstance(signatureAlgorithm); signer.initSign(privateKey); signer.update(digestToSign); byte[] signatureValue = signer.sign(); return signatureValue; } private static byte[] addPadding(byte[] digest, DigestAlgorithm digestAlgorithm) { return ArrayUtils.addAll(digestAlgorithm.digestInfoPrefix(), digest); // should find the prefix by checking digest length? } private DSSPrivateKeyEntry getPrivateKeyEntry() { if (privateKeyEntry == null) { privateKeyEntry = getPrivateKeyEntries().get(0); } return privateKeyEntry; } }