/* * * 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.remotep11.server; import java.io.IOException; import java.security.InvalidKeyException; import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.util.encoders.Hex; 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.security.exception.BadAsn1ObjectException; import org.xipki.commons.security.exception.P11DuplicateEntityException; import org.xipki.commons.security.exception.P11TokenException; import org.xipki.commons.security.exception.P11UnknownEntityException; import org.xipki.commons.security.exception.P11UnsupportedMechanismException; import org.xipki.commons.security.exception.XiSecurityException; import org.xipki.commons.security.pkcs11.P11CryptService; import org.xipki.commons.security.pkcs11.P11EntityIdentifier; import org.xipki.commons.security.pkcs11.P11Identity; import org.xipki.commons.security.pkcs11.P11ObjectIdentifier; import org.xipki.commons.security.pkcs11.P11Params; import org.xipki.commons.security.pkcs11.P11Slot; import org.xipki.commons.security.pkcs11.P11SlotIdentifier; import org.xipki.commons.security.pkcs11.proxy.P11ProxyConstants; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1EntityIdAndCert; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1GenDSAKeypairParams; 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.Asn1P11Params; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1P11SlotIdentifier; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1RSAPkcsPssParams; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1RemoveObjectsParams; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1ServerCaps; import org.xipki.commons.security.pkcs11.proxy.msg.Asn1SignTemplate; import org.xipki.commons.security.util.KeyUtil; import org.xipki.commons.security.util.X509Util; /** * @author Lijun Liao * @since 2.0.0 */ class P11ProxyResponder { private static final Logger LOG = LoggerFactory.getLogger(P11ProxyResponder.class); private static final Set<Short> actionsRequireNonNullRequest; private static final Set<Short> actionsRequireNullRequest; private final Set<Short> versions; static { Set<Short> actions = new HashSet<>(); actions.add(P11ProxyConstants.ACTION_GET_SERVER_CAPS); actions.add(P11ProxyConstants.ACTION_GET_SLOT_IDS); actionsRequireNullRequest = Collections.unmodifiableSet(actions); actions = new HashSet<>(); actions.add(P11ProxyConstants.ACTION_ADD_CERT); actions.add(P11ProxyConstants.ACTION_GEN_KEYPAIR_DSA); actions.add(P11ProxyConstants.ACTION_GEN_KEYPAIR_EC); actions.add(P11ProxyConstants.ACTION_GEN_KEYPAIR_RSA); actions.add(P11ProxyConstants.ACTION_GET_CERT); actions.add(P11ProxyConstants.ACTION_GET_CERT_IDS); actions.add(P11ProxyConstants.ACTION_GET_IDENTITY_IDS); actions.add(P11ProxyConstants.ACTION_GET_MECHANISMS); actions.add(P11ProxyConstants.ACTION_GET_PUBLICKEY); actions.add(P11ProxyConstants.ACTION_REMOVE_CERTS); actions.add(P11ProxyConstants.ACTION_REMOVE_IDENTITY); actions.add(P11ProxyConstants.ACTION_REMOVE_OBJECTS); actions.add(P11ProxyConstants.ACTION_SIGN); actions.add(P11ProxyConstants.ACTION_UPDATE_CERT); actionsRequireNonNullRequest = Collections.unmodifiableSet(actions); } P11ProxyResponder() { Set<Short> tmpVersions = new HashSet<>(); tmpVersions.add(P11ProxyConstants.VERSION_V1_0); this.versions = Collections.unmodifiableSet(tmpVersions); } public Set<Short> getVersions() { return versions; } /** * The request is constructed as follows: * <pre> * 0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 * | Version | Transaction ID | Body ... | * | ... Length | Action | Module ID | Content... | * | .Content | <-- 10 + Length (offset). * * </pre> */ byte[] processRequest(final LocalP11CryptServicePool pool, final byte[] request) { int reqLen = request.length; // TransactionID byte[] transactionId = new byte[4]; if (reqLen > 5) { System.arraycopy(request, 2, transactionId, 0, 4); } // Action short action = P11ProxyConstants.ACTION_NOPE; if (reqLen > 11) { action = IoUtil.parseShort(request, 10); } if (reqLen < 14) { LOG.error("response too short"); return getResp(P11ProxyConstants.VERSION_V1_0, transactionId, action, P11ProxyConstants.RC_BAD_REQUEST); } // Version short version = IoUtil.parseShort(request, 0); if (!versions.contains(version)) { LOG.error("unsupported version {}", version); return getResp(P11ProxyConstants.VERSION_V1_0, transactionId, action, P11ProxyConstants.RC_UNSUPPORTED_VERSION); } // Length int reqBodyLen = IoUtil.parseInt(request, 6); if (reqBodyLen + 10 != reqLen) { LOG.error("message length unmatch"); return getResp(version, transactionId, action, P11ProxyConstants.RC_BAD_REQUEST); } short moduleId = IoUtil.parseShort(request, 12); int contentLen = reqLen - 14; byte[] content; if (contentLen == 0) { if (actionsRequireNonNullRequest.contains(action)) { LOG.error("content is not present but is required"); return getResp(version, transactionId, P11ProxyConstants.RC_BAD_REQUEST, action); } content = null; } else { if (actionsRequireNullRequest.contains(action)) { LOG.error("content is present but is not permitted"); return getResp(version, transactionId, P11ProxyConstants.RC_BAD_REQUEST, action); } content = new byte[contentLen]; System.arraycopy(request, 14, content, 0, contentLen); } P11CryptService p11CryptService = pool.getP11CryptService(moduleId); if (p11CryptService == null) { LOG.error("no module {} available", moduleId); return getResp(version, transactionId, P11ProxyConstants.RC_UNKNOWN_MODULE, action); } try { switch (action) { case P11ProxyConstants.ACTION_GET_SERVER_CAPS: { boolean readOnly = p11CryptService.getModule().isReadOnly(); ASN1Object obj = new Asn1ServerCaps(readOnly, versions); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_ADD_CERT: { Asn1EntityIdAndCert asn1 = Asn1EntityIdAndCert.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1.getEntityId()); X509Certificate cert = X509Util.toX509Cert(asn1.getCertificate()); slot.addCert(asn1.getEntityId().getObjectId().getObjectId(), cert); return getSuccessResp(version, transactionId, action, (byte[]) null); } case P11ProxyConstants.ACTION_GEN_KEYPAIR_DSA: { Asn1GenDSAKeypairParams asn1 = Asn1GenDSAKeypairParams.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1.getSlotId()); P11ObjectIdentifier keyId = slot.generateDSAKeypair(asn1.getP(), asn1.getQ(), asn1.getG(), asn1.getLabel()); ASN1Object obj = new Asn1P11EntityIdentifier(asn1.getSlotId().getSlotId(), keyId); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_GEN_KEYPAIR_EC: { Asn1GenECKeypairParams asn1 = Asn1GenECKeypairParams.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1.getSlotId()); P11ObjectIdentifier keyId = slot.generateECKeypair(asn1.getCurveId().getId(), asn1.getLabel()); ASN1Object obj = new Asn1P11EntityIdentifier(asn1.getSlotId().getSlotId(), keyId); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_GEN_KEYPAIR_RSA: { Asn1GenRSAKeypairParams asn1 = Asn1GenRSAKeypairParams.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1.getSlotId()); P11ObjectIdentifier keyId = slot.generateRSAKeypair(asn1.getKeysize(), asn1.getPublicExponent(), asn1.getLabel()); ASN1Object obj = new Asn1P11EntityIdentifier(asn1.getSlotId().getSlotId(), keyId); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_GET_CERT: { P11EntityIdentifier entityId = Asn1P11EntityIdentifier.getInstance(content).getEntityId(); X509Certificate cert = p11CryptService.getIdentity(entityId).getCertificate(); return getSuccessResp(version, transactionId, action, cert.getEncoded()); } case P11ProxyConstants.ACTION_GET_CERT_IDS: case P11ProxyConstants.ACTION_GET_IDENTITY_IDS: { Asn1P11SlotIdentifier slotId = Asn1P11SlotIdentifier.getInstance(content); P11Slot slot = p11CryptService.getModule().getSlot(slotId.getSlotId()); Set<P11ObjectIdentifier> objectIds; if (P11ProxyConstants.ACTION_GET_CERT_IDS == action) { objectIds = slot.getCertIdentifiers(); } else { objectIds = slot.getIdentityIdentifiers(); } ASN1EncodableVector vec = new ASN1EncodableVector(); for (P11ObjectIdentifier objectId : objectIds) { vec.add(new Asn1P11ObjectIdentifier(objectId)); } ASN1Object obj = new DERSequence(vec); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_GET_MECHANISMS: { P11SlotIdentifier slotId = Asn1P11SlotIdentifier.getInstance(content).getSlotId(); Set<Long> mechs = p11CryptService.getSlot(slotId).getMechanisms(); ASN1EncodableVector vec = new ASN1EncodableVector(); for (Long mech : mechs) { vec.add(new ASN1Integer(mech)); } ASN1Object obj = new DERSequence(vec); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_GET_PUBLICKEY: { P11EntityIdentifier identityId = Asn1P11EntityIdentifier.getInstance(content).getEntityId(); PublicKey pubKey = p11CryptService.getIdentity(identityId).getPublicKey(); if (pubKey == null) { throw new P11UnknownEntityException(identityId); } ASN1Object obj = KeyUtil.createSubjectPublicKeyInfo(pubKey); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_GET_SLOT_IDS: { List<P11SlotIdentifier> slotIds = p11CryptService.getModule().getSlotIdentifiers(); ASN1EncodableVector vector = new ASN1EncodableVector(); for (P11SlotIdentifier slotId : slotIds) { vector.add(new Asn1P11SlotIdentifier(slotId)); } ASN1Object obj = new DERSequence(vector); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_REMOVE_CERTS: { Asn1P11EntityIdentifier asn1 = Asn1P11EntityIdentifier.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1); slot.removeCerts(asn1.getObjectId().getObjectId()); return getSuccessResp(version, transactionId, action, (byte[])null); } case P11ProxyConstants.ACTION_REMOVE_IDENTITY: { Asn1P11EntityIdentifier asn1 = Asn1P11EntityIdentifier.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1); slot.removeIdentity(asn1.getObjectId().getObjectId()); return getSuccessResp(version, transactionId, action, (byte[])null); } case P11ProxyConstants.ACTION_SIGN: { Asn1SignTemplate signTemplate = Asn1SignTemplate.getInstance(content); long mechanism = signTemplate.getMechanism().getMechanism(); Asn1P11Params tmpParams = signTemplate.getMechanism().getParams(); ASN1Encodable asn1Params = null; if (tmpParams != null) { asn1Params = tmpParams.getP11Params(); } P11Params params = null; if (asn1Params instanceof Asn1RSAPkcsPssParams) { params = Asn1RSAPkcsPssParams.getInstance(asn1Params).getPkcsPssParams(); } else if (asn1Params != null) { throw new BadAsn1ObjectException("unknown SignTemplate.params"); } byte[] message = signTemplate.getMessage(); P11Identity identity = p11CryptService.getIdentity( signTemplate.getIdentityId().getEntityId()); byte[] signature = identity.sign(mechanism, params, message); ASN1Object obj = new DEROctetString(signature); return getSuccessResp(version, transactionId, action, obj); } case P11ProxyConstants.ACTION_UPDATE_CERT: { Asn1EntityIdAndCert asn1 = Asn1EntityIdAndCert.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1.getEntityId()); slot.updateCertificate(asn1.getEntityId().getObjectId().getObjectId(), X509Util.toX509Cert(asn1.getCertificate())); return getSuccessResp(version, transactionId, action, (byte[])null); } case P11ProxyConstants.ACTION_REMOVE_OBJECTS: { Asn1RemoveObjectsParams asn1 = Asn1RemoveObjectsParams.getInstance(content); P11Slot slot = getSlot(p11CryptService, asn1.getSlotId()); int num = slot.removeObjects(asn1.getObjectId(), asn1.getObjectLabel()); ASN1Object obj = new ASN1Integer(num); return getSuccessResp(version, transactionId, action, obj); } default: { LOG.error("unsupported XiPKI action code '{}'", action); return getResp(version, transactionId, action, P11ProxyConstants.RC_UNSUPPORTED_ACTION); } } } catch (BadAsn1ObjectException ex) { LogUtil.error(LOG, ex, "could not process decode requested content (tid=" + Hex.toHexString(transactionId) + ")"); return getResp(version, transactionId, action, P11ProxyConstants.RC_BAD_REQUEST); } catch (P11TokenException ex) { LogUtil.error(LOG, ex, buildErrorMsg(action, transactionId)); short rc; if (ex instanceof P11UnknownEntityException) { rc = P11ProxyConstants.RC_DUPLICATE_ENTITY; } else if (ex instanceof P11DuplicateEntityException) { rc = P11ProxyConstants.RC_DUPLICATE_ENTITY; } else if (ex instanceof P11UnsupportedMechanismException) { rc = P11ProxyConstants.RC_UNSUPPORTED_MECHANISM; } else { rc = P11ProxyConstants.RC_P11_TOKENERROR; } return getResp(version, transactionId, action, rc); } catch (XiSecurityException | CertificateException | InvalidKeyException ex) { LogUtil.error(LOG, ex, buildErrorMsg(action, transactionId)); return getResp(version, transactionId, action, P11ProxyConstants.RC_INTERNAL_ERROR); } catch (Throwable th) { LogUtil.error(LOG, th, buildErrorMsg(action, transactionId)); return getResp(version, transactionId, action, P11ProxyConstants.RC_INTERNAL_ERROR); } } // method processPkiMessage private static final String buildErrorMsg(short action, byte[] transactionId) { return "could not process action " + P11ProxyConstants.getActionName(action) + " (tid=" + Hex.toHexString(transactionId) + ")"; } private P11Slot getSlot(final P11CryptService p11Service, final Asn1P11EntityIdentifier entityId) throws P11TokenException { return p11Service.getModule().getSlot(entityId.getSlotId().getSlotId()); } private P11Slot getSlot(final P11CryptService p11Service, final Asn1P11SlotIdentifier slotId) throws P11TokenException { return p11Service.getModule().getSlot(slotId.getSlotId()); } private static byte[] getResp(short version, byte[] transactionId, short rc, short action) { byte[] resp = new byte[14]; IoUtil.writeShort(version, resp, 0); // version System.arraycopy(transactionId, 0, resp, 2, 4); // transaction Id IoUtil.writeInt(4, resp, 6); // length IoUtil.writeShort(rc, resp, 10); // RC IoUtil.writeShort(action, resp, 12); // action return resp; } private static byte[] getSuccessResp(short version, byte[] transactionId, short action, ASN1Object respContent) { byte[] encoded; try { encoded = respContent.getEncoded(); } catch (IOException ex) { LogUtil.error(LOG, ex, "could not encode response ASN1Object"); return getResp(version, transactionId, action, P11ProxyConstants.RC_INTERNAL_ERROR); } return getSuccessResp(version, transactionId, action, encoded); } private static byte[] getSuccessResp(short version, byte[] transactionId, short action, byte[] respContent) { int bodyLen = 4; if (respContent != null) { bodyLen += respContent.length; } byte[] resp = (respContent == null) ? new byte[14] : new byte[10 + bodyLen]; IoUtil.writeShort(version, resp, 0); // version System.arraycopy(transactionId, 0, resp, 2, 4); // transaction Id IoUtil.writeInt(bodyLen, resp, 6); // length IoUtil.writeShort(P11ProxyConstants.RC_SUCCESS, resp, 10); // RC IoUtil.writeShort(action, resp, 12); // action if (respContent != null) { System.arraycopy(respContent, 0, resp, 14, respContent.length); } return resp; } }