Use OCSP protocol to send request to verify certificate status, OCSP verifies Apple P12 certificate status (valid | revoked)

In view of the company's need for Java server to verify the certificate, however, there are too few cases in Java implementation, searching hard for 2 days, Baidu, Google, OCSP protocol documents, finally achieved, can use this Demo to verify any OCSP protocol certificate!!!

Find a lot, and finally use bouncy castle to complete OCSP verification

Packages that Maven needs to introduce:

 <dependencies>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcmail-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bctls-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-ext-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpg-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>
        <!-- Commons libs -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.9</version>
        </dependency>
        <dependency>
            <groupId>commons-digester</groupId>
            <artifactId>commons-digester</artifactId>
            <version>2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
            <groupId>commons-cli</groupId>
            <artifactId>commons-cli</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>

Package of common java project: bouncy castle + commons related package

Here is the tool code:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.CertificateStatus;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPReq;
import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cert.ocsp.RevokedStatus;
import org.bouncycastle.cert.ocsp.SingleResp;
import org.bouncycastle.cert.ocsp.UnknownStatus;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;

import sun.misc.BASE64Decoder;

import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;

/**
 * Request to verify certificate status using OCSP protocol (valid | revoke | unknown)
 * @author i4026
 *
 */
public class OCSPTool {

	//Apple root certificate object
    private static X509Certificate issuerCert;

    /**
     * Initial instantiation of Apple root certificate object (publisher certificate)
     * @return
     */
    private static X509Certificate getIssuerCert() {
        if(issuerCert == null) {
            //Apple root certificate
            String issuerCertStr = "MIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UE\n" +
                    "BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp\n" +
                    "ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEz\n" +
                    "MDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVTMRMwEQYD\n" +
                    "VQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv\n" +
                    "cGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3Bl\n" +
                    "ciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3\n" +
                    "DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0U3rOfGOA\n" +
                    "YXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkVCBmsqtsq\n" +
                    "Mu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8V25nNYB2\n" +
                    "NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHld0WNUEi6\n" +
                    "Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1qarunFjVg\n" +
                    "0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGjgaYwgaMw\n" +
                    "HQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQFMAMBAf8w\n" +
                    "HwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcwJTAjoCGg\n" +
                    "H4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgGG\n" +
                    "MBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Zviz1smwv\n" +
                    "j+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/Nw0Uwj6OD\n" +
                    "Dc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJTleMa1s8\n" +
                    "Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1VAKmuu0sw\n" +
                    "ruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur+cmV6U/k\n" +
                    "TecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxRpVzscYqC\n" +
                    "tGwPDBUf";
            try {
                byte[] issuerByte = new BASE64Decoder().decodeBuffer(issuerCertStr);
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                issuerCert = (X509Certificate) cf.generateCertificate(new ByteInputStream(issuerByte, issuerByte.length));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return issuerCert;
    }


    /*
     *  Verify certificate status with ocsp
     * 0 Valid certificate
     * 1 Unknown certificate
     * -1 Revocation certificate
     * retry retry count
     */
    public static Integer checkCertStatus(String certStr, String pwd, int retry) {
        int resultNum = -2;
        while (retry > 0) {
            try {
                byte[] p12Byte = new BASE64Decoder().decodeBuffer(certStr);
                KeyStore ks = KeyStore.getInstance("PKCS12");
                ks.load(new ByteInputStream(p12Byte, p12Byte.length), pwd.toCharArray());
                X509Certificate cert = (X509Certificate)ks.getCertificate("1");
                OCSPReq ocspReq = GenOcspReq(cert, getIssuerCert());
                OCSPResp ocspResp = requestOCSPResponse(getOCSPUrl(cert), ocspReq);
                if(OCSPResp.SUCCESSFUL == ocspResp.getStatus()) {
                    BasicOCSPResp basic = (BasicOCSPResp) ocspResp.getResponseObject();
                    SingleResp[] resps = basic.getResponses();
                    if (resps != null && resps.length == 1) {
                        SingleResp resp = resps[0];
                        CertificateStatus certStatus = resp.getCertStatus();
                        if (certStatus == CertificateStatus.GOOD) {
                            resultNum = 0;
                        } else {
                            if (certStatus instanceof RevokedStatus) {
                                resultNum = -1;
                            } else if (certStatus instanceof UnknownStatus) {
                                resultNum = 1;
                            }
                        }
                        retry = 0;
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                retry --;
            }
        }

        return resultNum;
    }

    /**
     * Create OCSP requestq request
     * @param nextCert  Certificate to be inspected
     * @param nextIssuer   Issuer Certificate (Apple root certificate)
     * @return
     * @throws OCSPException
     * @throws OperatorCreationException
     * @throws CertificateEncodingException
     * @throws IOException
     */
    public static OCSPReq GenOcspReq(X509Certificate nextCert,
                                     X509Certificate nextIssuer) throws OCSPException, OperatorCreationException, CertificateEncodingException, IOException {
        OCSPReqBuilder ocspRequestGenerator = new OCSPReqBuilder();
        DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
        // Get certId
        CertificateID certId = new CertificateID(
                (new BcDigestCalculatorProvider())
                        .get(CertificateID.HASH_SHA1),
                new X509CertificateHolder(nextIssuer.getEncoded()),
                nextCert.getSerialNumber());
        ocspRequestGenerator.addRequest(certId);
        BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
        Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(nonce.toByteArray()));
        ocspRequestGenerator.setRequestExtensions(new Extensions(new Extension[]{ext}));
        return ocspRequestGenerator.build();
    }


    /**
     * Send request and receive return value
     * @param url       Request address, which can be obtained from the certificate
     * @param ocspReq   Request object
     * @return
     * @throws IOException
     * @throws MalformedURLException
     */
    public static OCSPResp requestOCSPResponse(String url, OCSPReq ocspReq) throws IOException, MalformedURLException {
        byte[] ocspReqData = ocspReq.getEncoded();
        HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
        try {
            con.setRequestProperty("Content-Type", "application/ocsp-request");
            con.setRequestProperty("Accept", "application/ocsp-response");
            con.setDoInput(true);
            con.setDoOutput(true);
            con.setUseCaches(false);

            OutputStream out = con.getOutputStream();
            try {
                IOUtils.write(ocspReqData, out);
                out.flush();
            } finally {
                IOUtils.closeQuietly(out);
            }

            byte[] responseBytes = IOUtils.toByteArray(con.getInputStream());
            OCSPResp ocspResp = new OCSPResp(responseBytes);

            return ocspResp;
        } finally {
            if (con != null) {
                con.disconnect();
            }
        }
    }


    /**
     * Get the request address of OSCP in the certificate
     * @param certificate
     * @return
     * @throws IOException
     */
    public static String getOCSPUrl(X509Certificate certificate) throws IOException {
        ASN1Primitive obj;
        try {
            obj = getExtensionValue(certificate, Extension.authorityInfoAccess.getId());
        } catch (IOException ex) {
            return null;
        }

        if (obj == null) {
            return null;
        }

        AuthorityInformationAccess authorityInformationAccess = AuthorityInformationAccess.getInstance(obj);

        AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions();
        for (AccessDescription accessDescription : accessDescriptions) {
            boolean correctAccessMethod = accessDescription.getAccessMethod().equals(X509ObjectIdentifiers.ocspAccessMethod);
            if (!correctAccessMethod) {
                continue;
            }

            GeneralName name = accessDescription.getAccessLocation();
            if (name.getTagNo() != GeneralName.uniformResourceIdentifier) {
                continue;
            }

            DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject) name.toASN1Primitive(), false);
            return derStr.getString();
        }

        return null;
    }

    /**
     * @param certificate
     *            the certificate from which we need the ExtensionValue
     * @param oid
     *            the Object Identifier value for the extension.
     * @return the extension value as an ASN1Primitive object
     * @throws IOException
     */
    private static ASN1Primitive getExtensionValue(X509Certificate certificate, String oid) throws IOException {
        byte[] bytes = certificate.getExtensionValue(oid);
        if (bytes == null) {
            return null;
        }
        ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(bytes));
        ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
        aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets()));
        return aIn.readObject();
    }

    
    /**
     * Test Demo
     * @param args
     */
    public static void main(String[] args) {
      String p12 = "MIIL1QIBAzCCC58GCSqGSIb3DQEHA----Certificate to be verified----";
      String pdw = "Certificate password";
      int checkStatus = checkCertStatus(p12, pdw, 3);
      String data = "Unknown";
      if (checkStatus == 0) {
	      data = "Certificate valid";
	  } else if (checkStatus == 1) {
	      data = "Certificate unknown";
	  } else if (checkStatus == -1) {
	      data = "Certificate revocation";
	  } else if (checkStatus == -2) {
	      data = "Verify exception";
	  }
      System.out.println(data);
    	
    }
}

Operation result:

 

This code has not been optimized. It's the first Demo version, but it can be checked normally. The reason for posting this blog is that there are too few cases for java to do this. I don't want other developers to waste as much time as I do (except Daniel)!

What's wrong? You can point it out

Published 6 original articles, won praise 3, visited 5309
Private letter follow

Keywords: Java codec Apache Google

Added by pietbez on Sat, 18 Jan 2020 20:22:56 +0200