org.digidoc4j.signers.PKCS11SignatureToken.java Source code

Java tutorial

Introduction

Here is the source code for org.digidoc4j.signers.PKCS11SignatureToken.java

Source

/* 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;
    }
}