Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* 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.cesecore.keys.token;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.jce.ECKeyUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.cesecore.config.CesecoreConfiguration;
import org.cesecore.internal.InternalResources;
import org.cesecore.keys.util.KeyTools;
import org.cesecore.util.StringTools;
/**
* Base class for crypto tokens handling things that are common for all crypto tokens, hard or soft.
*
* @version $Id: BaseCryptoToken.java 30503 2018-11-14 13:45:37Z anatom $
*/
public abstract class BaseCryptoToken implements CryptoToken {
private static final long serialVersionUID = 2133644669863292622L;
/** Log4j instance */
private static final Logger log = Logger.getLogger(BaseCryptoToken.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 char[] mAuthCode;
private Properties properties;
private int id;
/** The java KeyStore backing the Crypto Token */
protected transient CachingKeyStoreWrapper keyStore;
/** public constructor */
public BaseCryptoToken() {
super();
}
protected void setKeyStore(KeyStore keystore) throws KeyStoreException {
if (keystore==null) {
this.keyStore = null;
} else {
this.keyStore = new CachingKeyStoreWrapper(keystore, CesecoreConfiguration.isKeyStoreCacheEnabled());
}
}
/**
* Return the key store for this crypto token.
*
* @return the keystore.
* @throws CryptoTokenOfflineException if Crypto Token is not available or connected.
*/
protected CachingKeyStoreWrapper getKeyStore() throws CryptoTokenOfflineException {
autoActivate();
if (this.keyStore == null) {
final String msg = intres.getLocalizedMessage("token.errorinstansiate", mJcaProviderName, "keyStore ("+id+") == null");
throw new CryptoTokenOfflineException(msg);
}
return this.keyStore;
}
/**
* TODO: This structure is confusing, with exceptions being thrown, caught, ignored and then rethrown at a later stage. Please fix.
*
*/
protected void autoActivate() {
if ((this.mAuthCode != null) && (this.keyStore == null)) {
try {
if (log.isDebugEnabled()) {
log.debug("Trying to autoactivate CryptoToken");
}
activate(this.mAuthCode);
} catch (Exception e) {
log.debug(e);
}
}
}
/**
* Do we permit extractable private keys? Only SW keys should be permitted to be extractable, an overriding crypto token class can override this
* value.
*
* @return false if the key must not be extractable
*/
public boolean doPermitExtractablePrivateKey() {
return getProperties().containsKey(CryptoToken.ALLOW_EXTRACTABLE_PRIVATE_KEY) &&
Boolean.parseBoolean(getProperties().getProperty(CryptoToken.ALLOW_EXTRACTABLE_PRIVATE_KEY));
}
/** Similar to the method above, but only applies for internal testing of keys. This method is called during testKeyPair to verify that a key
* that is extractable can never be used, unless we allow extractable private keys. Used for PKCS#11 (HSMs) to ensure that they are configured
* correctly. On a PKCS11 Crypto Token, this should return the same as doPermitExtractablePrivateKey(), on a Soft Crypto Token this should always return true.
*
* @return false if the key must not be extractable, this will throw an error if the key is extractable when crypto token tries to test it.
*/
public abstract boolean permitExtractablePrivateKeyForTest();
@Override
public void testKeyPair(final String alias) throws InvalidKeyException, CryptoTokenOfflineException { // NOPMD:this is not a junit test
final PrivateKey privateKey = getPrivateKey(alias);
final PublicKey publicKey = getPublicKey(alias);
testKeyPair(alias, publicKey, privateKey);
}
@Override
public void testKeyPair(final String alias, PublicKey publicKey, PrivateKey privateKey) throws InvalidKeyException { // NOPMD:this is not a junit test
if (log.isDebugEnabled()) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
KeyTools.printPublicKeyInfo(publicKey, ps);
ps.flush();
log.debug("Testing key of type " + baos.toString());
}
if (!permitExtractablePrivateKeyForTest() && KeyTools.isPrivateKeyExtractable(privateKey)) {
String msg = intres.getLocalizedMessage("token.extractablekey", CesecoreConfiguration.isPermitExtractablePrivateKeys());
if (!CesecoreConfiguration.isPermitExtractablePrivateKeys()) {
throw new InvalidKeyException(msg);
}
log.info(msg);
}
KeyTools.testKey(privateKey, publicKey, getSignProviderName());
}
/**
* Reads the public key object, does so from the certificate retrieved from the alias from the KeyStore.
*
* @param alias alias the key alias to retrieve from the token
* @param warn if we should log a warning if the key does not exist
* @return the public key for the certificate represented by the given alias.
* @throws KeyStoreException if the keystore has not been initialized.
* @throws CryptoTokenOfflineException if Crypto Token is not available or connected.
*/
protected PublicKey readPublicKey(String alias, boolean warn) throws KeyStoreException, CryptoTokenOfflineException {
try {
Certificate cert = getKeyStore().getCertificate(alias);
PublicKey pubk = null;
if (cert != null) {
pubk = cert.getPublicKey();
} else if (warn) {
log.warn(intres.getLocalizedMessage("token.nopublic", alias));
if (log.isDebugEnabled()) {
Enumeration en = getKeyStore().aliases();
while (en.hasMoreElements()) {
log.debug("Existing alias: " + en.nextElement());
}
}
}
return pubk;
} catch (ProviderException e) {
throw new CryptoTokenOfflineException(e);
}
}
/**
* Initiates the class members of this crypto token.
*
* @param properties A Properties object containing properties for this token.
* @param doAutoActivate Set true if activation of this crypto token should happen in this method.
* @param id ID of this crypto token.
*/
protected void init(Properties properties, boolean doAutoActivate, int id) {
if (log.isDebugEnabled()) {
log.debug(">init: doAutoActivate=" + doAutoActivate);
}
this.id = id;
// Set basic properties that are of dynamic nature
setProperties(properties);
// Set properties that can not change dynamically
if (doAutoActivate) {
autoActivate();
}
if (log.isDebugEnabled()) {
log.debug(" 0) ? mAuthCode : null);
if (privateK == null) {
if (warn) {
log.warn(intres.getLocalizedMessage("token.noprivate", alias));
if (log.isDebugEnabled()) {
final Enumeration aliases;
aliases = getKeyStore().aliases();
while (aliases.hasMoreElements()) {
log.debug("Existing alias: " + aliases.nextElement());
}
}
}
final String msg = intres.getLocalizedMessage("token.errornosuchkey", alias);
throw new CryptoTokenOfflineException(msg);
}
return privateK;
} catch (KeyStoreException e) {
throw new CryptoTokenOfflineException(e);
} catch (UnrecoverableKeyException e) {
throw new CryptoTokenOfflineException(e);
} catch (NoSuchAlgorithmException e) {
throw new CryptoTokenOfflineException(e);
} catch (ProviderException e) {
throw new CryptoTokenOfflineException(e);
}
}
@Override
public PublicKey getPublicKey(final String alias) throws CryptoTokenOfflineException {
return getPublicKey(alias, true);
}
/** @see #getPublicKey(String)
* @param warn if we should log a warning if the key does not exist
*/
private PublicKey getPublicKey(final String alias, boolean warn) throws CryptoTokenOfflineException {
// Auto activate is done in the call to getKeyStore below (from readPublicKey)
try {
PublicKey publicK = readPublicKey(alias, warn);
if (publicK == null) {
final String msg = intres.getLocalizedMessage("token.errornosuchkey", alias);
throw new CryptoTokenOfflineException(msg);
}
final String str = getProperties().getProperty(CryptoToken.EXPLICIT_ECC_PUBLICKEY_PARAMETERS);
final boolean explicitEccParameters = Boolean.parseBoolean(str);
if (explicitEccParameters && publicK.getAlgorithm().equals("EC")) {
if (log.isDebugEnabled()) {
log.debug("Using explicit parameter encoding for ECC key.");
}
publicK = ECKeyUtil.publicToExplicitParameters(publicK, BouncyCastleProvider.PROVIDER_NAME);
}
return publicK;
} catch (KeyStoreException e) {
throw new CryptoTokenOfflineException(e);
} catch (NoSuchProviderException e) {
throw new CryptoTokenOfflineException(e);
} catch (IllegalArgumentException e) {
throw new CryptoTokenOfflineException(e);
} catch (NoSuchAlgorithmException e) {
throw new CryptoTokenOfflineException(e);
}
}
@Override
public Key getKey(final String alias) throws CryptoTokenOfflineException {
return getKey(alias, true);
}
/** see {@link #getKey(String)}
* @param warn if we should log a warning if the key does not exist
*/
private Key getKey(final String alias, final boolean warn) throws CryptoTokenOfflineException {
// Auto activate is done in the call to getKeyStore below
try {
Key key = getKeyStore().getKey(alias, (mAuthCode != null && mAuthCode.length > 0) ? mAuthCode : null);
if (key == null) {
// Do we have it stored as a soft key in properties?
key = getKeyFromProperties(alias);
if (key == null) {
if (warn) {
log.warn(intres.getLocalizedMessage("token.errornosuchkey", alias));
if (log.isDebugEnabled()) {
Enumeration aliases;
aliases = getKeyStore().aliases();
while (aliases.hasMoreElements()) {
log.debug("Existing alias: " + aliases.nextElement());
}
}
}
final String msg = intres.getLocalizedMessage("token.errornosuchkey", alias);
throw new CryptoTokenOfflineException(msg);
}
}
return key;
} catch (KeyStoreException e) {
throw new CryptoTokenOfflineException(e);
} catch (UnrecoverableKeyException e) {
throw new CryptoTokenOfflineException(e);
} catch (NoSuchAlgorithmException e) {
throw new CryptoTokenOfflineException(e);
} catch (ProviderException e) {
throw new CryptoTokenOfflineException(e);
}
}
private Key getKeyFromProperties(String alias) {
Key key = null;
Properties prop = getProperties();
String str = prop.getProperty(alias);
if (StringUtils.isNotEmpty(str)) {
// TODO: unwrapping with rsa key is also needed later on
try {
PrivateKey privK = getPrivateKey("symwrap");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", getEncProviderName());
cipher.init(Cipher.UNWRAP_MODE, privK);
byte[] bytes = Hex.decode(str);
// TODO: hardcoded AES for now
key = cipher.unwrap(bytes, "AES", Cipher.SECRET_KEY);
} catch (CryptoTokenOfflineException e) {
log.debug(e);
} catch (NoSuchAlgorithmException e) {
log.debug(e);
} catch (NoSuchProviderException e) {
log.debug(e);
} catch (NoSuchPaddingException e) {
log.debug(e);
} catch (InvalidKeyException e) {
log.debug(e);
}
}
return key;
}
@Override
public void reset() {
// do nothing. the implementing class decides whether something could be done to get the HSM working after a failure.
}
@Override
public int getTokenStatus() {
// Auto activate is done in the call to getKeyStore below
int ret = CryptoToken.STATUS_OFFLINE;
try {
getKeyStore();
ret = CryptoToken.STATUS_ACTIVE;
} catch (CryptoTokenOfflineException e) {
// NOPMD, ignore status is offline
}
return ret;
}
@Override
public List getAliases() throws KeyStoreException, CryptoTokenOfflineException {
return Collections.list(getKeyStore().aliases());
}
@Override
public boolean isAutoActivationPinPresent() {
return getAutoActivatePin(getProperties()) != null;
}
}