Package org.ejbca.core.model.ca.catoken

Source Code of org.ejbca.core.model.ca.catoken.BaseCAToken

/*************************************************************************
*                                                                       *
*  EJBCA: The OpenSource Certificate Authority                          *
*                                                                       *
*  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.ejbca.core.model.ca.catoken;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.ejbca.config.EjbcaConfiguration;
import org.ejbca.core.model.InternalResources;
import org.ejbca.core.model.SecConst;
import org.ejbca.util.StringTools;
import org.ejbca.util.keystore.KeyTools;


/**
* @author lars
* @version $Id: BaseCAToken.java 10358 2010-11-03 18:34:23Z mikekushner $
*/
public abstract class BaseCAToken implements ICAToken {

    /** Log4j instance */
    private static final Logger log = Logger.getLogger(BaseCAToken.class);
    /** Internal localization of logs and errors */
    private static final InternalResources intres = InternalResources.getInstance();

    /** Used for signatures */
    private String mJcaProviderName = null;
    /** Used for encrypt/decrypt, can be same as for signatures for example for pkcs#11 */
    private String mJceProviderName = null;

    private KeyStrings keyStrings;
    protected String sSlotLabel = null;
    private Map<String, KeyPair> mKeys;
  private String mAuthCode;

    public BaseCAToken() {
        super();
    }
    public BaseCAToken(String providerClass) throws InstantiationException {
        try {
            Class.forName(providerClass);
        } catch (ClassNotFoundException e) {
            throw new InstantiationException("Class not found: "+providerClass);
        }
    }
    protected void autoActivate() {
        if ( this.mKeys==null && this.mAuthCode!=null ) {
            try {
              log.debug("Trying to autoactivate CAToken");
                activate(this.mAuthCode);
            } catch (Exception e) {
                log.debug(e);
            }
        }
    }
    /**
     * do we permit extractable? Only SW keys should be permited to be extractable,
     * @return false if the key must not be extractable
     */
    protected boolean doPermitExtractablePrivateKey() {
        return false;
    }
    private void testKey( KeyPair pair ) throws Exception {
      if (log.isDebugEnabled()) {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final PrintStream ps = new PrintStream(baos);
        KeyTools.printPublicKeyInfo(pair.getPublic(), ps);
        ps.flush();
        log.debug("Using of "+baos.toString());         
      }
        if ( !doPermitExtractablePrivateKey() && KeyTools.isPrivateKeyExtractable(pair.getPrivate()) ) {
            String msg = intres.getLocalizedMessage("catoken.extractablekey", EjbcaConfiguration.doPermitExtractablePrivateKeys());
            if ( !EjbcaConfiguration.doPermitExtractablePrivateKeys() ) {
                throw new InvalidKeyException(msg);
            }
            log.info(msg);
        }
        KeyTools.testKey(pair.getPrivate(), pair.getPublic(), getProvider());
    }
    /**
     * @param keyStore
     * @param authCode
     * @throws Exception
     */
    protected void setKeys(KeyStore keyStore, String authCode) throws Exception {
        this.mKeys = null;
        final String keyAliases[] = this.keyStrings.getAllStrings();
        final Map<String, KeyPair> mTmp = new Hashtable<String, KeyPair>();
        for ( int i=0; i<keyAliases.length; i++ ) {
            PrivateKey privateK =
                (PrivateKey)keyStore.getKey(keyAliases[i],
                                            (authCode!=null && authCode.length()>0)? authCode.toCharArray():null);
            if (privateK == null) {
                log.error(intres.getLocalizedMessage("catoken.noprivate", keyAliases[i]));
            if (log.isDebugEnabled()) {
              for (int j=0; j<keyAliases.length;j++) {
                log.debug("Existing alias: "+keyAliases[j]);
              }
            }             
            } else {
                PublicKey publicK = readPublicKey(keyStore, keyAliases[i]);
                if ( publicK != null ) {
                    KeyPair keyPair = new KeyPair(publicK, privateK);
                    mTmp.put(keyAliases[i], keyPair);             
                }             
            }
        }
        for ( int i=0; i<keyAliases.length; i++ ) {
            KeyPair pair = mTmp.get(keyAliases[i]);
        if (log.isDebugEnabled()) {
          log.debug("Testing keys with alias "+keyAliases[i]);
        }
            if (pair == null) {
                log.info("No keys with alias "+keyAliases[i]+" exists.");
            } else {
                testKey(pair)// Test signing for the KeyPair (this could theoretically fail if singing is not allowed by the provider for this key)
                if (log.isDebugEnabled()) {
                    log.debug("Key with alias "+keyAliases[i]+" tested.");             
                }             
            }
        }
        this.mKeys = mTmp;
        if ( getCATokenStatus()!=ICAToken.STATUS_ACTIVE ) {
            throw new Exception("Activation test failed");
        }
    }

    /**
     * @param keyStore
     * @param alias
     * @return
     * @throws Exception
     */
    protected PublicKey readPublicKey(KeyStore keyStore, String alias) throws Exception {
      Certificate cert = keyStore.getCertificate(alias);
      PublicKey pubk = null;
      if (cert != null) {
        pubk = cert.getPublicKey();
      } else {
            log.error(intres.getLocalizedMessage("catoken.nopublic", alias));
        if (log.isDebugEnabled()) {
          Enumeration en = keyStore.aliases();
          while (en.hasMoreElements()) {
            log.debug("Existing alias: "+(String)en.nextElement());
          }
        }
      }
      return pubk;
    }

    protected void init(String sSlotLabelKey, Properties properties, String signaturealgorithm, boolean doAutoActivate) {
      if (log.isDebugEnabled()) {
        log.debug(">init: sSlotLabelKey="+sSlotLabelKey+", Signaturealg="+signaturealgorithm);
      }
      // Set basic properties that are of dynamic nature
      updateProperties(properties);
      // Set properties that can not change dynamically
        this.sSlotLabel = getSlotLabel(sSlotLabelKey, properties);
        if ( doAutoActivate ) {
            autoActivate();         
        }
      if (log.isDebugEnabled()) {
        log.debug("<init: sSlotLabelKey="+sSlotLabelKey+", Signaturealg="+signaturealgorithm);
      }
    } // init
   
    /** @see ICAToken#updateProperties(Properties)
     */
    public void updateProperties(Properties properties) {
      if (log.isDebugEnabled()) {
        // This is only a sections for debug logging. If we have enabled debug logging we don't want to display any password in the log.
        // These properties may contain autoactivation PIN codes and we will, only when debug logging, replace this with "hidden".
        if ( properties.containsKey(ICAToken.AUTOACTIVATE_PIN_PROPERTY) || properties.containsKey("PIN") ) {
          Properties prop = new Properties();
          prop.putAll(properties);
          if (properties.containsKey(ICAToken.AUTOACTIVATE_PIN_PROPERTY)) {
              prop.setProperty(ICAToken.AUTOACTIVATE_PIN_PROPERTY, "hidden");           
          }
          if (properties.containsKey("PIN")) {
              prop.setProperty("PIN", "hidden");           
          }
            log.debug("Prop: "+(prop!=null ? prop.toString() : "null"));
        } else {
          // If no autoactivation PIN codes exists we can debug log everything as original.
            log.debug("Properties: "+(properties!=null ? properties.toString() : "null"));         
        }
      } // if (log.isDebugEnabled())
        this.keyStrings = new KeyStrings(properties);
        this.mAuthCode = BaseCAToken.getAutoActivatePin(properties);
    } // updateProperties
   
    /** Extracts the slotLabel that is used for many tokens in construction of the provider
     *
     * @param sSlotLabelKey which key in the properties that gives us the label
     * @param properties CA token properties
     * @return String with the slot label, trimmed from whitespace
     */
  protected static String getSlotLabel(String sSlotLabelKey, Properties properties) {
    String ret = null;
    if (sSlotLabelKey != null && properties!=null) {
            ret = properties.getProperty(sSlotLabelKey);
            if (ret != null) {
              ret = ret.trim();
            }
        }
        return ret;
  }
   
    protected static String getAutoActivatePin(Properties properties) {
        final String pin = properties.getProperty(ICAToken.AUTOACTIVATE_PIN_PROPERTY);
        if (pin != null) {
            return StringTools.passwordDecryption(pin, "autoactivation pin");
        }
        if (log.isDebugEnabled()) {
          log.debug("Not using autoactivation pin");
        }
        return null;
    }
    /** Sets auto activation pin in passed in properties. Also returns the string format of the
     * autoactivation properties:
     * pin mypassword
     *
     * @param properties a Properties bag where to set the auto activation pin, can be null if you only want to create the return string, does not set a null or empty password
     * @param pin the activation password
     * @param encrypt if the PIN should be encrypted with a simple built in encryption with only purpose of hiding the password from simple viewing. No strong security from this encryption
     * @return A string that can be used to "setProperties" of a CAToken or null if pin is null or an empty string, this can safely be ignored if you don't know what to do with it
     */
    public static String setAutoActivatePin(Properties properties, String pin, boolean encrypt) {     
    String ret = null;
      if (StringUtils.isNotEmpty(pin)) {
        String authcode = pin;
        if (encrypt) {
          try {
          authcode = StringTools.pbeEncryptStringWithSha256Aes192(pin);
        } catch (Exception e) {
          log.error(intres.getLocalizedMessage("catoken.nopinencrypt"), e);
          authcode = pin;
        }
        }
        if (properties != null) {       
          properties.setProperty(ICAToken.AUTOACTIVATE_PIN_PROPERTY, authcode);
        }
        ret = ICAToken.AUTOACTIVATE_PIN_PROPERTY + " " + authcode;       
      }
      return ret;
    }
   
    /** Sets both signature and encryption providers. If encryption provider is the same as signature provider this
     * class name can be null.
     * @param jcaProviderClassName signature provider class name
     * @param jceProviderClassName encryption provider class name, can be null
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @see {@link #setJCAProvider(Provider)}
     */
    protected void setProviders(String jcaProviderClassName, String jceProviderClassName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
      Provider jcaProvider = (Provider)Class.forName(jcaProviderClassName).newInstance();
        setProvider(jcaProvider);
        this.mJcaProviderName = jcaProvider.getName();
        if (jceProviderClassName != null) {
          try {
            Provider jceProvider = (Provider)Class.forName(jceProviderClassName).newInstance();
            setProvider(jceProvider);         
            this.mJceProviderName = jceProvider.getName();
          } catch (Exception e) {
            log.error(intres.getLocalizedMessage("catoken.jceinitfail"), e);
          }
        } else {
          this.mJceProviderName = null;
        }
    }
    /** If we only have one provider to handle both JCA and JCE, and perhaps it is not so straightforward to
     * create the provider (for example PKCS#11 provider), we can create the provider in sub class and set it
     * here, instead of calling setProviders.
     *
     * @param prov the fully constructed Provider
     * @see #setProviders(String, String)
     */
    protected void setJCAProvider(Provider prov) {
      setProvider(prov);
      this.mJcaProviderName = prov!=null ? prov.getName() : null;
    }
    /** If we don't use any of the methods to set a specific provider, but use some already existing provider
     * we should set the name of that provider at least.
     * @param pName the provider name as retriever from Provider.getName()
     */
    protected void setJCAProviderName(String pName) {
      this.mJcaProviderName = pName;
    }
    private void setProvider(Provider prov) {
        if ( prov!=null ) {
          String pName = prov.getName();
            if (pName.startsWith("LunaJCA")) {
              // Luna Java provider does not contain support for RSA/ECB/PKCS1Padding but this is
              // the same as the alias below on small amounts of data 
                prov.put("Alg.Alias.Cipher.RSA/NONE/NoPadding","RSA//NoPadding");
                prov.put("Alg.Alias.Cipher.1.2.840.113549.1.1.1","RSA//NoPadding");
                prov.put("Alg.Alias.Cipher.RSA/ECB/PKCS1Padding","RSA//PKCS1v1_5");
                prov.put("Alg.Alias.Cipher.1.2.840.113549.3.7","DES3/CBC/PKCS5Padding");
            }
            if ( Security.getProvider(pName)==null ) {
                Security.addProvider( prov );
            }
            if ( Security.getProvider(pName)==null ) {
                throw new ProviderException("Not possible to install provider: "+pName);
            }
        } else {
          if (log.isDebugEnabled()) {
            log.debug("No provider passed to setProvider()");
          }
        }
    }

    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#activate(java.lang.String)
     */
    public abstract void activate(String authCode) throws CATokenOfflineException, CATokenAuthenticationFailedException;
   
    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#deactivate()
     */
    public boolean deactivate() throws Exception {
    String msg = intres.getLocalizedMessage("catoken.deactivate");
        log.info(msg);
        this.mKeys = null;
        return true
    }

    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#getPrivateKey(int)
     */
    public PrivateKey getPrivateKey(int purpose)
        throws CATokenOfflineException {
      autoActivate();
      String keystring = this.keyStrings.getString(purpose);
        KeyPair keyPair = this.mKeys!=null ?
            (KeyPair)this.mKeys.get(keystring) :
            null;
        if ( keyPair==null ) {
        String msg = intres.getLocalizedMessage("catoken.errornosuchkey", keystring, purpose);
            throw new CATokenOfflineException(msg);
        }
        return keyPair.getPrivate();
    }

    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#getPublicKey(int)
     */
    public PublicKey getPublicKey(int purpose)
        throws CATokenOfflineException {
      autoActivate();
      String keystring = this.keyStrings.getString(purpose);
        KeyPair keyPair = this.mKeys != null ? (KeyPair) this.mKeys.get(keystring) : null;
        if ( keyPair==null ) {
        String msg = intres.getLocalizedMessage("catoken.errornosuchkey", keystring, purpose);
            throw new CATokenOfflineException(msg);
        }
        return keyPair.getPublic();
    }

    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#getKeyLabel(int)
     */
    public String getKeyLabel(int purpose) {
      return this.keyStrings.getString(purpose);
    }

    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#getProvider()
     */
    public String getProvider() {
        return this.mJcaProviderName;
    }

    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#getJCEProvider()
     */
    public String getJCEProvider() {
      // If we don't have a specific JCE provider, it is most likely the same
      // as the JCA provider
      if (this.mJceProviderName == null) {
        return this.mJcaProviderName;
      }
      return this.mJceProviderName;
    }

  /* (non-Javadoc)
   * @see org.ejbca.core.model.ca.caadmin.ICAToken#getCATokenStatus()
   */
    public int getCATokenStatus() {     
    if (log.isTraceEnabled()) {
      log.trace(">getCATokenStatus");
    }
      autoActivate();
      int ret = ICAToken.STATUS_OFFLINE;
      // If we have no keystrings, no point in continuing...
      if (this.keyStrings != null) {
          String strings[] = this.keyStrings.getAllStrings();
          int i=0;
          while( strings!=null && i<strings.length && this.mKeys!=null && this.mKeys.get(strings[i])!=null ) {
            i++;                       
          }
          // If we don't have any keys for the strings, or we don't have enough keys for the strings, no point in continuing...
          if ( strings!=null && i>=strings.length) {
              PrivateKey privateKey;
              PublicKey publicKey;
              try {
                privateKey = getPrivateKey(SecConst.CAKEYPURPOSE_KEYTEST);
                publicKey = getPublicKey(SecConst.CAKEYPURPOSE_KEYTEST);
              } catch (CATokenOfflineException e) {
                privateKey = null;
                publicKey = null;
                if (log.isDebugEnabled()) {
                  log.debug("no test key defined");
                }
              }
              if ( privateKey!=null && publicKey!=null ) {
                //Check that that the testkey is usable by doing a test signature.
                try{
                  testKey(new KeyPair(publicKey, privateKey));
                  // If we can test the testkey, we are finally active!
                    ret = ICAToken.STATUS_ACTIVE;
                } catch( Throwable th ){
                  log.error(intres.getLocalizedMessage("catoken.activationtestfail"), th);
                }
              }
          }
      }
    if (log.isTraceEnabled()) {
      log.trace("<getCATokenStatus: "+ret);
    }
      return ret;
    }
    /* (non-Javadoc)
     * @see org.ejbca.core.model.ca.catoken.ICAToken#reset()
     */
    public void reset() {
        // do nothing. the implementing class decides whether something could be done to get the HSM working after a failure.
    }
    public boolean isActive() {
        return this.mKeys != null;
    }
}
TOP

Related Classes of org.ejbca.core.model.ca.catoken.BaseCAToken

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.