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

Java tutorial

Introduction

Here is the source code for org.signserver.server.cryptotokens.CryptoTokenBase.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.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.interfaces.DSAKey;
import java.security.interfaces.ECKey;
import java.security.interfaces.RSAKey;
import java.util.Collection;
import java.util.List;
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.bouncycastle.util.encoders.Hex;
import org.ejbca.core.model.ca.catoken.CATokenAuthenticationFailedException;
import org.ejbca.core.model.ca.catoken.CATokenOfflineException;
import org.ejbca.core.model.ca.catoken.ICAToken;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;
import org.signserver.common.*;

/**
 * A base class to wrap around CATokens from EJBCA. Makes it easy to use CA Tokens from EJBCA 
 * as crypto tokens in Signserver.
 * 
 * @see org.signserver.server.cryptotokens.ICryptoToken
 * @author Philip Vendil, Tomas Gustavsson
 * @version $Id$
 */
public abstract class CryptoTokenBase implements ICryptoToken {

    private static final Logger log = Logger.getLogger(CryptoTokenBase.class);
    protected ICAToken catoken = null;

    /** A workaround for the feature in SignServer 2.0 that property keys are 
     * always converted to upper case. The EJBCA CA Tokens usually use mixed case properties
     */
    protected Properties fixUpProperties(Properties props) {
        return CryptoTokenHelper.fixP11Properties(props);
    }

    /**
     * Method returning SignerStatus.STATUS_ACTIVE if every thing is OK, otherwise STATUS_OFFLINE.
     * 
     */
    @Override
    public int getCryptoTokenStatus() {
        int status = catoken.getCATokenStatus();
        if (status == ICAToken.STATUS_ACTIVE) {
            return SignerStatus.STATUS_ACTIVE;
        }
        return SignerStatus.STATUS_OFFLINE;
    }

    /**
     * Method activating the cryptographic token using the given key
     * 
     * @throws CryptoTokenAuthenticationFailureException if activation failed, message gives more info
     * @throws CryptoTokenOfflineException if connection to token could not be created.
     * 
     */
    @Override
    public void activate(String authenticationcode)
            throws CryptoTokenAuthenticationFailureException, CryptoTokenOfflineException {
        try {
            catoken.activate(authenticationcode);
        } catch (CATokenOfflineException e) {
            throw new CryptoTokenOfflineException(e.getMessage());
        } catch (CATokenAuthenticationFailedException e) {
            throw new CryptoTokenAuthenticationFailureException(e.getMessage());
        }

    }

    /**
     * Method deactivating the cryptographic token
     * 
     * @return true if everything went successful
     */
    @Override
    public boolean deactivate() throws CryptoTokenOfflineException {
        boolean ret = false;
        try {
            ret = catoken.deactivate();
        } catch (Exception e) {
            throw new CryptoTokenOfflineException(e);
        }
        return ret;
    }

    /**
     * Returns a reference to the private key to use.
     * 
     * @see org.signserver.server.cryptotokens.ICryptoToken 
     */
    @Override
    public PrivateKey getPrivateKey(int purpose) throws CryptoTokenOfflineException {
        try {
            return catoken.getPrivateKey(purpose);
        } catch (CATokenOfflineException e) {
            throw new CryptoTokenOfflineException(e.getMessage());
        }
    }

    /**
     * Returns a reference to the public key to use.
     * 
     * @see org.signserver.server.cryptotokens.ICryptoToken 
     */
    @Override
    public PublicKey getPublicKey(int purpose) throws CryptoTokenOfflineException {
        try {
            return catoken.getPublicKey(purpose);
        } catch (CATokenOfflineException e) {
            throw new CryptoTokenOfflineException(e.getMessage());
        }
    }

    /**
     * Returns the provider name that should be used.
     * @see ICryptoToken.PROVIDERUSAGE_SIGN
     */
    @Override
    public String getProvider(int providerUsage) {
        return catoken.getProvider();
    }

    @Override
    public Certificate getCertificate(int purpose) throws CryptoTokenOfflineException {
        return null;
    }

    @Override
    public List<Certificate> getCertificateChain(int purpose) throws CryptoTokenOfflineException {
        return null;
    }

    // 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,
            final boolean defaultKey) throws CryptoTokenOfflineException {
        Base64SignerCertReqData retval = null;
        if (info instanceof PKCS10CertReqInfo) {
            PKCS10CertReqInfo reqInfo = (PKCS10CertReqInfo) info;
            PKCS10CertificationRequest pkcs10;
            final int purpose = defaultKey ? PURPOSE_SIGN : PURPOSE_NEXTKEY;
            if (log.isDebugEnabled()) {
                log.debug("Purpose: " + purpose);
                log.debug("signatureAlgorithm: " + reqInfo.getSignatureAlgorithm());
                log.debug("subjectDN: " + reqInfo.getSubjectDN());
                log.debug("explicitEccParameters: " + explicitEccParameters);
            }

            try {
                PublicKey publicKey = getPublicKey(purpose);

                // Handle ECDSA key with explicit parameters
                if (explicitEccParameters && publicKey.getAlgorithm().contains("EC")) {
                    publicKey = ECKeyUtil.publicToExplicitParameters(publicKey, "BC");
                }
                // 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(getPrivateKey(purpose));
                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 (NoSuchAlgorithmException e) {
                log.error("Certificate request error: " + e.getMessage(), e);
            } catch (NoSuchProviderException e) {
                log.error("Certificate request error: " + e.getMessage(), e);
            }

        }
        return retval;
    }

    /**
     * Method not supported
     */
    @Override
    public boolean destroyKey(int purpose) {
        return false;
    }

    @Override
    public Collection<KeyTestResult> testKey(String alias, char[] authCode)
            throws CryptoTokenOfflineException, KeyStoreException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public static String createKeyHash(PublicKey key) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA1", "BC");
            final String res = new String(Hex.encode(md.digest(key.getEncoded())));
            md.reset();
            return res;
        } catch (NoSuchProviderException ex) {
            final String message = "Nu such provider trying to hash public key";
            log.error(message, ex);
            throw new RuntimeException(message, ex);
        } catch (NoSuchAlgorithmException ex) {
            final String message = "Nu such algorithm trying to hash public key";
            log.error(message, ex);
            throw new RuntimeException(message, ex);
        }
    }

    public static String suggestSigAlg(PublicKey key) {
        final String alg;
        if (key instanceof ECKey) {
            alg = "SHA1withECDSA";
        } else if (key instanceof RSAKey) {
            alg = "SHA1withRSA";
        } else if (key instanceof DSAKey) {
            alg = "SHA1withDSA";
        } else {
            alg = null;
        }
        return alg;
    }
}