org.signserver.server.cryptotokens.OldPKCS11CryptoToken.java Source code

Java tutorial

Introduction

Here is the source code for org.signserver.server.cryptotokens.OldPKCS11CryptoToken.java

Source

/*************************************************************************
 *                                                                       *
 *  SignServer: The OpenSource Automated Signing Server                  *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.signserver.server.cryptotokens;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.cert.Certificate;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.jce.ECKeyUtil;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.cesecore.keys.token.*;
import org.ejbca.core.model.ca.catoken.PKCS11CAToken;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;
import org.ejbca.util.keystore.KeyStoreContainer;
import org.ejbca.util.keystore.KeyStoreContainerFactory;
import org.signserver.common.*;
import org.signserver.common.CryptoTokenOfflineException;
import org.signserver.server.KeyUsageCounterHash;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

/**
 * Class used to connect to a PKCS11 HSM.
 *
 * Properties:
 *   sharedLibrary
 *   slot
 *   defaultKey
 *   pin
 *   attributesFile
 *
 * @see org.signserver.server.cryptotokens.ICryptoToken
 * @author Tomas Gustavsson, Philip Vendil
 * @version $Id: OldPKCS11CryptoToken.java 5968 2015-03-26 13:07:03Z netmackan $
 * @deprecated Use the PKCS11CryptoToken instead
 */
@Deprecated
public class OldPKCS11CryptoToken extends OldCryptoTokenBase implements ICryptoToken, IKeyGenerator, IKeyRemover {

    /** Logger for this class. */
    private static final Logger LOG = Logger.getLogger(OldPKCS11CryptoToken.class);

    private Properties properties;

    private char[] authenticationCode;

    private PKCS11Settings settings;

    public OldPKCS11CryptoToken() throws InstantiationException {
        catoken = new PKCS11CAToken();
    }

    /**
     * Method initializing the PKCS11 device
     *
     */
    @Override
    public void init(final int workerId, final Properties props) throws CryptoTokenInitializationFailureException {
        LOG.debug(">init");
        String signaturealgoritm = props.getProperty(WorkerConfig.SIGNERPROPERTY_SIGNATUREALGORITHM);

        this.properties = fixUpProperties(props);

        final String sharedLibraryName = properties.getProperty("sharedLibraryName");
        final String sharedLibraryProperty = properties.getProperty("sharedLibrary");

        settings = PKCS11Settings.getInstance();

        // at least one the SHAREDLIBRARYNAME or SHAREDLIBRAY
        // (for backwards compatability) properties must be defined
        if (sharedLibraryName == null && sharedLibraryProperty == null) {
            final StringBuilder sb = new StringBuilder();

            sb.append("Missing SHAREDLIBRARYNAME property\n");
            settings.listAvailableLibraryNames(sb);

            throw new CryptoTokenInitializationFailureException(sb.toString());
        }

        // if only the old SHAREDLIBRARY property is given, it must point
        // to one of the libraries defined at deploy-time
        if (sharedLibraryProperty != null && sharedLibraryName == null) {
            // check if the library was defined at deploy-time
            if (!settings.isP11LibraryExisting(sharedLibraryProperty)) {
                throw new CryptoTokenInitializationFailureException(
                        "SHAREDLIBRARY is not permitted when pointing to a library not defined at deploy-time");
            }
        }

        // lookup the library defined by SHAREDLIBRARYNAME among the
        // deploy-time-defined values
        final String sharedLibraryFile = sharedLibraryName == null ? null
                : settings.getP11SharedLibraryFileForName(sharedLibraryName);

        // both the old and new properties are allowed at the same time
        // to ease migration, given that they point to the same library
        if (sharedLibraryProperty != null && sharedLibraryName != null) {
            if (sharedLibraryFile != null) {
                final File byPath = new File(sharedLibraryProperty);
                final File byName = new File(sharedLibraryFile);

                try {
                    if (!byPath.getCanonicalPath().equals(byName.getCanonicalPath())) {
                        // the properties pointed to different libraries
                        throw new CryptoTokenInitializationFailureException(
                                "Can not specify both SHAREDLIBRARY and SHAREDLIBRARYNAME at the same time");
                    }
                } catch (IOException e) {
                    throw new CryptoTokenInitializationFailureException(
                            "Can not specify both SHAREDLIBRARY and SHAREDLIBRARYNAME at the same time");
                }
            } else {
                // SHAREDLIBRARYNAME was undefined
                throw new CryptoTokenInitializationFailureException(
                        "Can not specify both SHAREDLIBRARY and SHAREDLIBRARYNAME at the same time");
            }
        }

        // if only SHAREDLIBRARYNAME was given and the value couldn't be
        // found, include a list of available values in the token error
        // message
        if (sharedLibraryFile == null && sharedLibraryProperty == null) {
            final StringBuilder sb = new StringBuilder();

            sb.append("SHAREDLIBRARYNAME ");
            sb.append(sharedLibraryName);
            sb.append(" is not referring to a defined value");
            sb.append("\n");
            settings.listAvailableLibraryNames(sb);

            throw new CryptoTokenInitializationFailureException(sb.toString());
        }

        // check the file (again) and pass it on to the underlaying implementation
        if (sharedLibraryFile != null) {
            final File sharedLibrary = new File(sharedLibraryFile);
            if (!sharedLibrary.isFile() || !sharedLibrary.canRead()) {
                throw new CryptoTokenInitializationFailureException(
                        "The shared library file can't be read: " + sharedLibrary.getAbsolutePath());
            }

            // propagate the shared library property to the delegate
            properties.setProperty("SHAREDLIBRARY", sharedLibraryFile);
        }

        try {
            ((PKCS11CAToken) catoken).init(properties, null, signaturealgoritm, workerId);
        } catch (Exception e) {
            LOG.error("Error initializing PKCS11CryptoToken : " + e.getMessage(), e);
        }
        String authCode = properties.getProperty("pin");
        if (authCode != null) {
            try {
                this.activate(authCode);
            } catch (Exception e) {
                LOG.error("Error auto activating PKCS11CryptoToken : " + e.getMessage(), e);
            }
        }
        LOG.debug("<init");
    }

    @Override
    public void activate(String authenticationcode)
            throws CryptoTokenAuthenticationFailureException, CryptoTokenOfflineException {
        this.authenticationCode = authenticationcode == null ? null : authenticationcode.toCharArray();
        super.activate(authenticationcode);
    }

    @Override
    public byte[] decryptByteData(String alias, String pin, byte[] encryptedData)
            throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException,
            CryptoTokenOfflineException, UnrecoverableKeyException, KeyStoreException, InvalidKeyException {
        throw new UnsupportedOperationException("No such operation found");
    }

    @Override
    public byte[] encryptMessage(String alias, String authcode, byte[] message) throws NoSuchPaddingException,
            NoSuchAlgorithmException, org.cesecore.keys.token.CryptoTokenOfflineException, InvalidKeyException,
            UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
        throw new UnsupportedOperationException("No such operation found");
    }

    /**
     * @see IKeyGenerator#generateKey(java.lang.String, java.lang.String, java.lang.String, char[])
     */
    @Override
    public void generateKey(final String keyAlgorithm, String keySpec, String alias, char[] authCode)
            throws CryptoTokenOfflineException, IllegalArgumentException {

        if (keySpec == null) {
            throw new IllegalArgumentException("Missing keyspec parameter");
        }
        if (alias == null) {
            throw new IllegalArgumentException("Missing alias parameter");
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("keyAlgorithm: " + keyAlgorithm + ", keySpec: " + keySpec + ", alias: " + alias);
        }
        try {

            final Provider provider = Security.getProvider(getProvider(ICryptoToken.PROVIDERUSAGE_SIGN));
            if (LOG.isDebugEnabled()) {
                LOG.debug("provider: " + provider);
            }

            // Keyspec for DSA is prefixed with "dsa"
            if (keyAlgorithm != null && keyAlgorithm.equalsIgnoreCase("DSA") && !keySpec.contains("dsa")) {
                keySpec = "dsa" + keySpec;
            }

            KeyStore.ProtectionParameter pp;
            if (authCode == null) {
                LOG.debug("authCode == null");
                final String pin = properties.getProperty("pin");
                if (pin != null) {
                    LOG.debug("pin specified");
                    pp = new KeyStore.PasswordProtection(pin.toCharArray());
                } else if (authenticationCode != null) {
                    LOG.debug("Using autentication code");
                    pp = new KeyStore.PasswordProtection(authenticationCode);
                } else {
                    LOG.debug("pin == null");
                    pp = new KeyStore.ProtectionParameter() {
                    };
                }
            } else {
                LOG.debug("authCode specified");
                pp = new KeyStore.PasswordProtection(authCode);
            }

            final String sharedLibrary = properties.getProperty("sharedLibrary");
            final String slot = properties.getProperty("slot");
            final String attributesFile = properties.getProperty("attributesFile");

            if (LOG.isDebugEnabled()) {
                LOG.debug("sharedLibrary: " + sharedLibrary + ", slot: " + slot + ", attributesFile: "
                        + attributesFile);
            }

            final KeyStoreContainer store = KeyStoreContainerFactory.getInstance(
                    KeyStoreContainer.KEYSTORE_TYPE_PKCS11, sharedLibrary, null, slot, attributesFile, pp);
            store.setPassPhraseLoadSave(authCode);
            store.generate(keySpec, alias);
        } catch (Exception ex) {
            LOG.error(ex, ex);
            throw new CryptoTokenOfflineException(ex);
        }
    }

    @Override
    public Certificate generateSignerKeyAndGetCertificate(String keyAlgorithm, String keySpec, String alias,
            char[] authCode) throws CryptoTokenOfflineException, IllegalArgumentException, KeyStoreException {
        generateKey(keyAlgorithm, keySpec, alias, authCode);

        KeyStore ks = getKeyStore(authCode);
        Certificate cert = ks.getCertificate(alias);
        return cert;
    }

    private KeyStore getKeyStore(final char[] authCode) throws KeyStoreException {
        KeyStore.ProtectionParameter pp;
        if (authCode == null) {
            LOG.debug("authCode == null");
            final String pin = properties.getProperty("pin");
            if (pin == null) {
                LOG.debug("pin == null");
                pp = new KeyStore.ProtectionParameter() {
                };
            } else {
                LOG.debug("pin specified");
                pp = new KeyStore.PasswordProtection(pin.toCharArray());
            }
        } else {
            LOG.debug("authCode specified");
            pp = new KeyStore.PasswordProtection(authCode);
        }

        final Provider provider = Security.getProvider(getProvider(ICryptoToken.PROVIDERUSAGE_SIGN));
        if (LOG.isDebugEnabled()) {
            LOG.debug("provider: " + provider);
        }
        final KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", provider, pp);

        return builder.getKeyStore();
    }

    /**
     * @see ICryptoToken#testKey(java.lang.String, char[])
     */
    @Override
    public Collection<KeyTestResult> testKey(String alias, char[] authCode)
            throws CryptoTokenOfflineException, KeyStoreException {
        LOG.debug(">testKey");
        final Collection<KeyTestResult> result = new LinkedList<KeyTestResult>();

        final byte signInput[] = "Lillan gick on the roaden ut.".getBytes();

        final KeyStore keyStore = getKeyStore(authCode);

        try {
            final Enumeration<String> e = keyStore.aliases();
            while (e.hasMoreElements()) {
                final String keyAlias = e.nextElement();
                if (alias.equalsIgnoreCase(ICryptoToken.ALL_KEYS) || alias.equals(keyAlias)) {
                    if (keyStore.isKeyEntry(keyAlias)) {
                        String status;
                        String publicKeyHash = null;
                        boolean success = false;
                        try {
                            final PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, authCode);
                            final Certificate cert = keyStore.getCertificate(keyAlias);
                            if (cert != null) {
                                final KeyPair keyPair = new KeyPair(cert.getPublicKey(), privateKey);
                                publicKeyHash = CryptoTokenHelper.createKeyHash(keyPair.getPublic());
                                final String sigAlg = CryptoTokenHelper.suggestSigAlg(keyPair.getPublic());
                                if (sigAlg == null) {
                                    status = "Unknown key algorithm: " + keyPair.getPublic().getAlgorithm();
                                } else {
                                    Signature signature = Signature.getInstance(sigAlg, keyStore.getProvider());
                                    signature.initSign(keyPair.getPrivate());
                                    signature.update(signInput);
                                    byte[] signBA = signature.sign();

                                    Signature verifySignature = Signature.getInstance(sigAlg);
                                    verifySignature.initVerify(keyPair.getPublic());
                                    verifySignature.update(signInput);
                                    success = verifySignature.verify(signBA);
                                    status = success ? "" : "Test signature inconsistent";
                                }
                            } else {
                                status = "Not testing keys with alias " + keyAlias + ". No certificate exists.";
                            }
                        } catch (ClassCastException ce) {
                            status = "Not testing keys with alias " + keyAlias + ". Not a private key.";
                        } catch (Exception ex) {
                            LOG.error("Error testing key: " + keyAlias, ex);
                            status = ex.getMessage();
                        }
                        result.add(new KeyTestResult(keyAlias, success, status, publicKeyHash));
                    }
                }
            }
        } catch (KeyStoreException ex) {
            throw new CryptoTokenOfflineException(ex);
        }

        LOG.debug("<testKey");
        return result;
    }

    // TODO: The genCertificateRequest method is mostly a duplicate of the one in CryptoTokenBase, PKCS11CryptoTooken, KeyStoreCryptoToken and SoftCryptoToken.
    @Override
    public ICertReqData genCertificateRequest(ISignerCertReqInfo info, final boolean explicitEccParameters,
            boolean defaultKey) throws CryptoTokenOfflineException {
        LOG.debug(">genCertificateRequest PKCS11CryptoToken");
        Base64SignerCertReqData retval = null;
        if (info instanceof PKCS10CertReqInfo) {
            PKCS10CertReqInfo reqInfo = (PKCS10CertReqInfo) info;
            PKCS10CertificationRequest pkcs10;

            final String alias;
            if (defaultKey) {
                alias = properties.getProperty("defaultKey");
            } else {
                alias = properties.getProperty("nextCertSignKey");
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("defaultKey: " + defaultKey);
                LOG.debug("alias: " + alias);
                LOG.debug("signatureAlgorithm: " + reqInfo.getSignatureAlgorithm());
                LOG.debug("subjectDN: " + reqInfo.getSubjectDN());
                LOG.debug("explicitEccParameters: " + explicitEccParameters);
            }

            try {
                final KeyStore keyStore = getKeyStore(authenticationCode);

                final PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, authenticationCode);
                final Certificate cert = keyStore.getCertificate(alias);
                if (cert == null) {
                    throw new CryptoTokenOfflineException(
                            "Certificate request error: No key with the configured alias");
                }

                PublicKey publicKey = cert.getPublicKey();

                // Handle ECDSA key with explicit parameters
                if (explicitEccParameters && publicKey.getAlgorithm().contains("EC")) {
                    publicKey = ECKeyUtil.publicToExplicitParameters(publicKey, "BC");
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Public key SHA1: " + CryptoTokenHelper.createKeyHash(cert.getPublicKey()));
                    LOG.debug("Public key SHA256: " + KeyUsageCounterHash.create(cert.getPublicKey()));
                }

                // Generate request
                final JcaPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(
                        new X500Name(CertTools.stringToBCDNString(reqInfo.getSubjectDN())), publicKey);
                final ContentSigner contentSigner = new JcaContentSignerBuilder(reqInfo.getSignatureAlgorithm())
                        .setProvider(getProvider(ICryptoToken.PROVIDERUSAGE_SIGN)).build(privateKey);
                pkcs10 = builder.build(contentSigner);
                retval = new Base64SignerCertReqData(Base64.encode(pkcs10.getEncoded()));
            } catch (IOException e) {
                LOG.error("Certificate request error: " + e.getMessage(), e);
            } catch (OperatorCreationException e) {
                LOG.error("Certificate request error: signer could not be initialized", e);
            } catch (UnrecoverableKeyException e) {
                LOG.error("Certificate request error: " + e.getMessage(), e);
            } catch (KeyStoreException e) {
                LOG.error("Certificate request error: " + e.getMessage(), e);
            } catch (NoSuchAlgorithmException e) {
                LOG.error("Certificate request error: " + e.getMessage(), e);
            } catch (NoSuchProviderException e) {
                LOG.error("Certificate request error: " + e.getMessage(), e);
            }

        }
        LOG.debug("<genCertificateRequest PKCS11CryptoToken");
        return retval;
    }

    @Override
    public KeyStore getKeyStore()
            throws UnsupportedOperationException, CryptoTokenOfflineException, KeyStoreException {
        return getKeyStore(authenticationCode); // TODO: check loaded etc
    }

    @Override
    public boolean removeKey(String alias)
            throws CryptoTokenOfflineException, KeyStoreException, SignServerException {
        return CryptoTokenHelper.removeKey(getKeyStore(), alias);
    }
}