org.xipki.remotep11.server.impl.CmpResponder.java Source code

Java tutorial

Introduction

Here is the source code for org.xipki.remotep11.server.impl.CmpResponder.java

Source

/*
 *
 * This file is part of the XiPKI project.
 * Copyright (c) 2014 - 2015 Lijun Liao
 * Author: 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.remotep11.server.impl;

import java.security.SecureRandom;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.cmp.ErrorMsgContent;
import org.bouncycastle.asn1.cmp.GenMsgContent;
import org.bouncycastle.asn1.cmp.GenRepContent;
import org.bouncycastle.asn1.cmp.InfoTypeAndValue;
import org.bouncycastle.asn1.cmp.PKIBody;
import org.bouncycastle.asn1.cmp.PKIFailureInfo;
import org.bouncycastle.asn1.cmp.PKIFreeText;
import org.bouncycastle.asn1.cmp.PKIHeader;
import org.bouncycastle.asn1.cmp.PKIHeaderBuilder;
import org.bouncycastle.asn1.cmp.PKIMessage;
import org.bouncycastle.asn1.cmp.PKIStatus;
import org.bouncycastle.asn1.cmp.PKIStatusInfo;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.cert.cmp.GeneralPKIMessage;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.common.ObjectIdentifiers;
import org.xipki.common.XipkiCmpConstants;
import org.xipki.security.api.p11.P11CryptService;
import org.xipki.security.api.p11.P11KeyIdentifier;
import org.xipki.security.api.p11.P11SlotIdentifier;
import org.xipki.security.api.p11.remote.KeyIdentifier;
import org.xipki.security.api.p11.remote.PSOTemplate;
import org.xipki.security.api.p11.remote.SlotAndKeyIdentifer;
import org.xipki.security.api.p11.remote.SlotIdentifier;

/**
 * @author Lijun Liao
 */

class CmpResponder {
    private static final Logger LOG = LoggerFactory.getLogger(CmpResponder.class);

    private final SecureRandom random = new SecureRandom();
    private final GeneralName sender = XipkiCmpConstants.remoteP11_cmp_server;

    CmpResponder() {
    }

    PKIMessage processPKIMessage(final LocalP11CryptServicePool localP11CryptServicePool, final String moduleName,
            final PKIMessage pkiMessage) {
        GeneralPKIMessage message = new GeneralPKIMessage(pkiMessage);

        PKIHeader reqHeader = message.getHeader();
        ASN1OctetString tid = reqHeader.getTransactionID();

        if (tid == null) {
            byte[] randomBytes = randomTransactionId();
            tid = new DEROctetString(randomBytes);
        }
        String tidStr = Hex.toHexString(tid.getOctets());

        PKIHeaderBuilder respHeaderBuilder = new PKIHeaderBuilder(reqHeader.getPvno().getValue().intValue(), sender,
                reqHeader.getSender());
        respHeaderBuilder.setTransactionID(tid);

        PKIBody reqBody = message.getBody();
        final int type = reqBody.getType();

        PKIHeader respHeader = respHeaderBuilder.build();

        if (type != PKIBody.TYPE_GEN_MSG) {
            ErrorMsgContent emc = new ErrorMsgContent(new PKIStatusInfo(PKIStatus.rejection,
                    new PKIFreeText("unsupported type " + type), new PKIFailureInfo(PKIFailureInfo.badRequest)));

            PKIBody respBody = new PKIBody(PKIBody.TYPE_ERROR, emc);

            return new PKIMessage(respHeader, respBody);
        }

        GenMsgContent genMsgBody = (GenMsgContent) reqBody.getContent();
        InfoTypeAndValue[] itvs = genMsgBody.toInfoTypeAndValueArray();

        InfoTypeAndValue itv = null;
        if (itvs != null && itvs.length > 0) {
            for (InfoTypeAndValue m : itvs) {
                ASN1ObjectIdentifier itvType = m.getInfoType();
                if (ObjectIdentifiers.id_xipki_cmp.equals(itvType)) {
                    itv = m;
                    break;
                }
            }
        }

        if (itv == null) {
            final String statusMessage = String.format("PKIBody type %s is only supported with the sub-knownTypes",
                    ObjectIdentifiers.id_xipki_cmp.getId());
            return createRejectionPKIMessage(respHeader, PKIFailureInfo.badRequest, statusMessage);
        }

        try {
            ASN1Encodable asn1 = itv.getInfoValue();
            ASN1Integer asn1Code = null;
            ASN1Encodable reqValue = null;

            try {
                ASN1Sequence seq = ASN1Sequence.getInstance(asn1);
                asn1Code = ASN1Integer.getInstance(seq.getObjectAt(0));
                if (seq.size() > 1) {
                    reqValue = seq.getObjectAt(1);
                }
            } catch (IllegalArgumentException e) {
                final String statusMessage = "invalid value of the InfoTypeAndValue for "
                        + ObjectIdentifiers.id_xipki_cmp.getId();
                return createRejectionPKIMessage(respHeader, PKIFailureInfo.badRequest, statusMessage);
            }

            int action = asn1Code.getPositiveValue().intValue();
            ASN1Encodable respItvInfoValue;

            P11CryptService p11CryptService = localP11CryptServicePool.getP11CryptService(moduleName);

            switch (action) {
            case XipkiCmpConstants.ACTION_RP11_VERSION: {
                respItvInfoValue = new ASN1Integer(localP11CryptServicePool.getVersion());
                break;
            }
            case XipkiCmpConstants.ACTION_RP11_PSO_DSA_PLAIN:
            case XipkiCmpConstants.ACTION_RP11_PSO_DSA_X962:
            case XipkiCmpConstants.ACTION_RP11_PSO_ECDSA_PLAIN:
            case XipkiCmpConstants.ACTION_RP11_PSO_ECDSA_X962:
            case XipkiCmpConstants.ACTION_RP11_PSO_RSA_PKCS:
            case XipkiCmpConstants.ACTION_RP11_PSO_RSA_X509: {
                byte[] psoMessage = null;
                P11SlotIdentifier slot = null;
                P11KeyIdentifier keyId = null;
                {
                    try {
                        PSOTemplate psoTemplate = PSOTemplate.getInstance(reqValue);
                        psoMessage = psoTemplate.getMessage();
                        SlotAndKeyIdentifer slotAndKeyIdentifier = psoTemplate.getSlotAndKeyIdentifer();
                        slot = slotAndKeyIdentifier.getSlotIdentifier().getSlotId();
                        KeyIdentifier keyIdentifier = slotAndKeyIdentifier.getKeyIdentifier();
                        keyId = keyIdentifier.getKeyId();
                    } catch (IllegalArgumentException e) {
                        final String statusMessage = "invalid PSOTemplate";
                        return createRejectionPKIMessage(respHeader, PKIFailureInfo.badRequest, statusMessage);
                    }
                }

                byte[] signature;

                if (XipkiCmpConstants.ACTION_RP11_PSO_ECDSA_PLAIN == action) {
                    signature = p11CryptService.CKM_ECDSA_Plain(psoMessage, slot, keyId);
                } else if (XipkiCmpConstants.ACTION_RP11_PSO_ECDSA_X962 == action) {
                    signature = p11CryptService.CKM_ECDSA_X962(psoMessage, slot, keyId);
                } else if (XipkiCmpConstants.ACTION_RP11_PSO_DSA_PLAIN == action) {
                    signature = p11CryptService.CKM_DSA_Plain(psoMessage, slot, keyId);
                } else if (XipkiCmpConstants.ACTION_RP11_PSO_DSA_X962 == action) {
                    signature = p11CryptService.CKM_DSA_X962(psoMessage, slot, keyId);
                } else if (XipkiCmpConstants.ACTION_RP11_PSO_RSA_X509 == action) {
                    signature = p11CryptService.CKM_RSA_X509(psoMessage, slot, keyId);
                } else if (XipkiCmpConstants.ACTION_RP11_PSO_RSA_PKCS == action) {
                    signature = p11CryptService.CKM_RSA_PKCS(psoMessage, slot, keyId);
                } else {
                    throw new RuntimeException("should not reach here");
                }

                respItvInfoValue = new DEROctetString(signature);
                break;
            }
            case XipkiCmpConstants.ACTION_RP11_GET_CERTIFICATE:
            case XipkiCmpConstants.ACTION_RP11_GET_PUBLICKEY: {
                P11SlotIdentifier slot = null;
                P11KeyIdentifier keyId = null;
                try {
                    SlotAndKeyIdentifer slotAndKeyIdentifier = SlotAndKeyIdentifer.getInstance(reqValue);
                    slot = slotAndKeyIdentifier.getSlotIdentifier().getSlotId();
                    KeyIdentifier keyIdentifier = slotAndKeyIdentifier.getKeyIdentifier();
                    keyId = keyIdentifier.getKeyId();
                } catch (IllegalArgumentException e) {
                    final String statusMessage = "invalid SlotAndKeyIdentifier";
                    return createRejectionPKIMessage(respHeader, PKIFailureInfo.badRequest, statusMessage);
                }

                byte[] encodeCertOrKey;
                if (XipkiCmpConstants.ACTION_RP11_GET_CERTIFICATE == action) {
                    encodeCertOrKey = p11CryptService.getCertificate(slot, keyId).getEncoded();
                } else if (XipkiCmpConstants.ACTION_RP11_GET_PUBLICKEY == action) {
                    encodeCertOrKey = p11CryptService.getPublicKey(slot, keyId).getEncoded();
                } else {
                    throw new RuntimeException("should not reach here");
                }

                respItvInfoValue = new DEROctetString(encodeCertOrKey);
                break;
            }
            case XipkiCmpConstants.ACTION_RP11_LIST_SLOTS: {
                P11SlotIdentifier[] slotIds = p11CryptService.getSlotIdentifiers();

                ASN1EncodableVector vector = new ASN1EncodableVector();
                for (P11SlotIdentifier slotId : slotIds) {
                    vector.add(new SlotIdentifier(slotId));
                }
                respItvInfoValue = new DERSequence(vector);
                break;
            }
            case XipkiCmpConstants.ACTION_RP11_LIST_KEYLABELS: {
                SlotIdentifier slotId = SlotIdentifier.getInstance(reqValue);
                String[] keyLabels = p11CryptService.getKeyLabels(slotId.getSlotId());

                ASN1EncodableVector vector = new ASN1EncodableVector();
                for (String keyLabel : keyLabels) {
                    vector.add(new DERUTF8String(keyLabel));
                }
                respItvInfoValue = new DERSequence(vector);
                break;
            }
            default: {
                final String statusMessage = "unsupported XiPKI action code '" + action + "'";
                return createRejectionPKIMessage(respHeader, PKIFailureInfo.badRequest, statusMessage);
            }
            } // end switch(code)

            ASN1EncodableVector v = new ASN1EncodableVector();
            v.add(new ASN1Integer(action));
            if (respItvInfoValue != null) {
                v.add(respItvInfoValue);
            }
            InfoTypeAndValue respItv = new InfoTypeAndValue(ObjectIdentifiers.id_xipki_cmp, new DERSequence(v));
            GenRepContent genRepContent = new GenRepContent(respItv);
            PKIBody respBody = new PKIBody(PKIBody.TYPE_GEN_REP, genRepContent);
            return new PKIMessage(respHeader, respBody);
        } catch (Throwable t) {
            LOG.error("error while processing CMP message {}, message: {}", tidStr, t.getMessage());
            LOG.debug("error while processing CMP message " + tidStr, t);
            return createRejectionPKIMessage(respHeader, PKIFailureInfo.systemFailure, t.getMessage());
        }
    }

    private PKIMessage createRejectionPKIMessage(final PKIHeader header, final int pkiFailureInfo,
            final String statusMessage) {
        ErrorMsgContent emc = new ErrorMsgContent(new PKIStatusInfo(PKIStatus.rejection,
                new PKIFreeText(statusMessage), new PKIFailureInfo(pkiFailureInfo)));
        PKIBody respBody = new PKIBody(PKIBody.TYPE_ERROR, emc);
        return new PKIMessage(header, respBody);
    }

    private byte[] randomTransactionId() {
        byte[] b = new byte[10];
        synchronized (random) {
            random.nextBytes(b);
        }
        return b;
    }
}