/* * * 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.proxy; import java.math.BigInteger; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.List; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xipki.commons.common.util.StringUtil; import org.xipki.commons.security.X509Cert; import org.xipki.commons.security.exception.BadAsn1ObjectException; 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.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.pkcs11.proxy.msg.Asn1GenECKeypairParams; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1GenRSAKeypairParams; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1P11EntityIdentifier; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1P11ObjectIdentifier; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1P11ObjectIdentifiers; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1P11SlotIdentifier; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1RemoveObjectsParams; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1EntityIdAndCert; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1GenDSAKeypairParams; import org.xipki.commons.security.util.KeyUtil; import org.xipki.commons.security.util.X509Util; /** * @author Lijun Liao * @since 2.0.0 */ public class ProxyP11Slot extends AbstractP11Slot { private static final Logger LOG = LoggerFactory.getLogger(ProxyP11Slot.class); private final ProxyP11Module module; private final Asn1P11SlotIdentifier asn1SlotId; ProxyP11Slot(final ProxyP11Module module, final P11SlotIdentifier slotId, final boolean readOnly, final P11MechanismFilter mechanismFilter) throws P11TokenException { super(module.getName(), slotId, readOnly, mechanismFilter); this.module = module; this.asn1SlotId = new Asn1P11SlotIdentifier(slotId); refresh(); } @Override protected P11SlotRefreshResult doRefresh() throws P11TokenException { P11SlotRefreshResult refreshResult = new P11SlotRefreshResult(); // mechanisms List<Long> mechs = getMechanismsFromServer(); for (Long mech : mechs) { refreshResult.addMechanism(mech); } // certificates List<P11ObjectIdentifier> certIds = getObjectIdsFromServer(P11ProxyConstants.ACTION_GET_CERT_IDS); for (P11ObjectIdentifier certId : certIds) { X509Cert cert = getCertificate(certId); if (cert != null) { refreshResult.addCertificate(certId, cert); } } // keys List<P11ObjectIdentifier> keyIds = getObjectIdsFromServer(P11ProxyConstants.ACTION_GET_IDENTITY_IDS); for (P11ObjectIdentifier keyId : keyIds) { byte[] id = keyId.getId(); java.security.PublicKey pubKey = null; X509Cert cert = refreshResult.getCertForId(id); if (cert != null) { pubKey = cert.getCert().getPublicKey(); } else { pubKey = getPublicKey(keyId); if (pubKey == null) { LOG.warn("Neither public key nor certificate is associated with private key {}", keyId); continue; } } X509Certificate[] certs = (cert == null) ? null : new X509Certificate[]{cert.getCert()}; ProxyP11Identity identity = new ProxyP11Identity(this, new P11EntityIdentifier(slotId, keyId), pubKey, certs); refreshResult.addIdentity(identity); } return refreshResult; } @Override public void close() { } private PublicKey getPublicKey(final P11ObjectIdentifier objectId) throws P11UnknownEntityException, P11TokenException { P11EntityIdentifier entityId = new P11EntityIdentifier(slotId, objectId); byte[] resp = module.send(P11ProxyConstants.ACTION_GET_PUBLICKEY, new Asn1P11EntityIdentifier(entityId)); if (resp == null) { return null; } SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfo.getInstance(resp); try { return KeyUtil.generatePublicKey(pkInfo); } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { throw new P11TokenException("could not generate Public Key from SubjectPublicKeyInfo:" + ex.getMessage(), ex); } } private X509Cert getCertificate(final P11ObjectIdentifier certId) throws P11TokenException { P11EntityIdentifier entityId = new P11EntityIdentifier(slotId, certId); byte[] resp = module.send(P11ProxyConstants.ACTION_GET_CERT, new Asn1P11EntityIdentifier(entityId)); if (resp == null) { return null; } try { return new X509Cert(X509Util.parseCert(resp), resp); } catch (CertificateException ex) { throw new P11TokenException("could not parse certificate:" + ex.getMessage(), ex); } } @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"); } Asn1RemoveObjectsParams params = new Asn1RemoveObjectsParams(slotId, id, label); byte[] resp = module.send(P11ProxyConstants.ACTION_REMOVE_OBJECTS, params); try { return ASN1Integer.getInstance(resp).getValue().intValue(); } catch (IllegalArgumentException ex) { throw new P11TokenException(ex.getMessage(), ex); } } @Override protected void doRemoveIdentity(final P11ObjectIdentifier objectId) throws P11TokenException { Asn1P11EntityIdentifier asn1EntityId = new Asn1P11EntityIdentifier(slotId, objectId); module.send(P11ProxyConstants.ACTION_REMOVE_IDENTITY, asn1EntityId); } @Override protected void doAddCert(final P11ObjectIdentifier objectId, final X509Certificate cert) throws P11TokenException, XiSecurityException { Asn1EntityIdAndCert asn1 = new Asn1EntityIdAndCert( new P11EntityIdentifier(slotId, objectId), cert); module.send(P11ProxyConstants.ACTION_ADD_CERT, asn1); } @Override protected void doRemoveCerts(final P11ObjectIdentifier objectId) throws P11TokenException { Asn1P11EntityIdentifier asn1EntityId = new Asn1P11EntityIdentifier(slotId, objectId); module.send(P11ProxyConstants.ACTION_REMOVE_CERTS, asn1EntityId); } @Override protected P11Identity doGenerateRSAKeypair(final int keysize, final BigInteger publicExponent, final String label) throws P11TokenException { Asn1GenRSAKeypairParams asn1 = new Asn1GenRSAKeypairParams(asn1SlotId, label, keysize, publicExponent); byte[] resp = module.send(P11ProxyConstants.ACTION_GEN_KEYPAIR_RSA, asn1); return parseGenerateKeypairResult(resp); } @Override // CHECKSTYLE:OFF protected P11Identity doGenerateDSAKeypair(final BigInteger p, final BigInteger q, final BigInteger g, final String label) throws P11TokenException { // CHECKSTYLE:ON Asn1GenDSAKeypairParams asn1 = new Asn1GenDSAKeypairParams(asn1SlotId, label, p, q, g); byte[] resp = module.send(P11ProxyConstants.ACTION_GEN_KEYPAIR_DSA, asn1); return parseGenerateKeypairResult(resp); } @Override protected P11Identity doGenerateECKeypair(final ASN1ObjectIdentifier curveId, final String label) throws P11TokenException { Asn1GenECKeypairParams asn1 = new Asn1GenECKeypairParams(asn1SlotId, label, curveId); byte[] resp = module.send(P11ProxyConstants.ACTION_GEN_KEYPAIR_EC, asn1); return parseGenerateKeypairResult(resp); } private P11Identity parseGenerateKeypairResult(final byte[] resp) throws P11TokenException { if (resp == null) { throw new P11TokenException("server returned no result"); } Asn1P11EntityIdentifier ei; try { ei = Asn1P11EntityIdentifier.getInstance(resp); } catch (BadAsn1ObjectException ex) { throw new P11TokenException( "invalid ASN1 object Asn1P11EntityIdentifier: " + ex.getMessage(), ex); } if (!slotId.equals(ei.getSlotId().getSlotId())) { throw new P11TokenException(""); } P11EntityIdentifier entityId = ei.getEntityId(); PublicKey publicKey = getPublicKey(entityId.getObjectId()); return new ProxyP11Identity(this, entityId, publicKey, null); } @Override protected void doUpdateCertificate(final P11ObjectIdentifier objectId, final X509Certificate newCert) throws XiSecurityException, P11TokenException { Asn1EntityIdAndCert asn1 = new Asn1EntityIdAndCert( new P11EntityIdentifier(slotId, objectId), newCert); module.send(P11ProxyConstants.ACTION_UPDATE_CERT, asn1); } private List<Long> getMechanismsFromServer() throws P11TokenException { Asn1P11SlotIdentifier asn1SlotId = new Asn1P11SlotIdentifier(slotId); byte[] resp = module.send(P11ProxyConstants.ACTION_GET_MECHANISMS, asn1SlotId); ASN1Sequence seq = requireSequence(resp); final int n = seq.size(); List<Long> mechs = new ArrayList<>(n); for ( int i = 0; i < n; i++) { long mech = ASN1Integer.getInstance(seq.getObjectAt(i)).getValue().longValue(); mechs.add(mech); } return mechs; } private List<P11ObjectIdentifier> getObjectIdsFromServer(final short action) throws P11TokenException { Asn1P11SlotIdentifier asn1SlotId = new Asn1P11SlotIdentifier(slotId); byte[] resp = module.send(action, asn1SlotId); List<Asn1P11ObjectIdentifier> asn1ObjectIds; try { asn1ObjectIds = Asn1P11ObjectIdentifiers.getInstance(resp).getObjectIds(); } catch (BadAsn1ObjectException ex) { throw new P11TokenException("bad ASN1 object: " + ex.getMessage(), ex); } List<P11ObjectIdentifier> objectIds = new ArrayList<>(asn1ObjectIds.size()); for (Asn1P11ObjectIdentifier asn1Id : asn1ObjectIds) { objectIds.add(asn1Id.getObjectId()); } return objectIds; } private ASN1Sequence requireSequence(final byte[] response) throws P11TokenException { try { return ASN1Sequence.getInstance(response); } catch (IllegalArgumentException ex) { throw new P11TokenException("response is not ASN1Sequence", ex); } } ProxyP11Module getModule() { return module; } }