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