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.
/*************************************************************************
* *
* EJBCA Community: 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.cesecore.keys.token.p11;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.Hex;
import org.cesecore.config.CesecoreConfiguration;
import org.cesecore.keys.token.p11.exception.NoSuchSlotException;
import org.cesecore.keys.token.p11.exception.P11RuntimeException;
/**
* @version $Id: Pkcs11SlotLabel.java 31385 2019-02-04 16:01:09Z anatom $
*
* Object for handling a PKCS#11 Slot Label.
*
*/
public class Pkcs11SlotLabel {
/** The name of Suns pkcs11 implementation */
public static final String SUN_PKCS11_CLASS = "sun.security.pkcs11.SunPKCS11";
public static final String IAIK_PKCS11_CLASS = "iaik.pkcs.pkcs11.provider.IAIKPkcs11";
public static final String IAIK_JCEPROVIDER_CLASS = "iaik.security.provider.IAIK";
private static final Logger log = Logger.getLogger(Pkcs11SlotLabel.class);
private final static String DELIMETER = ":";
private final Pkcs11SlotLabelType type;
private final String value;
/**
* Use explicit values.
* @param type
* @param value
*/
public Pkcs11SlotLabel(final Pkcs11SlotLabelType type, final String value) {
if(type == null) {
throw new IllegalArgumentException("Type can not be null");
}
this.type = type;
this.value = value == null ? null : value.trim();
}
/**
* Get a string that later could be used to create a new object with {@link Pkcs11SlotLabel#PKCS11Slot(String)}.
* Use it when you want to store a reference to the slot.
* @return the string.
*/
public String getTaggedString() {
return this.type.name() + DELIMETER + this.value;
}
@Override
public String toString() {
return "Slot type: '" + this.type + "'. Slot value: '" + this.value + "'.";
}
/**
* Get provider for the slot.
* @param fileName path name to the P11 module so file or sun config file (only in the case of {@link #type}=={@link Pkcs11SlotLabelType#SUN_FILE})
* @param attributesFile Path to file with P11 attributes to be used when generating keys with the provider. If null a good default will be used.
* @param privateKeyLabel Label that will be set to all private keys generated by the provider. If null no label will be set.
* @return the provider, or null if none is available.
* @throws NoSuchSlotException if no slot as defined by this slot label was found
*
*/
public Provider getProvider(final String fileName, final String attributesFile, final String privateKeyLabel) throws NoSuchSlotException {
if (StringUtils.isEmpty(fileName)) {
throw new IllegalArgumentException("A file name must be supplied.");
}
final File libFile = new File(fileName);
if (!libFile.isFile() || !libFile.canRead()) {
throw new IllegalArgumentException("The file " + fileName + " can't be read.");
}
// We will construct the PKCS11 provider (sun.security..., or iaik...) using reflection, because
// the sun class does not exist on all platforms in jdk5, and we want to be able to compile everything.
if (log.isDebugEnabled()) {
log.debug("slot spec: " + toString());
}
if ( this.type==Pkcs11SlotLabelType.SUN_FILE ) {// if sun cfg file then we do not know the name of the p11 module and we must quit.
try {
List fileContent = new ArrayList<>(Files.readAllLines(Paths.get(fileName), StandardCharsets.UTF_8));
replaceCkaLabel(fileContent, privateKeyLabel);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try( final PrintWriter pw = new PrintWriter(baos) ) {
for (String string : fileContent) {
pw.println(string);
}
}
if (log.isDebugEnabled()) {
log.debug(baos.toString());
}
try ( final ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray()) ) {
return getSunP11Provider(inputStream);
}
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("File " + libFile + " was not found.");
} catch ( IOException e ) {
throw new P11RuntimeException(String.format("The file %s can not be closed after use.", libFile), e);
}
}
final long slot;
final Pkcs11Wrapper p11 = Pkcs11Wrapper.getInstance(libFile); // must be called before any provider is created for libFile
switch (this.type) {
case SLOT_LABEL:
slot = getSlotID(this.value, p11);
if (slot < 0) {
throw new IllegalStateException("Token label '" + this.value + "' not found.");
}
break;
case SLOT_NUMBER:
slot = Long.parseLong(this.value);
break;
case SLOT_INDEX:
//Be generous and allow numbers to act as indexes as well
slot = Long.parseLong((this.value.charAt(0) == 'i' ? this.value.substring(1) : this.value));
break;
//$CASES-OMITTED$
default:
throw new IllegalStateException("This should not ever happen if all type of slots are tested.");
}
{// We will first try to construct the more competent IAIK provider, if it exists in the classpath
final Provider prov = getIAIKP11Provider(slot, libFile, this.type);
if (prov != null) {
return prov;
}
}
{// if that does not exist, we will revert back to use the SUN provider
final Provider prov = getSunP11Provider( getSunP11ProviderInputStream(slot, libFile, this.type, attributesFile, privateKeyLabel) );
if (prov != null) {
return prov;
}
}
log.error("No provider available.");
return null;
}
/** @return a List of "slotId;tokenLabel" in the (indexed) order we get the from the P11 */
public static List getExtendedTokenLabels(final File libFile) {
final List tokenLabels = new ArrayList<>();
final Pkcs11Wrapper p11 = Pkcs11Wrapper.getInstance(libFile);
final long slots[] = p11.getSlotList();
if (log.isDebugEnabled()) {
log.debug("Found numer of slots:\t" + slots.length);
}
for (int i=0; i implClass = (Class) Class.forName(IAIK_PKCS11_CLASS);
if (log.isDebugEnabled()) {
log.debug("Using IAIK PKCS11 provider: " + IAIK_PKCS11_CLASS);
}
// iaik PKCS11 has Properties as constructor argument
ret = implClass.getConstructor(Properties.class).newInstance(new Object[] { prop });
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | IllegalArgumentException |
NoSuchMethodException | SecurityException | ClassNotFoundException e) {
return null;
}
if ( ret==null ) {
return null;
}
try {
// It's not enough just to add the p11 provider. Depending on algorithms we may have to install the IAIK JCE provider as well in order
// to support algorithm delegation
@SuppressWarnings("unchecked")
final Class jceImplClass = (Class) Class.forName(IAIK_JCEPROVIDER_CLASS);
final Provider iaikProvider = jceImplClass.getConstructor().newInstance();
if (Security.getProvider(iaikProvider.getName()) == null) {
log.info("Adding IAIK JCE provider for Delegation: " + IAIK_JCEPROVIDER_CLASS);
Security.addProvider(iaikProvider);
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | IllegalArgumentException |
NoSuchMethodException | SecurityException | ClassNotFoundException e) {
// NOPMD: Ignore, reflection related errors are handled elsewhere
}
return ret;
}
/**
* Get an InputStream to be used to create the Sun provider.
* @param slot Slot list index or slot ID.
* @param libFile P11 module so file.
* @param type a Pkcs11SlotLabelType if the slot is not an ID byt a label for example.
* @param attributesFile Path to file with P11 attributes to be used when generating keys with the provider. If null a good default will be used.
* @param privateKeyLabel Label that will be set to all private keys generated by the provider. If null no label will be set.
* @return the stream
*/
private static InputStream getSunP11ProviderInputStream(final long slot, final File libFile, final Pkcs11SlotLabelType type, final String attributesFile,
String privateKeyLabel) {
// Properties for the SUN PKCS#11 provider
final String sSlot = Long.toString(slot);
final String libFilePath;
try {
libFilePath = libFile.getCanonicalPath();
} catch (IOException e) {
throw new RuntimeException("Could for unknown reason not construct canonical filename.", e);
}
if (log.isDebugEnabled()) {
log.debug(">getSunP11ProviderInputStream: "+sSlot+", "+libFilePath+", "+type.toString()+", "+attributesFile+", "+privateKeyLabel);
}
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try( final PrintWriter pw = new PrintWriter(baos) ) {
pw.println("name = " + libFile.getName() + "-slot" + sSlot);
pw.println("library = " + libFilePath);
if (sSlot != null) {
pw.println("slot" + (type.isEqual(Pkcs11SlotLabelType.SLOT_INDEX) ? "ListIndex" : "") + " = " + sSlot);
}
if (attributesFile != null) {
try {
List fileContent = new ArrayList<>(Files.readAllLines(Paths.get(attributesFile), StandardCharsets.UTF_8));
replaceCkaLabel(fileContent, privateKeyLabel);
for (String string : fileContent) {
pw.println(string);
}
} catch (IOException e) {
throw new IllegalArgumentException("File " + attributesFile + " was not found, or could not be read.", e);
}
//pw.println(new String(attrs));
} else {
// setting the attributes like this should work for most HSMs.
pw.println("attributes(*, CKO_PUBLIC_KEY, *) = {");
pw.println(" CKA_TOKEN = false"); // Do not save public keys permanent in order to save space.
pw.println(" CKA_ENCRYPT = true");
pw.println(" CKA_VERIFY = true");
pw.println(" CKA_WRAP = true");// no harm allowing wrapping of keys. created private keys can not be wrapped anyway since CKA_EXTRACTABLE
// is false.
pw.println("}");
pw.println("attributes(*, CKO_PRIVATE_KEY, *) = {");
pw.println(" CKA_DERIVE = false"); //Amazon CloudHSM will not accept that CKA_DERIVE is present in private key template and will give CKR_TEMPLATE_INCONSISTENT.
pw.println(" CKA_TOKEN = true"); // all created private keys should be permanent. They should not only exist during the session.
pw.println(" CKA_PRIVATE = true"); // always require logon with password to use the key
pw.println(" CKA_SENSITIVE = true"); // not possible to read the key
pw.println(" CKA_EXTRACTABLE = false"); // not possible to wrap the key with another key
pw.println(" CKA_DECRYPT = true");
pw.println(" CKA_SIGN = true");
if (privateKeyLabel != null && privateKeyLabel.length() > 0) {
final String labelStr = " CKA_LABEL = 0h" + new String(Hex.encode(privateKeyLabel.getBytes()));
if (log.isDebugEnabled()) {
log.debug("Setting CKA_LABEL to '"+labelStr+"'");
}
pw.println(labelStr);
}
pw.println(" CKA_UNWRAP = true");// for unwrapping of session keys,
pw.println("}");
if ( CesecoreConfiguration.p11disableHashingSignMechanisms() ) {
pw.println("disabledMechanisms = {");
// by disabling these mechanisms the hashing will be done in the application instead of the HSM.
pw.println(" CKM_SHA1_RSA_PKCS");
pw.println(" CKM_SHA256_RSA_PKCS");
pw.println(" CKM_SHA384_RSA_PKCS");
pw.println(" CKM_SHA512_RSA_PKCS");
pw.println(" CKM_MD2_RSA_PKCS");
pw.println(" CKM_MD5_RSA_PKCS");
pw.println(" CKM_DSA_SHA1");
pw.println(" CKM_ECDSA_SHA1");
pw.println("}");
}
pw.println("attributes(*, CKO_SECRET_KEY, *) = {");
pw.println(" CKA_SENSITIVE = true"); // not possible to read the key
pw.println(" CKA_EXTRACTABLE = false"); // not possible to wrap the key with another key
pw.println(" CKA_ENCRYPT = true");
pw.println(" CKA_DECRYPT = true");
pw.println(" CKA_SIGN = true");
pw.println(" CKA_VERIFY = true");
pw.println(" CKA_WRAP = true");// for unwrapping of session keys,
pw.println(" CKA_UNWRAP = true");// for unwrapping of session keys,
pw.println("}");
}
}
if (log.isDebugEnabled()) {
log.debug(baos.toString());
}
return new ByteArrayInputStream(baos.toByteArray());
}
private static void replaceCkaLabel(List fileContent, String privateKeyLabel) {
// If the attributes file contain a CKA_LABEL without a label, set it to our desired label
for (int i = 0; i < fileContent.size(); i++) {
if (fileContent.get(i).equals(" CKA_LABEL")) { // just the CKA_LABEL with two spaces before and nothing, not even a space after
if (privateKeyLabel != null && privateKeyLabel.length() > 0) {
final String labelStr = " CKA_LABEL = 0h" + new String(Hex.encode(privateKeyLabel.getBytes()));
if (log.isDebugEnabled()) {
log.debug("Setting CKA_LABEL to '"+labelStr+"'");
}
fileContent.set(i, labelStr);
} else {
// If there is no privateKeyLabel, we have to remove the bogus empty CKA_LABEL
log.debug("Removing placeholder CKA_LABEL");
fileContent.remove(i); // .remove() is ok since we break out of the loop here, if we didn't this would be bad practice
}
break;
}
}
}
/**
* Get the provider without taking care of exceptions.
* @param is InputStream for sun configuration file.
* @return The Sun provider
* @throws ClassNotFoundException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
private static Provider getSunP11ProviderNoExceptionHandling(final InputStream is) throws ClassNotFoundException, IllegalArgumentException,
SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
try {
// > JDK8
@SuppressWarnings("rawtypes")
final Class[] paramString = new Class[1];
paramString[0] = String.class;
// The configuration is loaded by the "configure" method (only available since Java 9). JavaDoc for configure method:
// "[...] Note that if this provider cannot be configured in-place, a new provider will be created and returned.
// Therefore, callers should always use the returned provider."
final Method method = Provider.class.getDeclaredMethod("configure", paramString);
final String config = getSunP11ConfigStringFromInputStream(is);
return getSunP11Provider(method, config);
} catch (NoSuchMethodException e) {
// <= JDK8, configuration is loaded by InputStream containing a string of configuration parameters.
@SuppressWarnings("unchecked")
final Class implClass = (Class) Class.forName(SUN_PKCS11_CLASS);
if (log.isDebugEnabled()) {
log.debug("Using JDK8 SUN PKCS11 provider: " + SUN_PKCS11_CLASS);
}
// The old (<=JDK8) Sun PKCS11 has InputStream as constructor argument
return implClass.getConstructor(InputStream.class).newInstance(new Object[] { is });
} catch (IOException e) {
// re-throw an IllegalArgumentException to avoid too invasive changes, maybe not so elegant...
throw new IllegalArgumentException("Could not read InputStream", e);
}
}
private static String getSunP11ConfigStringFromInputStream(final InputStream is) throws IOException {
final StringBuilder configBuilder = new StringBuilder();
// Since Java 9, we need to prepend -- to indicate to the configure() method
// that the config is treated as a string
configBuilder.append("--").append(IOUtils.toString(is, "UTF-8"));
return configBuilder.toString();
}
private static Provider getSunP11Provider(final Method configMethod, final String config) throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
Provider prototype = Security.getProvider("SunPKCS11");
if (log.isDebugEnabled()) {
log.debug("Using JDK9 (and later) SUN PKCS11 provider: SunPKCS11");
}
Provider provider = (Provider) configMethod.invoke(prototype, config);
return provider;
}
/**
* @param is InputStream for sun configuration file.
* @return The Sun provider
*/
private static Provider getSunP11Provider(final InputStream is) {
try {
return getSunP11ProviderNoExceptionHandling(is);
} catch (Exception e) {
final String msg = "Error constructing pkcs11 provider: " + e.getMessage();
log.error(msg);
throw new IllegalStateException(msg, e);
}
}
/**
* Creating dummy provider just to have C_Initialize executed with multiple
* thread argument. {@link Pkcs11Wrapper#getInstance(File)} should call it
* before making any PKCS#11 calls. If not then C_Initialize will be called
* with null argument causing multi threading to be disabled for the token.
*
* There is a boolean in the sun code ensuring that C_Initialize is only
* called once.
* To check this implementation the p11 spy utils could be used. Check that
* it is only one C_Initialize call and that null is not passed.
* @param a file with the path of the p11 module on which C_Finalize should
* be called.
*/
static void doC_Initialize(final File libFile) {
try {
getSunP11ProviderNoExceptionHandling( getSunP11ProviderInputStream(-1, libFile, Pkcs11SlotLabelType.SLOT_NUMBER, null, null) );
} catch (InvocationTargetException e) {
// the p11 module don't like the bogus arguments and throws an exception but we don't bother about this since
// C_Initialize has already been called with multithread arguments.
log.debug("Get dummy sun provider throws an exception for "+libFile.getPath()+". This is OK.", e);
} catch (Exception e) {
final String msg = "Error constructing pkcs11 provider: " + e.getMessage();
log.error(msg);
throw new IllegalStateException(msg, e);
}
}
/**
* Creates a SUN or IAIK PKCS#11 provider using the passed in pkcs11 library. First we try to see if the IAIK provider is available, because it
* supports more algorithms. If the IAIK provider is not available in the classpath, we try the SUN provider.
*
* @param sSlot
* The value of the slot, which may be a number ([0...9]*), an index i[0...9] or a label, but may also be labels matching the former.
* To solve this ambiguity, slots will be presumed to be numbers or indexes if the names match, and if no slot is found by that number
* or index will then be presumed to be labels (for legacy reasons).
* @param fileName
* the manufacturers provided pkcs11 library (.dll or .so) or config file name if slot is null
* @param attributesFile
* a file specifying PKCS#11 attributes (used mainly for key generation) in the format specified in the
* "JavaTM PKCS#11 Reference Guide", http://java.sun.com/javase/6/docs/technotes/guides/security/p11guide.html
*
* Example contents of attributes file:
*
* attributes(generate,CKO_PRIVATE_KEY,*) = { CKA_PRIVATE = true CKA_SIGN = true CKA_DECRYPT = true CKA_TOKEN = true }
*
* See also html documentation for PKCS#11 HSMs in EJBCA.
*
* @return AuthProvider of type "sun.security.pkcs11.SunPKCS11", or null if none is available
* @throws NoSuchSlotException is no slot as defined by sSlot and slotLabelType was found
*/
public static Provider getP11Provider(final String sSlot, Pkcs11SlotLabelType slotLabelType, final String fileName, final String attributesFile) throws NoSuchSlotException {
return getP11Provider(sSlot, slotLabelType, fileName, attributesFile, null);
}
/**
* Creates a SUN or IAIK PKCS#11 provider using the passed in pkcs11 library. First we try to see if the IAIK provider is available, because it
* supports more algorithms. If the IAIK provider is not available in the classpath, we try the SUN provider.
*
* @param sSlot
* The value of the slot, which may be a number ([0...9]*), an index i[0...9] or a label, but may also be labels matching the former.
* To solve this ambiguity, slots will be presumed to be numbers or indexes if the names match, and if no slot is found by that number
* or index will then be presumed to be labels (for legacy reasons). Can be null if slotLabelType is SUN_FILE, then the slot must be specified in the attributesFile.
* @param fileName
* the manufacturers provided pkcs11 library (.dll or .so) or config file name if slot is null
* @param attributesFile
* a file specifying PKCS#11 attributes (used mainly for key generation) in the format specified in the
* "JavaTM PKCS#11 Reference Guide", http://java.sun.com/javase/6/docs/technotes/guides/security/p11guide.html
*
* Example contents of attributes file:
*
* attributes(generate,CKO_PRIVATE_KEY,*) = { CKA_PRIVATE = true CKA_SIGN = true CKA_DECRYPT = true CKA_TOKEN = true }
*
* See also html documentation for PKCS#11 HSMs in EJBCA.
* @param privateKeyLabel
* The private key label to be set to generated keys. null means no label.
*
* @return AuthProvider of type "sun.security.pkcs11.SunPKCS11", or null if none is available
* @throws NoSuchSlotException if no slot as defined by this label was found
*/
public static Provider getP11Provider(final String sSlot, final Pkcs11SlotLabelType slotLabelType, final String fileName,
final String attributesFile, final String privateKeyLabel) throws NoSuchSlotException {
if ((sSlot == null || sSlot.length() < 1) && slotLabelType!=Pkcs11SlotLabelType.SUN_FILE) {
return null;
}
final Pkcs11SlotLabel slotSpec = new Pkcs11SlotLabel(slotLabelType, sSlot);
return slotSpec.getProvider(fileName, attributesFile, privateKeyLabel);
}
}