/* * * 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.iaik; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.DSAPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; 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.CollectionUtil; 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.X509Cert; import org.xipki.commons.security.exception.P11TokenException; 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.P11Params; import org.xipki.commons.security.pkcs11.P11RSAPkcsPssParams; 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; import iaik.pkcs.pkcs11.Mechanism; import iaik.pkcs.pkcs11.Session; import iaik.pkcs.pkcs11.SessionInfo; import iaik.pkcs.pkcs11.Slot; import iaik.pkcs.pkcs11.State; import iaik.pkcs.pkcs11.Token; import iaik.pkcs.pkcs11.TokenException; import iaik.pkcs.pkcs11.objects.Certificate.CertificateType; import iaik.pkcs.pkcs11.objects.CharArrayAttribute; import iaik.pkcs.pkcs11.objects.DSAPrivateKey; import iaik.pkcs.pkcs11.objects.DSAPublicKey; import iaik.pkcs.pkcs11.objects.ECDSAPrivateKey; import iaik.pkcs.pkcs11.objects.ECDSAPublicKey; import iaik.pkcs.pkcs11.objects.Key; import iaik.pkcs.pkcs11.objects.KeyPair; import iaik.pkcs.pkcs11.objects.PrivateKey; import iaik.pkcs.pkcs11.objects.PublicKey; import iaik.pkcs.pkcs11.objects.RSAPrivateKey; import iaik.pkcs.pkcs11.objects.RSAPublicKey; import iaik.pkcs.pkcs11.objects.Storage; import iaik.pkcs.pkcs11.objects.X509PublicKeyCertificate; import iaik.pkcs.pkcs11.parameters.RSAPkcsPssParameters; import iaik.pkcs.pkcs11.wrapper.PKCS11Exception; /** * @author Lijun Liao * @since 2.0.0 */ class IaikP11Slot extends AbstractP11Slot { private static final Logger LOG = LoggerFactory.getLogger(IaikP11Slot.class); private static final long DEFAULT_MAX_COUNT_SESSION = 32; private final int maxMessageSize; private Slot slot; private final long userType; private List<char[]> password; private int maxSessionCount; private long timeOutWaitNewSession = 10000; // maximal wait for 10 second private final AtomicLong countSessions = new AtomicLong(0); private final BlockingDeque<Session> idleSessions = new LinkedBlockingDeque<>(); private final BlockingDeque<Session> busySessions = new LinkedBlockingDeque<>(); private boolean writableSessionInUse; private Session writableSession; IaikP11Slot(final String moduleName, final P11SlotIdentifier slotId, final Slot slot, final boolean readOnly, final long userType, final List<char[]> password, final int maxMessageSize, final P11MechanismFilter mechanismFilter) throws P11TokenException { super(moduleName, slotId, readOnly, mechanismFilter); this.slot = ParamUtil.requireNonNull("slot", slot); this.maxMessageSize = ParamUtil.requireMin("maxMessageSize", maxMessageSize, 1); this.userType = ParamUtil.requireMin("userType", userType, 0); this.password = password; Session session; try { session = openSession(false); } catch (P11TokenException ex) { LogUtil.error(LOG, ex, "openSession"); close(); throw ex; } try { firstLogin(session, password); } catch (P11TokenException ex) { LogUtil.error(LOG, ex, "firstLogin"); close(); throw ex; } Token token; try { token = this.slot.getToken(); } catch (TokenException ex) { throw new P11TokenException("could not getToken: " + ex.getMessage(), ex); } long maxSessionCount2; try { maxSessionCount2 = token.getTokenInfo().getMaxSessionCount(); } catch (TokenException ex) { throw new P11TokenException("could not get tokenInfo: " + ex.getMessage(), ex); } if (maxSessionCount2 == 0) { maxSessionCount2 = DEFAULT_MAX_COUNT_SESSION; } else { // 2 sessions as buffer, they may be used elsewhere. maxSessionCount2 = (maxSessionCount2 < 3) ? 1 : maxSessionCount2 - 2; } this.maxSessionCount = (int) maxSessionCount2; LOG.info("maxSessionCount: {}", this.maxSessionCount); idleSessions.addLast(session); refresh(); } // constructor Slot getSlot() { return slot; } @Override protected P11SlotRefreshResult doRefresh() throws P11TokenException { Mechanism[] mechanisms; try { mechanisms = slot.getToken().getMechanismList(); } catch (TokenException ex) { throw new P11TokenException("could not getMechanismList: " + ex.getMessage(), ex); } P11SlotRefreshResult ret = new P11SlotRefreshResult(); if (mechanisms != null) { for (Mechanism mech : mechanisms) { ret.addMechanism(mech.getMechanismCode()); } } // first get the list of all CA certificates List<X509PublicKeyCertificate> p11Certs = getAllCertificateObjects(); for (X509PublicKeyCertificate p11Cert : p11Certs) { P11ObjectIdentifier objId = new P11ObjectIdentifier(p11Cert.getId().getByteArrayValue(), toString(p11Cert.getLabel())); ret.addCertificate(objId, parseCert(p11Cert)); } List<PrivateKey> privKeys = getAllPrivateObjects(); for (PrivateKey privKey : privKeys) { byte[] keyId = privKey.getId().getByteArrayValue(); if (keyId == null || keyId.length == 0) { break; } try { analyseSingleKey(privKey, ret); } catch (XiSecurityException ex) { LogUtil.error(LOG, ex, "XiSecurityException while initializing key with id " + hex(keyId)); continue; } catch (Throwable th) { String label = ""; if (privKey.getLabel() != null) { label = new String(privKey.getLabel().getCharArrayValue()); } LogUtil.error(LOG, th, "unexpected exception while initializing key with id " + hex(keyId) + " and label " + label); continue; } } // end for (PrivateKey signatureKey : signatureKeys) return ret; } // method refresh @Override public void close() { if (slot != null) { try { LOG.info("close all sessions on token: {}", slot.getSlotID()); if (writableSession != null) { writableSession.closeSession(); } for (Session session : idleSessions) { session.closeSession(); } } catch (Throwable th) { LogUtil.error(LOG, th, "could not slot.getToken().closeAllSessions()"); } slot = null; } // clear the session pool idleSessions.clear(); countSessions.lazySet(0); } private void analyseSingleKey(final PrivateKey privKey, final P11SlotRefreshResult refreshResult) throws P11TokenException, XiSecurityException { byte[] id = privKey.getId().getByteArrayValue(); java.security.PublicKey pubKey = null; X509Cert cert = refreshResult.getCertForId(id); if (cert != null) { pubKey = cert.getCert().getPublicKey(); } else { PublicKey p11PublicKey = getPublicKeyObject(id, null); if (p11PublicKey == null) { LOG.info("neither certificate nor public key for the key (" + Hex.toHexString(id) + " is available"); return; } pubKey = generatePublicKey(p11PublicKey); } P11ObjectIdentifier objectId = new P11ObjectIdentifier(privKey.getId().getByteArrayValue(), toString(privKey.getLabel())); X509Certificate[] certs = (cert == null) ? null : new X509Certificate[]{cert.getCert()}; IaikP11Identity identity = new IaikP11Identity(this, new P11EntityIdentifier(slotId, objectId), privKey, pubKey, certs); refreshResult.addIdentity(identity); } byte[] sign(final long mechanism, final P11Params parameters, final byte[] content, final IaikP11Identity identity) throws P11TokenException { ParamUtil.requireNonNull("content", content); assertMechanismSupported(mechanism); int len = content.length; if (len <= maxMessageSize) { return singleSign(mechanism, parameters, content, identity); } PrivateKey signingKey = identity.getPrivateKey(); Mechanism mechanismObj = getMechanism(mechanism, parameters); if (LOG.isTraceEnabled()) { LOG.debug("sign (init, update, then finish) with private key:\n{}", signingKey); } Session session = borrowIdleSession(); if (session == null) { throw new P11TokenException("no idle session available"); } try { session.signInit(mechanismObj, signingKey); for (int i = 0; i < len; i += maxMessageSize) { int blockLen = Math.min(maxMessageSize, len - i); //byte[] block = new byte[blockLen]; //System.arraycopy(content, i, block, 0, blockLen); session.signUpdate(content, i, blockLen); } return session.signFinal(identity.getExpectedSignatureLen()); } catch (TokenException e) { throw new P11TokenException(e); } finally { returnIdleSession(session); } } private byte[] singleSign(final long mechanism, final P11Params parameters, final byte[] content, final IaikP11Identity identity) throws P11TokenException { PrivateKey signingKey = identity.getPrivateKey(); Mechanism mechanismObj = getMechanism(mechanism, parameters); if (LOG.isTraceEnabled()) { LOG.debug("sign with private key:\n{}", signingKey); } Session session = borrowIdleSession(); if (session == null) { throw new P11TokenException("no idle session available"); } byte[] signature; try { synchronized (session) { session.signInit(mechanismObj, signingKey); signature = session.sign(content); } } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } finally { returnIdleSession(session); } if (LOG.isDebugEnabled()) { LOG.debug("signature:\n{}", Hex.toHexString(signature)); } return signature; } private static Mechanism getMechanism(final long mechanism, final P11Params parameters) throws P11TokenException { Mechanism ret = Mechanism.get(mechanism); if (parameters == null) { return ret; } if (parameters instanceof P11RSAPkcsPssParams) { P11RSAPkcsPssParams param = (P11RSAPkcsPssParams) parameters; RSAPkcsPssParameters paramObj = new RSAPkcsPssParameters( Mechanism.get(param.getHashAlgorithm()), param.getMaskGenerationFunction(), param.getSaltLength()); ret.setParameters(paramObj); } else { throw new P11TokenException("unknown P11Parameters " + parameters.getClass().getName()); } return ret; } private Session openSession(final boolean rwSession) throws P11TokenException { Session session; try { session = slot.getToken().openSession(Token.SessionType.SERIAL_SESSION, rwSession, null, null); } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } countSessions.incrementAndGet(); return session; } private Session borrowIdleSession() throws P11TokenException { Session session = null; if (countSessions.get() < maxSessionCount) { session = idleSessions.poll(); if (session == null) { // create new session session = openSession(false); } } if (session == null) { try { session = idleSessions.poll(timeOutWaitNewSession, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { // CHECKSTYLE:SKIP } } if (session == null) { throw new P11TokenException("no idle session"); } busySessions.addLast(session); login(session); return session; } private void returnIdleSession(final Session session) { if (session == null) { return; } boolean isBusySession = busySessions.remove(session); if (isBusySession) { idleSessions.addLast(session); } else { final String msg = "session has not been borrowed before or has been returned more than once: " + session; LOG.error(msg); throw new IllegalStateException(msg); } } private void firstLogin(final Session session, final List<char[]> password) throws P11TokenException { try { boolean isProtectedAuthenticationPath = session.getToken().getTokenInfo().isProtectedAuthenticationPath(); if (isProtectedAuthenticationPath || CollectionUtil.isEmpty(password)) { LOG.info("verify on PKCS11Module with PROTECTED_AUTHENTICATION_PATH"); singleLogin(session, null); } else { LOG.info("verify on PKCS11Module with PIN"); for (char[] singlePwd : password) { singleLogin(session, singlePwd); } this.password = password; } } catch (PKCS11Exception ex) { // 0x100: user already logged in if (ex.getErrorCode() != 0x100) { throw new P11TokenException(ex.getMessage(), ex); } } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } } private void login(final Session session) throws P11TokenException { boolean isSessionLoggedIn = checkSessionLoggedIn(session); if (isSessionLoggedIn) { return; } boolean loginRequired; try { loginRequired = session.getToken().getTokenInfo().isLoginRequired(); } catch (TokenException ex) { LogUtil.error(LOG, ex, "could not check whether LoginRequired of token"); loginRequired = true; } LOG.debug("loginRequired: {}", loginRequired); if (!loginRequired) { return; } if (CollectionUtil.isEmpty(password)) { singleLogin(session, null); } else { for (char[] singlePwd : password) { singleLogin(session, singlePwd); } } } private void singleLogin(final Session session, final char[] pin) throws P11TokenException { char[] tmpPin = pin; // some driver does not accept null PIN if (pin == null) { tmpPin = new char[]{}; } try { if (userType == P11Constants.CKU_USER) { session.login(Session.UserType.USER, tmpPin); } else if (userType == P11Constants.CKU_SO) { session.login(Session.UserType.SO, tmpPin); } else { session.login(userType, tmpPin); } } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } } private List<PrivateKey> getAllPrivateObjects() throws P11TokenException { Session session = borrowIdleSession(); try { PrivateKey template = new PrivateKey(); List<Storage> tmpObjects = getObjects(session, template); if (CollectionUtil.isEmpty(tmpObjects)) { return Collections.emptyList(); } final int n = tmpObjects.size(); LOG.info("found {} private keys", n); List<PrivateKey> privateKeys = new ArrayList<>(n); for (Storage tmpObject : tmpObjects) { PrivateKey privateKey = (PrivateKey) tmpObject; privateKeys.add(privateKey); } return privateKeys; } finally { returnIdleSession(session); } } private PrivateKey getPrivateKeyObject(final byte[] keyId, final char[] keyLabel) throws P11TokenException { return (PrivateKey) getKeyObject(new PrivateKey(), keyId, keyLabel); } private PublicKey getPublicKeyObject(final byte[] keyId, final char[] keyLabel) throws P11TokenException { return (PublicKey) getKeyObject(new PublicKey(), keyId, keyLabel); } private Key getKeyObject(final Key template, final byte[] keyId, final char[] keyLabel) throws P11TokenException { Session session = borrowIdleSession(); try { if (keyId != null) { template.getId().setByteArrayValue(keyId); } if (keyLabel != null) { template.getLabel().setCharArrayValue(keyLabel); } List<Storage> tmpObjects = getObjects(session, template, 2); if (CollectionUtil.isEmpty(tmpObjects)) { return null; } int size = tmpObjects.size(); if (size > 1) { LOG.warn("found {} public key identified by {}, use the first one", size, getDescription(keyId, keyLabel)); } return (Key) tmpObjects.get(0); } finally { returnIdleSession(session); } } private static boolean checkSessionLoggedIn(final Session session) throws P11TokenException { SessionInfo info; try { info = session.getSessionInfo(); } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } if (LOG.isTraceEnabled()) { LOG.debug("SessionInfo: {}", info); } State state = info.getState(); long deviceError = info.getDeviceError(); LOG.debug("to be verified PKCS11Module: state = {}, deviceError: {}", state, deviceError); boolean isRwSessionLoggedIn = state.equals(State.RW_USER_FUNCTIONS); boolean isRoSessionLoggedIn = state.equals(State.RO_USER_FUNCTIONS); boolean sessionLoggedIn = ((isRoSessionLoggedIn || isRwSessionLoggedIn) && deviceError == 0); LOG.debug("sessionLoggedIn: {}", sessionLoggedIn); return sessionLoggedIn; } private static List<Storage> getObjects(final Session session, final Storage template) throws P11TokenException { return getObjects(session, template, 9999); } private static List<Storage> getObjects(final Session session, final Storage template, final int maxNo) throws P11TokenException { List<Storage> objList = new LinkedList<>(); try { session.findObjectsInit(template); while (objList.size() < maxNo) { iaik.pkcs.pkcs11.objects.Object[] foundObjects = session.findObjects(1); if (foundObjects == null || foundObjects.length == 0) { break; } for (iaik.pkcs.pkcs11.objects.Object object : foundObjects) { if (LOG.isTraceEnabled()) { LOG.debug("found object: {}", object); } objList.add((Storage) object); } } } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } finally { try { session.findObjectsFinal(); } catch (Exception ex) { // CHECKSTYLE:SKIP } } return objList; } // method getObjects private static java.security.PublicKey generatePublicKey(final PublicKey p11Key) throws XiSecurityException { if (p11Key instanceof RSAPublicKey) { RSAPublicKey rsaP11Key = (RSAPublicKey) p11Key; byte[] expBytes = rsaP11Key.getPublicExponent().getByteArrayValue(); BigInteger exp = new BigInteger(1, expBytes); byte[] modBytes = rsaP11Key.getModulus().getByteArrayValue(); BigInteger mod = new BigInteger(1, modBytes); RSAPublicKeySpec keySpec = new RSAPublicKeySpec(mod, exp); try { return KeyUtil.generateRSAPublicKey(keySpec); } catch (InvalidKeySpecException ex) { throw new XiSecurityException(ex.getMessage(), ex); } } else if (p11Key instanceof DSAPublicKey) { DSAPublicKey dsaP11Key = (DSAPublicKey) p11Key; BigInteger prime = new BigInteger(1, dsaP11Key.getPrime().getByteArrayValue()); // p BigInteger subPrime = new BigInteger(1, dsaP11Key.getSubprime().getByteArrayValue()); // q BigInteger base = new BigInteger(1, dsaP11Key.getBase().getByteArrayValue()); // g BigInteger value = new BigInteger(1, dsaP11Key.getValue().getByteArrayValue()); // y DSAPublicKeySpec keySpec = new DSAPublicKeySpec(value, prime, subPrime, base); try { return KeyUtil.generateDSAPublicKey(keySpec); } catch (InvalidKeySpecException ex) { throw new XiSecurityException(ex.getMessage(), ex); } } else if (p11Key instanceof ECDSAPublicKey) { ECDSAPublicKey ecP11Key = (ECDSAPublicKey) p11Key; byte[] encodedAlgorithmIdParameters = ecP11Key.getEcdsaParams().getByteArrayValue(); byte[] encodedPoint = DEROctetString.getInstance( ecP11Key.getEcPoint().getByteArrayValue()).getOctets(); try { return KeyUtil.createECPublicKey(encodedAlgorithmIdParameters, encodedPoint); } catch (InvalidKeySpecException ex) { throw new XiSecurityException(ex.getMessage(), ex); } } else { throw new XiSecurityException("unknown publicKey class " + p11Key.getClass().getName()); } } // method generatePublicKey private static String toString(final CharArrayAttribute charArrayAttr) { String labelStr = ""; if (charArrayAttr != null) { char[] chars = charArrayAttr.getCharArrayValue(); if (chars != null) { labelStr = new String(chars); } } return labelStr; } private static X509Cert parseCert(final X509PublicKeyCertificate p11Cert) throws P11TokenException { try { byte[] encoded = p11Cert.getValue().getByteArrayValue(); return new X509Cert(X509Util.parseCert(encoded), encoded); } catch (CertificateException ex) { throw new P11TokenException("could not parse certificate: " + ex.getMessage(), ex); } } private synchronized Session borrowWritableSession() throws P11TokenException { if (writableSession == null) { writableSession = openSession(true); } if (writableSessionInUse) { throw new P11TokenException("no idle writable session available"); } writableSessionInUse = true; login(writableSession); return writableSession; } private synchronized void returnWritableSession(final Session session) throws P11TokenException { if (session != writableSession) { throw new P11TokenException("the returned session does not belong to me"); } this.writableSessionInUse = false; } private List<X509PublicKeyCertificate> getAllCertificateObjects() throws P11TokenException { X509PublicKeyCertificate template = new X509PublicKeyCertificate(); Session session = borrowIdleSession(); List<Storage> tmpObjects; try { tmpObjects = getObjects(session, template); } finally { returnIdleSession(session); } List<X509PublicKeyCertificate> certs = new ArrayList<>(tmpObjects.size()); for (iaik.pkcs.pkcs11.objects.Object tmpObject : tmpObjects) { X509PublicKeyCertificate cert = (X509PublicKeyCertificate) tmpObject; certs.add(cert); } return certs; } @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"); } Key keyTemplate = new Key(); if (id != null && id.length > 0) { keyTemplate.getId().setByteArrayValue(id); } if (StringUtil.isNotBlank(label)) { keyTemplate.getLabel().setCharArrayValue(label.toCharArray()); } String objIdDesc = getDescription(id, label); int num = removeObjects(keyTemplate, "keys " + objIdDesc); X509PublicKeyCertificate certTemplate = new X509PublicKeyCertificate(); if (id != null && id.length > 0) { certTemplate.getId().setByteArrayValue(id); } if (StringUtil.isNotBlank(label)) { certTemplate.getLabel().setCharArrayValue(label.toCharArray()); } num += removeObjects(certTemplate, "certificates" + objIdDesc); return num; } private int removeObjects(final Storage template, final String desc) throws P11TokenException { Session session = borrowWritableSession(); try { List<Storage> objects = getObjects(session, template); for (Storage obj : objects) { session.destroyObject(obj); } return objects.size(); } catch (TokenException ex) { LogUtil.error(LOG, ex, "could not remove " + desc); throw new P11TokenException(ex.getMessage(), ex); } finally { returnWritableSession(session); } } @Override protected void doRemoveCerts(final P11ObjectIdentifier objectId) throws P11TokenException { X509PublicKeyCertificate[] existingCerts = getCertificateObjects(objectId.getId(), objectId.getLabelChars()); if (existingCerts == null || existingCerts.length == 0) { LOG.warn("could not find certificates " + objectId); return; } Session session = borrowWritableSession(); try { for (X509PublicKeyCertificate cert : existingCerts) { session.destroyObject(cert); } } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } finally { returnWritableSession(session); } } @Override protected void doAddCert(final P11ObjectIdentifier objectId, final X509Certificate cert) throws P11TokenException, XiSecurityException { X509PublicKeyCertificate newCaCertTemp = createPkcs11Template( new X509Cert(cert), objectId.getId(), objectId.getLabelChars()); Session session = borrowWritableSession(); try { session.createObject(newCaCertTemp); } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } finally { returnWritableSession(session); } } @Override protected P11Identity doGenerateRSAKeypair(final int keysize, final BigInteger publicExponent, final String label) throws P11TokenException { RSAPrivateKey privateKey = new RSAPrivateKey(); RSAPublicKey publicKey = new RSAPublicKey(); setKeyAttributes(label, P11Constants.CKK_RSA, publicKey, privateKey); publicKey.getModulusBits().setLongValue((long) keysize); if (publicExponent != null) { publicKey.getPublicExponent().setByteArrayValue(publicExponent.toByteArray()); } return generateKeyPair(P11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN, privateKey, publicKey); } @Override // CHECKSTYLE:OFF protected P11Identity doGenerateDSAKeypair(final BigInteger p, final BigInteger q, final BigInteger g, final String label) throws P11TokenException { // CHECKSTYLE:ON DSAPrivateKey privateKey = new DSAPrivateKey(); DSAPublicKey publicKey = new DSAPublicKey(); setKeyAttributes(label, P11Constants.CKK_DSA, publicKey, privateKey); publicKey.getPrime().setByteArrayValue(p.toByteArray()); publicKey.getSubprime().setByteArrayValue(q.toByteArray()); publicKey.getBase().setByteArrayValue(g.toByteArray()); return generateKeyPair(P11Constants.CKM_DSA_KEY_PAIR_GEN, privateKey, publicKey); } @Override protected P11Identity doGenerateECKeypair(final ASN1ObjectIdentifier curveId, final String label) throws P11TokenException { ECDSAPrivateKey privateKey = new ECDSAPrivateKey(); ECDSAPublicKey publicKey = new ECDSAPublicKey(); setKeyAttributes(label, P11Constants.CKK_EC, publicKey, privateKey); byte[] encodedCurveId; try { encodedCurveId = curveId.getEncoded(); } catch (IOException ex) { throw new P11TokenException(ex.getMessage(), ex); } try { publicKey.getEcdsaParams().setByteArrayValue(encodedCurveId); return generateKeyPair(P11Constants.CKM_EC_KEY_PAIR_GEN, privateKey, publicKey); } catch (P11TokenException ex) { X9ECParameters ecParams = ECNamedCurveTable.getByOID(curveId); if (ecParams == null) { throw new IllegalArgumentException("could not get X9ECParameters for curve " + curveId.getId()); } try { publicKey.getEcdsaParams().setByteArrayValue(ecParams.getEncoded()); } catch (IOException ex2) { throw new P11TokenException(ex.getMessage(), ex); } return generateKeyPair(P11Constants.CKM_EC_KEY_PAIR_GEN, privateKey, publicKey); } } private P11Identity generateKeyPair(final long mech, final PrivateKey privateKey, final PublicKey publicKey) throws P11TokenException { final String label = toString(privateKey.getLabel()); byte[] id = null; try { KeyPair keypair; Session session = borrowWritableSession(); try { if (labelExists(session, label)) { throw new IllegalArgumentException( "label " + label + " exists, please specify another one"); } id = generateKeyId(session); privateKey.getId().setByteArrayValue(id); publicKey.getId().setByteArrayValue(id); try { keypair = session.generateKeyPair(Mechanism.get(mech), publicKey, privateKey); } catch (TokenException ex) { throw new P11TokenException("could not generate keypair " + P11Constants.getMechanismName(mech), ex); } } finally { returnWritableSession(session); } P11ObjectIdentifier objId = new P11ObjectIdentifier(id, label); P11EntityIdentifier entityId = new P11EntityIdentifier(slotId, objId); java.security.PublicKey jcePublicKey; try { jcePublicKey = generatePublicKey(keypair.getPublicKey()); } catch (XiSecurityException ex) { throw new P11TokenException("could not generate public key " + objId, ex); } PrivateKey privateKey2 = getPrivateKeyObject(id, label.toCharArray()); if (privateKey2 == null) { throw new P11TokenException("could not read the generated private key"); } return new IaikP11Identity(this, entityId, privateKey2, jcePublicKey, null); } catch (P11TokenException | RuntimeException ex) { try { removeObjects(id, label); } catch (Throwable th) { LogUtil.error(LOG, th, "could not remove objects"); } throw ex; } } private static X509PublicKeyCertificate createPkcs11Template(final X509Cert cert, final byte[] keyId, final char[] label) { if (label == null || label.length == 0) { throw new IllegalArgumentException("label must not be null or empty"); } X509PublicKeyCertificate newCertTemp = new X509PublicKeyCertificate(); newCertTemp.getId().setByteArrayValue(keyId); newCertTemp.getLabel().setCharArrayValue(label); newCertTemp.getToken().setBooleanValue(true); newCertTemp.getCertificateType().setLongValue(CertificateType.X_509_PUBLIC_KEY); newCertTemp.getSubject().setByteArrayValue( cert.getCert().getSubjectX500Principal().getEncoded()); newCertTemp.getIssuer().setByteArrayValue( cert.getCert().getIssuerX500Principal().getEncoded()); newCertTemp.getSerialNumber().setByteArrayValue( cert.getCert().getSerialNumber().toByteArray()); newCertTemp.getValue().setByteArrayValue(cert.getEncodedCert()); return newCertTemp; } private static void setKeyAttributes(final String label, final long keyType, final PublicKey publicKey, final PrivateKey privateKey) { if (privateKey != null) { privateKey.getToken().setBooleanValue(true); privateKey.getLabel().setCharArrayValue(label.toCharArray()); privateKey.getKeyType().setLongValue(keyType); privateKey.getSign().setBooleanValue(true); privateKey.getPrivate().setBooleanValue(true); privateKey.getSensitive().setBooleanValue(true); } if (publicKey != null) { publicKey.getToken().setBooleanValue(true); publicKey.getLabel().setCharArrayValue(label.toCharArray()); publicKey.getKeyType().setLongValue(keyType); publicKey.getVerify().setBooleanValue(true); publicKey.getModifiable().setBooleanValue(Boolean.TRUE); } } @Override protected void doUpdateCertificate(final P11ObjectIdentifier objectId, final X509Certificate newCert) throws XiSecurityException, P11TokenException { removeCerts(objectId); try { Thread.sleep(1000); } catch (InterruptedException ex) { // CHECKSTYLE:SKIP } X509PublicKeyCertificate newCertTemp = createPkcs11Template(new X509Cert(newCert), objectId.getId(), objectId.getLabelChars()); Session session = borrowWritableSession(); try { session.createObject(newCertTemp); } catch (TokenException ex) { throw new P11TokenException("could not createObject: " + ex.getMessage(), ex); } finally { returnWritableSession(session); } } private X509PublicKeyCertificate[] getCertificateObjects(final byte[] keyId, final char[] keyLabel) throws P11TokenException { X509PublicKeyCertificate template = new X509PublicKeyCertificate(); if (keyId != null) { template.getId().setByteArrayValue(keyId); } if (keyLabel != null) { template.getLabel().setCharArrayValue(keyLabel); } List<Storage> tmpObjects; Session session = borrowIdleSession(); try { tmpObjects = getObjects(session, template); } finally { returnIdleSession(session); } if (CollectionUtil.isEmpty(tmpObjects)) { LOG.info("found no certificate identified by {}", getDescription(keyId, keyLabel)); return null; } int size = tmpObjects.size(); X509PublicKeyCertificate[] certs = new X509PublicKeyCertificate[size]; for (int i = 0; i < size; i++) { certs[i] = (X509PublicKeyCertificate) tmpObjects.get(i); } return certs; } @Override protected void doRemoveIdentity(final P11ObjectIdentifier objectId) throws P11TokenException { Session session = borrowWritableSession(); try { PrivateKey privKey = getPrivateKeyObject(objectId.getId(), objectId.getLabelChars()); if (privKey != null) { try { session.destroyObject(privKey); } catch (TokenException ex) { String msg = "could not delete private key " + objectId; LogUtil.error(LOG, ex, msg); throw new P11TokenException(msg); } } PublicKey pubKey = getPublicKeyObject(objectId.getId(), objectId.getLabelChars()); if (pubKey != null) { try { session.destroyObject(pubKey); } catch (TokenException ex) { String msg = "could not delete public key " + objectId; LogUtil.error(LOG, ex, msg); throw new P11TokenException(msg); } } X509PublicKeyCertificate[] certs = getCertificateObjects(objectId.getId(), objectId.getLabelChars()); if (certs != null && certs.length > 0) { for (int i = 0; i < certs.length; i++) { try { session.destroyObject(certs[i]); } catch (TokenException ex) { String msg = "could not delete certificate " + objectId; LogUtil.error(LOG, ex, msg); throw new P11TokenException(msg); } } } } finally { returnWritableSession(session); } } private static byte[] generateKeyId(@NonNull final Session session) throws P11TokenException { SecureRandom random = new SecureRandom(); byte[] keyId = null; do { keyId = new byte[8]; random.nextBytes(keyId); } while (idExists(session, keyId)); return keyId; } private static boolean idExists(@NonNull final Session session, @NonNull final byte[] keyId) throws P11TokenException { Key key = new Key(); key.getId().setByteArrayValue(keyId); Object[] objects; try { session.findObjectsInit(key); objects = session.findObjects(1); session.findObjectsFinal(); if (objects.length > 0) { return true; } X509PublicKeyCertificate cert = new X509PublicKeyCertificate(); cert.getId().setByteArrayValue(keyId); session.findObjectsInit(cert); objects = session.findObjects(1); session.findObjectsFinal(); } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } return objects.length > 0; } private static boolean labelExists(@NonNull final Session session, @NonNull final String keyLabel) throws P11TokenException { ParamUtil.requireNonBlank("keyLabel", keyLabel); Key key = new Key(); key.getLabel().setCharArrayValue(keyLabel.toCharArray()); Object[] objects; try { session.findObjectsInit(key); objects = session.findObjects(1); session.findObjectsFinal(); if (objects.length > 0) { return true; } X509PublicKeyCertificate cert = new X509PublicKeyCertificate(); cert.getLabel().setCharArrayValue(keyLabel.toCharArray()); session.findObjectsInit(cert); objects = session.findObjects(1); session.findObjectsFinal(); } catch (TokenException ex) { throw new P11TokenException(ex.getMessage(), ex); } return objects.length > 0; } }