org.xipki.pki.ocsp.client.shell.BaseOcspStatusCommandSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.xipki.pki.ocsp.client.shell.BaseOcspStatusCommandSupport.java

Source

/*
 *
 * Copyright (c) 2013 - 2016 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.pki.ocsp.client.shell;

import java.math.BigInteger;
import java.net.URL;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

import org.apache.karaf.shell.api.action.Completion;
import org.apache.karaf.shell.api.action.Option;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.AttributeCertificateIssuer;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.xipki.commons.common.RequestResponseDebug;
import org.xipki.commons.common.RequestResponsePair;
import org.xipki.commons.common.util.BigIntegerRange;
import org.xipki.commons.common.util.CollectionUtil;
import org.xipki.commons.common.util.IoUtil;
import org.xipki.commons.common.util.StringUtil;
import org.xipki.commons.console.karaf.IllegalCmdParamException;
import org.xipki.commons.console.karaf.completer.FilePathCompleter;
import org.xipki.commons.security.HashAlgoType;
import org.xipki.commons.security.IssuerHash;
import org.xipki.commons.security.ObjectIdentifiers;
import org.xipki.commons.security.util.X509Util;
import org.xipki.pki.ocsp.client.api.RequestOptions;

/**
 * @author Lijun Liao
 * @since 2.0.0
 */

public abstract class BaseOcspStatusCommandSupport extends OcspStatusCommandSupport {

    protected static final Map<ASN1ObjectIdentifier, String> EXTENSION_OIDNAME_MAP = new HashMap<>();

    @Option(name = "--verbose", aliases = "-v", description = "show status verbosely")
    protected Boolean verbose = Boolean.FALSE;

    @Option(name = "--resp-issuer", description = "certificate file of the responder's issuer")
    @Completion(FilePathCompleter.class)
    private String respIssuerFile;

    @Option(name = "--url", description = "OCSP responder URL")
    private String serverUrl;

    @Option(name = "--req-out", description = "where to save the request")
    @Completion(FilePathCompleter.class)
    private String reqout;

    @Option(name = "--resp-out", description = "where to save the response")
    @Completion(FilePathCompleter.class)
    private String respout;

    @Option(name = "--hex", description = "serial number without prefix is hex number")
    private Boolean hex = Boolean.FALSE;

    @Option(name = "--serial", aliases = "-s", description = "comma-separated serial numbers or ranges (like 1,3,6-10)\n"
            + "(at least one of serial and cert must be specified)")
    private String serialNumberList;

    @Option(name = "--cert", aliases = "-c", multiValued = true, description = "certificate\n" + "(multi-valued)")
    @Completion(FilePathCompleter.class)
    private List<String> certFiles;

    @Option(name = "--ac", description = "the certificates are attribute certificates")
    @Completion(FilePathCompleter.class)
    private Boolean isAttrCert = Boolean.FALSE;

    static {
        EXTENSION_OIDNAME_MAP.put(OCSPObjectIdentifiers.id_pkix_ocsp_archive_cutoff, "ArchiveCutoff");
        EXTENSION_OIDNAME_MAP.put(OCSPObjectIdentifiers.id_pkix_ocsp_crl, "CrlID");
        EXTENSION_OIDNAME_MAP.put(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, "Nonce");
        EXTENSION_OIDNAME_MAP.put(ObjectIdentifiers.id_pkix_ocsp_extendedRevoke, "ExtendedRevoke");
    }

    protected abstract void checkParameters(@Nullable X509Certificate respIssuer,
            @NonNull List<BigInteger> serialNumbers, @Nullable Map<BigInteger, byte[]> encodedCerts)
            throws Exception;

    protected abstract Object processResponse(@NonNull OCSPResp response, @Nullable X509Certificate respIssuer,
            @NonNull IssuerHash issuerHash, @NonNull List<BigInteger> serialNumbers,
            @Nullable Map<BigInteger, byte[]> encodedCerts) throws Exception;

    @Override
    protected final Object doExecute() throws Exception {
        if (StringUtil.isBlank(serialNumberList) && isEmpty(certFiles)) {
            throw new IllegalCmdParamException("Neither serialNumbers nor certFiles is set");
        }

        X509Certificate issuerCert = X509Util.parseCert(issuerCertFile);

        Map<BigInteger, byte[]> encodedCerts = null;
        List<BigInteger> sns = new LinkedList<>();

        if (isNotEmpty(certFiles)) {
            encodedCerts = new HashMap<>(certFiles.size());

            String ocspUrl = null;

            X500Name issuerX500Name = null;
            if (isAttrCert) {
                issuerX500Name = X500Name.getInstance(issuerCert.getSubjectX500Principal().getEncoded());
            }

            for (String certFile : certFiles) {
                BigInteger sn;
                List<String> ocspUrls;

                if (isAttrCert) {
                    X509AttributeCertificateHolder cert = new X509AttributeCertificateHolder(IoUtil.read(certFile));
                    // no signature validation
                    AttributeCertificateIssuer reqIssuer = cert.getIssuer();
                    if (reqIssuer != null && issuerX500Name != null) {
                        X500Name reqIssuerName = reqIssuer.getNames()[0];
                        if (!issuerX500Name.equals(reqIssuerName)) {
                            throw new IllegalCmdParamException(
                                    "certificate " + certFile + " is not issued by the given issuer");
                        }
                    }

                    ocspUrls = extractOcspUrls(cert);
                    sn = cert.getSerialNumber();
                } else {
                    X509Certificate cert = X509Util.parseCert(certFile);
                    if (!X509Util.issues(issuerCert, cert)) {
                        throw new IllegalCmdParamException(
                                "certificate " + certFile + " is not issued by the given issuer");
                    }
                    ocspUrls = extractOcspUrls(cert);
                    sn = cert.getSerialNumber();
                }

                if (isBlank(serverUrl)) {
                    if (CollectionUtil.isEmpty(ocspUrls)) {
                        throw new IllegalCmdParamException("could not extract OCSP responder URL");
                    } else {
                        String url = ocspUrls.get(0);
                        if (ocspUrl != null && !ocspUrl.equals(url)) {
                            throw new IllegalCmdParamException(
                                    "given certificates have different" + " OCSP responder URL in certificate");
                        } else {
                            ocspUrl = url;
                        }
                    }
                } // end if

                sns.add(sn);

                byte[] encodedCert = IoUtil.read(certFile);
                encodedCerts.put(sn, encodedCert);
            } // end for

            if (isBlank(serverUrl)) {
                serverUrl = ocspUrl;
            }
        } else {
            StringTokenizer st = new StringTokenizer(serialNumberList, ", ");
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                StringTokenizer st2 = new StringTokenizer(token, "-");
                BigInteger from = toBigInt(st2.nextToken(), hex);
                BigInteger to = st2.hasMoreTokens() ? toBigInt(st2.nextToken(), hex) : null;
                if (to == null) {
                    sns.add(from);
                } else {
                    BigIntegerRange range = new BigIntegerRange(from, to);
                    if (range.getDiff().compareTo(BigInteger.valueOf(10)) > 0) {
                        throw new IllegalCmdParamException("to many serial numbers");
                    }

                    BigInteger sn = range.getFrom();
                    while (range.isInRange(sn)) {
                        sns.add(sn);
                        sn = sn.add(BigInteger.ONE);
                    }
                }
            }
        }

        if (isBlank(serverUrl)) {
            throw new IllegalCmdParamException("could not get URL for the OCSP responder");
        }

        X509Certificate respIssuer = null;
        if (respIssuerFile != null) {
            respIssuer = X509Util.parseCert(IoUtil.expandFilepath(respIssuerFile));
        }

        URL serverUrlObj = new URL(serverUrl);
        RequestOptions options = getRequestOptions();
        checkParameters(respIssuer, sns, encodedCerts);
        boolean saveReq = isNotBlank(reqout);
        boolean saveResp = isNotBlank(respout);
        RequestResponseDebug debug = null;
        if (saveReq || saveResp) {
            debug = new RequestResponseDebug();
        }

        IssuerHash issuerHash = new IssuerHash(HashAlgoType.getNonNullHashAlgoType(options.getHashAlgorithmId()),
                Certificate.getInstance(issuerCert.getEncoded()));
        OCSPResp response;
        try {
            response = requestor.ask(issuerCert, sns.toArray(new BigInteger[0]), serverUrlObj, options, debug);
        } finally {
            if (debug != null && debug.size() > 0) {
                RequestResponsePair reqResp = debug.get(0);
                if (saveReq) {
                    byte[] bytes = reqResp.getRequest();
                    if (bytes != null) {
                        IoUtil.save(reqout, bytes);
                    }
                }

                if (saveResp) {
                    byte[] bytes = reqResp.getResponse();
                    if (bytes != null) {
                        IoUtil.save(respout, bytes);
                    }
                }
            } // end if
        } // end finally

        return processResponse(response, respIssuer, issuerHash, sns, encodedCerts);
    } // method doExecute

    public static List<String> extractOcspUrls(final X509Certificate cert) throws CertificateEncodingException {
        byte[] extValue = X509Util.getCoreExtValue(cert, Extension.authorityInfoAccess);
        if (extValue == null) {
            return Collections.emptyList();
        }

        AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(extValue);
        return extractOcspUrls(aia);
    }

    public static List<String> extractOcspUrls(final X509AttributeCertificateHolder cert)
            throws CertificateEncodingException {
        byte[] extValue = X509Util.getCoreExtValue(cert, Extension.authorityInfoAccess);
        if (extValue == null) {
            return Collections.emptyList();
        }
        AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(extValue);
        return extractOcspUrls(aia);
    }

    public static List<String> extractOcspUrls(final AuthorityInformationAccess aia)
            throws CertificateEncodingException {
        AccessDescription[] accessDescriptions = aia.getAccessDescriptions();
        List<AccessDescription> ocspAccessDescriptions = new LinkedList<>();
        for (AccessDescription accessDescription : accessDescriptions) {
            if (accessDescription.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_ocsp)) {
                ocspAccessDescriptions.add(accessDescription);
            }
        }

        final int n = ocspAccessDescriptions.size();
        List<String> ocspUris = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            GeneralName accessLocation = ocspAccessDescriptions.get(i).getAccessLocation();
            if (accessLocation.getTagNo() == GeneralName.uniformResourceIdentifier) {
                String ocspUri = ((ASN1String) accessLocation.getName()).getString();
                ocspUris.add(ocspUri);
            }
        }

        return ocspUris;
    }

}