/*
*
* Copyright (c) 2013 - 2017 Lijun Liao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
*
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the XiPKI software without
* disclosing the source code of your own applications.
*
* For more information, please contact Lijun Liao at this
* address: lijun.liao@gmail.com
*/
package org.xipki.commons.security.pkcs11.emulator;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.util.encoders.Hex;
import org.eclipse.jdt.annotation.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.commons.common.util.IoUtil;
import org.xipki.commons.common.util.LogUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.commons.common.util.StringUtil;
import org.xipki.commons.security.HashAlgoType;
import org.xipki.commons.security.X509Cert;
import org.xipki.commons.security.exception.P11TokenException;
import org.xipki.commons.security.exception.P11UnknownEntityException;
import org.xipki.commons.security.exception.XiSecurityException;
import org.xipki.commons.security.pkcs11.AbstractP11Slot;
import org.xipki.commons.security.pkcs11.P11Constants;
import org.xipki.commons.security.pkcs11.P11EntityIdentifier;
import org.xipki.commons.security.pkcs11.P11Identity;
import org.xipki.commons.security.pkcs11.P11MechanismFilter;
import org.xipki.commons.security.pkcs11.P11ObjectIdentifier;
import org.xipki.commons.security.pkcs11.P11SlotIdentifier;
import org.xipki.commons.security.pkcs11.P11SlotRefreshResult;
import org.xipki.commons.security.util.KeyUtil;
import org.xipki.commons.security.util.X509Util;
/**
* @author Lijun Liao
* @since 2.0.0
*/
class EmulatorP11Slot extends AbstractP11Slot {
private static class InfoFilenameFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(INFO_FILE_SUFFIX);
}
}
// slotinfo
private static final String FILE_SLOTINFO = "slot.info";
private static final String PROP_NAMED_CURVE_SUPPORTED = "namedCurveSupported";
private static final String DIR_PRIV_KEY = "privkey";
private static final String DIR_PUB_KEY = "pubkey";
private static final String DIR_CERT = "cert";
private static final String INFO_FILE_SUFFIX = ".info";
private static final String VALUE_FILE_SUFFIX = ".value";
private static final String PROP_ID = "id";
private static final String PROP_LABEL = "label";
private static final String PROP_SHA1SUM = "sha1";
private static final String PROP_ALGORITHM = "algorithm";
// RSA
private static final String PROP_RSA_MODUS = "modus";
private static final String PROP_RSA_PUBLIC_EXPONENT = "publicExponent";
// DSA
private static final String PROP_DSA_PRIME = "prime"; // p
private static final String PROP_DSA_SUBPRIME = "subprime"; // q
private static final String PROP_DSA_BASE = "base"; // g
private static final String PROP_DSA_VALUE = "value"; // y
// EC
private static final String PROP_EC_ECDSA_PARAMS = "ecdsaParams";
private static final String PROP_EC_EC_POINT = "ecPoint";
private static final long[] supportedMechs = new long[]{
P11Constants.CKM_DSA_KEY_PAIR_GEN,
P11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN,
P11Constants.CKM_EC_KEY_PAIR_GEN,
P11Constants.CKM_RSA_X_509,
P11Constants.CKM_RSA_PKCS,
P11Constants.CKM_SHA1_RSA_PKCS,
P11Constants.CKM_SHA224_RSA_PKCS,
P11Constants.CKM_SHA256_RSA_PKCS,
P11Constants.CKM_SHA384_RSA_PKCS,
P11Constants.CKM_SHA512_RSA_PKCS,
P11Constants.CKM_SHA3_224_RSA_PKCS,
P11Constants.CKM_SHA3_256_RSA_PKCS,
P11Constants.CKM_SHA3_384_RSA_PKCS,
P11Constants.CKM_SHA3_512_RSA_PKCS,
P11Constants.CKM_RSA_PKCS_PSS,
P11Constants.CKM_SHA1_RSA_PKCS_PSS,
P11Constants.CKM_SHA224_RSA_PKCS_PSS,
P11Constants.CKM_SHA256_RSA_PKCS_PSS,
P11Constants.CKM_SHA384_RSA_PKCS_PSS,
P11Constants.CKM_SHA512_RSA_PKCS_PSS,
P11Constants.CKM_SHA3_224_RSA_PKCS_PSS,
P11Constants.CKM_SHA3_256_RSA_PKCS_PSS,
P11Constants.CKM_SHA3_384_RSA_PKCS_PSS,
P11Constants.CKM_SHA3_512_RSA_PKCS_PSS,
P11Constants.CKM_DSA,
P11Constants.CKM_DSA_SHA1,
P11Constants.CKM_DSA_SHA224,
P11Constants.CKM_DSA_SHA256,
P11Constants.CKM_DSA_SHA384,
P11Constants.CKM_DSA_SHA512,
P11Constants.CKM_DSA_SHA3_224,
P11Constants.CKM_DSA_SHA3_256,
P11Constants.CKM_DSA_SHA3_384,
P11Constants.CKM_DSA_SHA3_512,
P11Constants.CKM_ECDSA,
P11Constants.CKM_ECDSA_SHA1,
P11Constants.CKM_ECDSA_SHA224,
P11Constants.CKM_ECDSA_SHA256,
P11Constants.CKM_ECDSA_SHA384,
P11Constants.CKM_ECDSA_SHA512,
P11Constants.CKM_ECDSA_SHA3_224,
P11Constants.CKM_ECDSA_SHA3_256,
P11Constants.CKM_ECDSA_SHA3_384,
P11Constants.CKM_ECDSA_SHA3_512};
private static final FilenameFilter INFO_FILENAME_FILTER = new InfoFilenameFilter();
private final boolean namedCurveSupported;
private final File slotDir;
private final File privKeyDir;
private final File pubKeyDir;
private final File certDir;
private final PrivateKeyCryptor privateKeyCryptor;
private final SecureRandom random = new SecureRandom();
//private final SecurityFactory securityFactory;
private final int maxSessions;
private static final Logger LOG = LoggerFactory.getLogger(EmulatorP11Slot.class);
EmulatorP11Slot(final String moduleName, final File slotDir, final P11SlotIdentifier slotId,
final boolean readOnly, final PrivateKeyCryptor privateKeyCryptor,
final P11MechanismFilter mechanismFilter, final int maxSessions)
throws P11TokenException {
super(moduleName, slotId, readOnly, mechanismFilter);
this.slotDir = ParamUtil.requireNonNull("slotDir", slotDir);
this.privateKeyCryptor = ParamUtil.requireNonNull("privateKeyCryptor", privateKeyCryptor);
this.maxSessions = ParamUtil.requireMin("maxSessions", maxSessions, 1);
this.privKeyDir = new File(slotDir, DIR_PRIV_KEY);
if (!this.privKeyDir.exists()) {
this.privKeyDir.mkdirs();
}
this.pubKeyDir = new File(slotDir, DIR_PUB_KEY);
if (!this.pubKeyDir.exists()) {
this.pubKeyDir.mkdirs();
}
this.certDir = new File(slotDir, DIR_CERT);
if (!this.certDir.exists()) {
this.certDir.mkdirs();
}
File slotInfoFile = new File(slotDir, FILE_SLOTINFO);
if (slotInfoFile.exists()) {
Properties props = loadProperties(slotInfoFile);
this.namedCurveSupported = Boolean.parseBoolean(
props.getProperty(PROP_NAMED_CURVE_SUPPORTED, "true"));
} else {
this.namedCurveSupported = true;
}
refresh();
}
@Override
protected P11SlotRefreshResult doRefresh()
throws P11TokenException {
P11SlotRefreshResult ret = new P11SlotRefreshResult();
for (long mech : supportedMechs) {
ret.addMechanism(mech);
}
// Certificates
File[] certInfoFiles = certDir.listFiles(INFO_FILENAME_FILTER);
if (certInfoFiles != null) {
for (File infoFile : certInfoFiles) {
byte[] id = getKeyIdFromInfoFilename(infoFile.getName());
Properties props = loadProperties(infoFile);
String label = props.getProperty(PROP_LABEL);
P11ObjectIdentifier objId = new P11ObjectIdentifier(id, label);
try {
X509Cert cert = readCertificate(id);
ret.addCertificate(objId, cert);
} catch (CertificateException | IOException ex) {
LOG.warn("could not parse certificate " + objId);
}
}
}
// Private / Public keys
File[] privKeyInfoFiles = privKeyDir.listFiles(INFO_FILENAME_FILTER);
if (privKeyInfoFiles == null || privKeyInfoFiles.length == 0) {
return ret;
}
for (File privKeyInfoFile : privKeyInfoFiles) {
byte[] id = getKeyIdFromInfoFilename(privKeyInfoFile.getName());
String hexId = Hex.toHexString(id);
try {
Properties props = loadProperties(privKeyInfoFile);
String label = props.getProperty(PROP_LABEL);
P11ObjectIdentifier p11ObjId = new P11ObjectIdentifier(id, label);
X509Cert cert = ret.getCertForId(id);
java.security.PublicKey publicKey = (cert == null) ? readPublicKey(id)
: cert.getCert().getPublicKey();
if (publicKey == null) {
LOG.warn("Neither public key nor certificate is associated with private key {}",
p11ObjId);
continue;
}
byte[] encodedValue = IoUtil.read(new File(privKeyDir, hexId + VALUE_FILE_SUFFIX));
PKCS8EncryptedPrivateKeyInfo epki = new PKCS8EncryptedPrivateKeyInfo(encodedValue);
PrivateKey privateKey = privateKeyCryptor.decrypt(epki);
X509Certificate[] certs = (cert == null) ? null
: new X509Certificate[]{cert.getCert()};
EmulatorP11Identity identity = new EmulatorP11Identity(this,
new P11EntityIdentifier(slotId, p11ObjId), privateKey, publicKey, certs,
maxSessions, random);
LOG.info("added PKCS#11 key {}", p11ObjId);
ret.addIdentity(identity);
} catch (InvalidKeyException ex) {
LogUtil.warn(LOG, ex,
"InvalidKeyException while initializing key with key-id " + hexId);
continue;
} catch (Throwable th) {
LogUtil.warn(LOG, th,
"unexpected exception while initializing key with key-id " + hexId);
continue;
}
}
return ret;
} // method refresh
File getSlotDir() {
return slotDir;
}
private PublicKey readPublicKey(final byte[] keyId) throws P11TokenException {
String hexKeyId = Hex.toHexString(keyId);
File pubKeyFile = new File(pubKeyDir, hexKeyId + INFO_FILE_SUFFIX);
Properties props = loadProperties(pubKeyFile);
String algorithm = props.getProperty(PROP_ALGORITHM);
if (PKCSObjectIdentifiers.rsaEncryption.getId().equals(algorithm)) {
BigInteger exp = new BigInteger(1,
Hex.decode(props.getProperty(PROP_RSA_PUBLIC_EXPONENT)));
BigInteger mod = new BigInteger(1, Hex.decode(props.getProperty(PROP_RSA_MODUS)));
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(mod, exp);
try {
return KeyUtil.generateRSAPublicKey(keySpec);
} catch (InvalidKeySpecException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
} else if (X9ObjectIdentifiers.id_dsa.getId().equals(algorithm)) {
BigInteger prime = new BigInteger(1,
Hex.decode(props.getProperty(PROP_DSA_PRIME))); // p
BigInteger subPrime = new BigInteger(1,
Hex.decode(props.getProperty(PROP_DSA_SUBPRIME))); // q
BigInteger base = new BigInteger(1,
Hex.decode(props.getProperty(PROP_DSA_BASE))); // g
BigInteger value = new BigInteger(1,
Hex.decode(props.getProperty(PROP_DSA_VALUE))); // y
DSAPublicKeySpec keySpec = new DSAPublicKeySpec(value, prime, subPrime, base);
try {
return KeyUtil.generateDSAPublicKey(keySpec);
} catch (InvalidKeySpecException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
} else if (X9ObjectIdentifiers.id_ecPublicKey.getId().equals(algorithm)) {
byte[] ecdsaParams = Hex.decode(props.getProperty(PROP_EC_ECDSA_PARAMS));
byte[] asn1EncodedPoint = Hex.decode(props.getProperty(PROP_EC_EC_POINT));
byte[] ecPoint = DEROctetString.getInstance(asn1EncodedPoint).getOctets();
try {
return KeyUtil.createECPublicKey(ecdsaParams, ecPoint);
} catch (InvalidKeySpecException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
} else {
throw new P11TokenException("unknown key algorithm " + algorithm);
}
}
private X509Cert readCertificate(final byte[] keyId) throws CertificateException, IOException {
byte[] encoded = IoUtil.read(new File(certDir, Hex.toHexString(keyId) + VALUE_FILE_SUFFIX));
X509Certificate cert = X509Util.parseCert(encoded);
return new X509Cert(cert, encoded);
}
private Properties loadProperties(final File file) throws P11TokenException {
try {
try (InputStream stream = new FileInputStream(file)) {
Properties props = new Properties();
props.load(stream);
return props;
}
} catch (IOException ex) {
throw new P11TokenException("could not load properties from the file " + file.getPath(),
ex);
}
}
private static byte[] getKeyIdFromInfoFilename(final String fileName) {
return Hex.decode(fileName.substring(0, fileName.length() - INFO_FILE_SUFFIX.length()));
}
@Override
public void close() {
LOG.info("close slot " + slotId);
}
private boolean removePkcs11Cert(final P11ObjectIdentifier objectId) throws P11TokenException {
return removePkcs11Entry(certDir, objectId);
}
private boolean removePkcs11Entry(final File dir, final P11ObjectIdentifier objectId)
throws P11TokenException {
byte[] id = objectId.getId();
String label = objectId.getLabel();
if (id != null) {
String hextId = Hex.toHexString(id);
File infoFile = new File(dir, hextId + INFO_FILE_SUFFIX);
if (!infoFile.exists()) {
return false;
}
if (StringUtil.isBlank(label)) {
return deletePkcs11Entry(dir, id);
} else {
Properties props = loadProperties(infoFile);
return label.equals(props.getProperty("label")) ? deletePkcs11Entry(dir, id)
: false;
}
}
// id is null, delete all entries with the specified label
boolean deleted = false;
File[] infoFiles = dir.listFiles(INFO_FILENAME_FILTER);
if (infoFiles != null) {
for (File infoFile : infoFiles) {
if (!infoFile.isFile()) {
continue;
}
Properties props = loadProperties(infoFile);
if (label.equals(props.getProperty("label"))) {
if (deletePkcs11Entry(dir, getKeyIdFromInfoFilename(infoFile.getName()))) {
deleted = true;
}
}
}
}
return deleted;
}
private static boolean deletePkcs11Entry(final File dir, final byte[] objectId) {
String hextId = Hex.toHexString(objectId);
File infoFile = new File(dir, hextId + INFO_FILE_SUFFIX);
boolean b1 = true;
if (infoFile.exists()) {
b1 = infoFile.delete();
}
File valueFile = new File(dir, hextId + VALUE_FILE_SUFFIX);
boolean b2 = true;
if (valueFile.exists()) {
b2 = valueFile.delete();
}
return b1 || b2;
}
private int deletePkcs11Entry(final File dir, final byte[] id, final String label)
throws P11TokenException {
if (StringUtil.isBlank(label)) {
return deletePkcs11Entry(dir, id) ? 1 : 0;
}
if (id != null && id.length > 0) {
String hextId = Hex.toHexString(id);
File infoFile = new File(dir, hextId + INFO_FILE_SUFFIX);
if (!infoFile.exists()) {
return 0;
}
Properties props = loadProperties(infoFile);
if (!label.equals(props.get(PROP_LABEL))) {
return 0;
}
return deletePkcs11Entry(dir, id) ? 1 : 0;
}
File[] infoFiles = dir.listFiles(INFO_FILENAME_FILTER);
if (infoFiles == null || infoFiles.length == 0) {
return 0;
}
List<byte[]> ids = new LinkedList<>();
for (File infoFile : infoFiles) {
Properties props = loadProperties(infoFile);
if (label.equals(props.getProperty(PROP_LABEL))) {
ids.add(getKeyIdFromInfoFilename(infoFile.getName()));
}
}
if (ids.isEmpty()) {
return 0;
}
for (byte[] m : ids) {
deletePkcs11Entry(dir, m);
}
return ids.size();
}
private void savePkcs11PrivateKey(final byte[] id, final String label,
final PrivateKey privateKey) throws P11TokenException {
PKCS8EncryptedPrivateKeyInfo encryptedPrivKeyInfo = privateKeyCryptor.encrypt(privateKey);
byte[] encoded;
try {
encoded = encryptedPrivKeyInfo.getEncoded();
} catch (IOException ex) {
LogUtil.error(LOG, ex);
throw new P11TokenException("could not encode PrivateKey");
}
savePkcs11Entry(privKeyDir, id, label, encoded);
}
private void savePkcs11PublicKey(final byte[] id, final String label, final PublicKey publicKey)
throws P11TokenException {
String hexId = Hex.toHexString(id).toLowerCase();
StringBuilder sb = new StringBuilder(100);
sb.append(PROP_ID).append('=').append(hexId).append('\n');
sb.append(PROP_LABEL).append('=').append(label).append('\n');
if (publicKey instanceof RSAPublicKey) {
sb.append(PROP_ALGORITHM).append('=');
sb.append(PKCSObjectIdentifiers.rsaEncryption.getId());
sb.append('\n');
sb.append(PROP_RSA_MODUS).append('=');
RSAPublicKey rsaKey = (RSAPublicKey) publicKey;
sb.append(Hex.toHexString(rsaKey.getModulus().toByteArray()));
sb.append('\n');
sb.append(PROP_RSA_PUBLIC_EXPONENT).append('=');
sb.append(Hex.toHexString(rsaKey.getPublicExponent().toByteArray()));
sb.append('\n');
} else if (publicKey instanceof DSAPublicKey) {
sb.append(PROP_ALGORITHM).append('=');
sb.append(X9ObjectIdentifiers.id_dsa.getId());
sb.append('\n');
sb.append(PROP_DSA_PRIME).append('=');
DSAPublicKey dsaKey = (DSAPublicKey) publicKey;
sb.append(Hex.toHexString(dsaKey.getParams().getP().toByteArray()));
sb.append('\n');
sb.append(PROP_DSA_SUBPRIME).append('=');
sb.append(Hex.toHexString(dsaKey.getParams().getQ().toByteArray()));
sb.append('\n');
sb.append(PROP_DSA_BASE).append('=');
sb.append(Hex.toHexString(dsaKey.getParams().getG().toByteArray()));
sb.append('\n');
sb.append(PROP_DSA_VALUE).append('=');
sb.append(Hex.toHexString(dsaKey.getY().toByteArray()));
sb.append('\n');
} else if (publicKey instanceof ECPublicKey) {
sb.append(PROP_ALGORITHM).append('=');
sb.append(X9ObjectIdentifiers.id_ecPublicKey.getId());
sb.append('\n');
ECPublicKey ecKey = (ECPublicKey) publicKey;
ECParameterSpec paramSpec = ecKey.getParams();
// ecdsaParams
org.bouncycastle.jce.spec.ECParameterSpec bcParamSpec =
EC5Util.convertSpec(paramSpec, false);
ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(bcParamSpec);
if (curveOid == null) {
throw new P11TokenException("EC public key is not of namedCurve");
}
byte[] encodedParams;
try {
if (namedCurveSupported) {
encodedParams = curveOid.getEncoded();
} else {
encodedParams = ECNamedCurveTable.getByOID(curveOid).getEncoded();
}
} catch (IOException | NullPointerException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
sb.append(PROP_EC_ECDSA_PARAMS).append('=');
sb.append(Hex.toHexString(encodedParams));
sb.append('\n');
// EC point
java.security.spec.ECPoint pointW = ecKey.getW();
int keysize = (paramSpec.getOrder().bitLength() + 7) / 8;
byte[] ecPoint = new byte[1 + keysize * 2];
ecPoint[0] = 4; // uncompressed
bigIntToBytes("Wx", pointW.getAffineX(), ecPoint, 1, keysize);
bigIntToBytes("Wy", pointW.getAffineY(), ecPoint, 1 + keysize, keysize);
byte[] encodedEcPoint;
try {
encodedEcPoint = new DEROctetString(ecPoint).getEncoded();
} catch (IOException ex) {
throw new P11TokenException("could not ASN.1 encode the ECPoint");
}
sb.append(PROP_EC_EC_POINT).append('=');
sb.append(Hex.toHexString(encodedEcPoint));
sb.append('\n');
} else {
throw new IllegalArgumentException(
"unsupported public key " + publicKey.getClass().getName());
}
try {
IoUtil.save(new File(pubKeyDir, hexId + INFO_FILE_SUFFIX), sb.toString().getBytes());
} catch (IOException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
}
private static void bigIntToBytes(final String numName, final BigInteger num, final byte[] dest,
final int destPos, final int length) throws P11TokenException {
if (num.signum() != 1) {
throw new P11TokenException(numName + " is not positive");
}
byte[] bytes = num.toByteArray();
if (bytes.length == length) {
System.arraycopy(bytes, 0, dest, destPos, length);
} else if (bytes.length < length) {
System.arraycopy(bytes, 0, dest, destPos + length - bytes.length, bytes.length);
} else {
System.arraycopy(bytes, bytes.length - length, dest, destPos, length);
}
}
private void savePkcs11Cert(final byte[] id, final String label, final X509Certificate cert)
throws XiSecurityException, P11TokenException {
try {
savePkcs11Entry(certDir, id, label, cert.getEncoded());
} catch (CertificateEncodingException ex) {
throw new XiSecurityException(ex.getMessage(), ex);
}
}
private static void savePkcs11Entry(final File dir, final byte[] id, final String label,
final byte[] value) throws P11TokenException {
ParamUtil.requireNonNull("dir", dir);
ParamUtil.requireNonNull("id", id);
ParamUtil.requireNonBlank("label", label);
ParamUtil.requireNonNull("value", value);
String hexId = Hex.toHexString(id).toLowerCase();
StringBuilder sb = new StringBuilder(200);
sb.append(PROP_ID).append('=').append(hexId).append('\n');
sb.append(PROP_LABEL).append('=').append(label).append('\n');
sb.append(PROP_SHA1SUM).append('=').append(HashAlgoType.SHA1.hexHash(value)).append('\n');
try {
IoUtil.save(new File(dir, hexId + INFO_FILE_SUFFIX), sb.toString().getBytes());
IoUtil.save(new File(dir, hexId + VALUE_FILE_SUFFIX), value);
} catch (IOException ex) {
throw new P11TokenException("could not save certificate");
}
}
@Override
public int removeObjects(final byte[] id, final String label) throws P11TokenException {
if ((id == null || id.length == 0) && StringUtil.isBlank(label)) {
throw new IllegalArgumentException("at least one of id and label must not be null");
}
int num = deletePkcs11Entry(privKeyDir, id, label);
num += deletePkcs11Entry(pubKeyDir, id, label);
num += deletePkcs11Entry(certDir, id, label);
return num;
}
@Override
protected void doRemoveIdentity(final P11ObjectIdentifier objectId) throws P11TokenException {
boolean b1 = removePkcs11Entry(certDir, objectId);
boolean b2 = removePkcs11Entry(privKeyDir, objectId);
boolean b3 = removePkcs11Entry(pubKeyDir, objectId);
if (! (b1 || b2 || b3)) {
throw new P11UnknownEntityException(slotId, objectId);
}
}
@Override
protected void doRemoveCerts(final P11ObjectIdentifier objectId) throws P11TokenException {
deletePkcs11Entry(certDir, objectId.getId());
}
@Override
protected void doAddCert(final P11ObjectIdentifier objectId, final X509Certificate cert)
throws P11TokenException, XiSecurityException {
savePkcs11Cert(objectId.getId(), objectId.getLabel(), cert);
}
@Override
protected P11Identity doGenerateRSAKeypair(final int keysize, final BigInteger publicExponent,
final String label) throws P11TokenException {
KeyPair keypair;
try {
keypair = KeyUtil.generateRSAKeypair(keysize, publicExponent, random);
} catch (NoSuchAlgorithmException | NoSuchProviderException
| InvalidAlgorithmParameterException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
return saveP11Entity(keypair, label);
}
@Override
// CHECKSTYLE:OFF
protected P11Identity doGenerateDSAKeypair(final BigInteger p, final BigInteger q,
final BigInteger g, final String label) throws P11TokenException {
// CHECKSTYLE:ON
DSAParameters dsaParams = new DSAParameters(p, q, g);
KeyPair keypair;
try {
keypair = KeyUtil.generateDSAKeypair(dsaParams, random);
} catch (NoSuchAlgorithmException | NoSuchProviderException
| InvalidAlgorithmParameterException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
return saveP11Entity(keypair, label);
}
@Override
protected P11Identity doGenerateECKeypair(final ASN1ObjectIdentifier curveId,
final String label) throws P11TokenException {
KeyPair keypair;
try {
keypair = KeyUtil.generateECKeypairForCurveNameOrOid(curveId.getId(), random);
} catch (NoSuchAlgorithmException | NoSuchProviderException
| InvalidAlgorithmParameterException ex) {
throw new P11TokenException(ex.getMessage(), ex);
}
return saveP11Entity(keypair, label);
}
private P11Identity saveP11Entity(@NonNull final KeyPair keypair, @NonNull final String label)
throws P11TokenException {
byte[] id = generateId();
savePkcs11PrivateKey(id, label, keypair.getPrivate());
savePkcs11PublicKey(id, label, keypair.getPublic());
P11EntityIdentifier identityId = new P11EntityIdentifier(slotId,
new P11ObjectIdentifier(id, label));
try {
return new EmulatorP11Identity(this,identityId, keypair.getPrivate(),
keypair.getPublic(), null, maxSessions, random);
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException ex) {
throw new P11TokenException(
"could not construct KeyStoreP11Identity: " + ex.getMessage(), ex);
}
}
@Override
protected void doUpdateCertificate(final P11ObjectIdentifier objectId,
final X509Certificate newCert) throws XiSecurityException, P11TokenException {
removePkcs11Cert(objectId);
doAddCert(objectId, newCert);
}
}