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

Java tutorial

Introduction

Here is the source code for org.signserver.server.cryptotokens.SoftCryptoToken.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.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import javax.naming.NamingException;
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.DecoderException;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;
import org.ejbca.util.keystore.KeyTools;
import org.signserver.common.*;
import org.signserver.ejb.interfaces.IWorkerSession;

/**
 * Cryptographic token that uses soft keys stored in the worker properties in the database.
 * Is support generation of certificate requests and regeneration of keys.
 * Every time genCertificateRequest is called i a new key called. destroyKey method
 * is not supported.
 * 
 * 
 * Currently is only one key supported used for all purposes.
 * 
 * This Cryptographic token should mainly be used for test and demonstration purposes
 * not for production.
 * 
 * Available properties are:
 * KEYALG : The algorithms of the keys generated. (for future use, currently is only "RSA" supported and the one used by default).
 * KEYSPEC : The specification of the keys generated. (Optional). If not set will "2048" be used.
 * KEYDATA : The base64 encoded key data.
 * 
 * @author Philip Vendil
 * @version $Id: SoftCryptoToken.java 5973 2015-03-26 15:52:33Z netmackan $
 * @deprecated This crypto token lacks a lot of functionality. Use any of the other.
 */
@Deprecated
public class SoftCryptoToken implements ICryptoToken {

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

    public static final String PROPERTY_KEYDATA = "KEYDATA";
    public static final String PROPERTY_KEYALG = "KEYALG";
    public static final String PROPERTY_KEYSPEC = "KEYSPEC";

    private int workerId;
    private KeyPair keys = null;
    private String keySpec = null;
    private String keyAlg = null;
    private boolean active = true;

    // FIXME:  Consider doing manual injection using the init method or similar. If it really needs to use the worker session?
    private IWorkerSession workerSession;

    /**
     * @see org.signserver.server.cryptotokens.ICryptoToken#init(int, java.util.Properties)
     */
    @Override
    public void init(int workerId, Properties props) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Init deprecated SoftCryptoToken");
        }
        this.workerId = workerId;
        keySpec = props.getProperty(PROPERTY_KEYSPEC, "2048");
        keyAlg = props.getProperty(PROPERTY_KEYALG, "RSA");
        final String keyDataValue = props.getProperty(PROPERTY_KEYDATA);

        if (keyDataValue != null) {
            try {
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");

                byte[] keyData = Base64.decode(keyDataValue.getBytes());
                ByteArrayInputStream bais = new ByteArrayInputStream(keyData);
                DataInputStream dis = new DataInputStream(bais);

                int pubKeySize = dis.readInt();
                byte[] pubKeyData = new byte[pubKeySize];
                dis.read(pubKeyData, 0, pubKeySize);
                int privKeySize = dis.readInt();
                byte[] privKeyData = new byte[privKeySize];
                dis.read(privKeyData, 0, privKeySize);
                // decode public key
                X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyData);
                RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);

                // decode private key
                PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privKeyData);
                RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);

                keys = new KeyPair(pubKey, privKey);
            } catch (NoSuchAlgorithmException e) {
                LOG.error("Error loading soft keys : KEYDATA=\"" + keyDataValue + "\"", e);
            } catch (IOException e) {
                LOG.error("Error loading soft keys : KEYDATA=\"" + keyDataValue + "\"", e);
            } catch (InvalidKeySpecException e) {
                LOG.error("Error loading soft keys : KEYDATA=\"" + keyDataValue + "\"", e);
            } catch (DecoderException e) {
                LOG.error("Error loading soft keys : KEYDATA=\"" + keyDataValue + "\"", e);
            }
        } else {
            active = false;
        }

    }

    /**
     * Returns true if the key store was properly loaded
     * 
     * @see org.signserver.server.cryptotokens.ICryptoToken#getCryptoTokenStatus()
     * 
     */
    @Override
    public int getCryptoTokenStatus() {
        if (active) {
            return WorkerStatus.STATUS_ACTIVE;
        }
        return WorkerStatus.STATUS_OFFLINE;
    }

    /**
     * Loads the key store into memory
     * 
     * @see org.signserver.server.cryptotokens.ICryptoToken#activate(java.lang.String)
     */
    @Override
    public void activate(String authenticationcode)
            throws CryptoTokenAuthenticationFailureException, CryptoTokenOfflineException {
        active = keys != null;
    }

    /**
     * Method that clear the key data from memory.
     * 
     * @see org.signserver.server.cryptotokens.ICryptoToken#deactivate()
     */
    @Override
    public boolean deactivate() {
        active = false;
        return true;
    }

    /**
     * Returns the same private key for all purposes.
     * @see org.signserver.server.cryptotokens.ICryptoToken#getPrivateKey(int)
     */
    @Override
    public PrivateKey getPrivateKey(int purpose) throws CryptoTokenOfflineException {

        if (!active) {
            throw new CryptoTokenOfflineException("Signtoken isn't active.");
        }
        return keys.getPrivate();
    }

    /**
     * Returns the same public key for all purposes.
     * @see org.signserver.server.cryptotokens.ICryptoToken#getPublicKey(int)
     */
    @Override
    public PublicKey getPublicKey(int purpose) throws CryptoTokenOfflineException {

        if (!active) {
            throw new CryptoTokenOfflineException("Signtoken isn't active.");
        }
        return keys.getPublic();
    }

    /**
     * Always returns BC.
     * @see org.signserver.server.cryptotokens.ICryptoToken#getPrivateKey(int)
     */
    @Override
    public String getProvider(int providerUsage) {
        return "BC";
    }

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

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

    /**
     * Special method that generates a new key pair that is written to the worker configuration
     * before the request is generated. The new keys aren't activated until reload is issued.
     * 
     */
    @Override
    public ICertReqData genCertificateRequest(ISignerCertReqInfo info, final boolean explicitEccParameters,
            final boolean defaultKey) throws CryptoTokenOfflineException {
        Base64SignerCertReqData retval = null;

        try {
            KeyPair newKeys = KeyTools.genKeys(keySpec, keyAlg);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            byte[] pubKeyData = newKeys.getPublic().getEncoded();
            byte[] prvKeyData = newKeys.getPrivate().getEncoded();
            dos.writeInt(pubKeyData.length);
            dos.write(pubKeyData);
            dos.writeInt(prvKeyData.length);
            dos.write(prvKeyData);

            getWorkerSession().setWorkerProperty(workerId, PROPERTY_KEYDATA,
                    new String(Base64.encode(baos.toByteArray())));

            if (info instanceof PKCS10CertReqInfo) {
                PKCS10CertReqInfo reqInfo = (PKCS10CertReqInfo) info;
                PKCS10CertificationRequest pkcs10;
                PublicKey publicKey = newKeys.getPublic();

                // Handle ECDSA key with explicit parameters
                if (explicitEccParameters && publicKey.getAlgorithm().contains("EC")) {
                    publicKey = ECKeyUtil.publicToExplicitParameters(publicKey, "BC");
                }
                // Generate request
                // 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(newKeys.getPrivate());
                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 e1) {
            LOG.error("Error generating new certificate request : " + e1.getMessage(), e1);
        } catch (NoSuchProviderException e1) {
            LOG.error("Error generating new certificate request : " + e1.getMessage(), e1);
        } catch (InvalidAlgorithmParameterException e1) {
            LOG.error("Error generating new certificate request : " + e1.getMessage(), e1);
        } catch (NamingException e1) {
            LOG.error("Error generating new certificate request : " + e1.getMessage(), e1);
        }
        return retval;
    }

    /**
     * Method not supported
     */
    @Override
    public boolean destroyKey(int purpose) {
        LOG.error("destroyKey method isn't supported");
        return false;
    }

    protected IWorkerSession getWorkerSession() throws NamingException {
        if (workerSession == null) {
            workerSession = ServiceLocator.getInstance().lookupLocal(IWorkerSession.class);
        }
        return workerSession;
    }

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

    @Override
    public KeyStore getKeyStore()
            throws UnsupportedOperationException, CryptoTokenOfflineException, KeyStoreException {
        throw new UnsupportedOperationException("Operation not supported by crypto token.");
    }
}