数字证书的相关专业名词(下)—OCSP及其java中的应用

数字证书的相关专业名词(下)—OCSP及其java中的应用本文详细介绍了 OCSP 协议的概念 优势及在 Java 中获取 OCSP 地址的步骤 包括解析证书扩展信息 获取 OCSPURL

大家好,欢迎来到IT知识分享网。

一、前言

上篇文章我们了解了根证书和校验证书有效性中的一个比较重要的渠道–CRL,但是CRL有着时间延迟,网络带宽消耗等缺点,本篇文章我们了解另一种更高效也是目前被广泛应用的校验证书有效性的另一种方式–OCSP,并且我会结合java来聊聊如何获取OCSP地址以及如何去通过获取的OCSP url去获取ocsp结果

二、OCSP

2.1、OCSP概念

  • OCSP(Online Certificate Status Protocol)是一种用于验证数字证书有效性的协议。它允许一个客户端向证书颁发机构(CA)的OCSP服务器查询某个特定证书是否被撤销或者是否仍然有效。与传统的证书撤销列表(CRL)相比,OCSP 具有更快的响应时间和更精确的证书状态信息。OCSP协议的维护者通常是负责数字证书签发和管理的组织或个人。例如CA机构等
  • 具体来说,当一个客户端需要验证某个证书的有效性时,它会向OCSP服务器发送一个查询请求,询问该证书的状态。OCSP服务器会返回一个响应,其中包含有关该证书的信息,例如证书是否被撤销,以及该证书是否还有效。

OCSP 协议的工作方式如下:

  1. 客户端向 OCSP 服务器发送一个查询请求,其中包含要验证的证书的序列号。
  2. OCSP 服务器检查该证书是否被撤销或者是否仍然有效,并将其状态返回给客户端。
  3. 客户端收到 OCSP 服务器的响应,根据响应中的信息来确定证书的有效性。

相比于传统的证书撤销列表(CRL),OCSP 的优势在于它能够提供更快的响应时间和更精确的证书状态信息。因为 CRL 需要定期更新,并且可能很大,所以使用 OCSP 可以避免这些问题。

值得一提的是,OCSP 也有一些缺点,如可能存在安全和隐私方面的问题,因为使用 OCSP 需要向 CA 公开某个特定证书的信息。此外,如果 OCSP 服务器无法响应,则客户端可能无法验证证书的有效性。

2.2、OCSP地址在java中的获取

在java中,我们可以通过证书的X509形式类X509Certificate去获取CRL地址,步骤如下,前两步大家可以发现其实和获取CRL地址代码差不多

  1. 获取证书中的扩展信息:
X509Certificate cert = ... // 从某处获取证书对象 byte[] crlDistributionPointsExtension = cert.getExtensionValue("1.3.6.1.5.5.7.1.1"); 

其中1.3.6.1.5.5.7.1.1是X.509标准中定义的证书扩展之一,也称为authorityInfoAccess扩展。该扩展用于指定证书颁发者(CA)的证书撤销列表(CRL)位置和/或在线证书状态协议(OCSP)验证器地址等信息。这个扩展字段允许使用者在验证证书时获取到关于该证书颁发者的更多信息,从而增加了证书验证的安全性。,以便验证人员可以在验证证书时检查该证书是否已被撤销。java中也可以通过以下方法获取,同样也是1.3.6.1.5.5.7.1.1.所以大家感兴趣的话可以用这个id去试一下获取CRL地址,同样也是可以获取crl地址的,但是如果通过

Extension.authorityInfoAccess.getId() 
  1. 解码扩展信息,首先创建一个新的ASN1InputStream对象,用于读取传递的字节数组参数crlDistributionPointsExtension。 然后,将扩展值包装在一个ASN1OctetString对象中,这可以通过在ASN1InputStream对象上调用readObject()来获取。接下来,使用ASN1OctetString对象的八位字节创建一个新的ASN1InputStream对象,并再次调用readObject()以获取表示CRL Distribution Points扩展的ASN1Primitive对象。
 ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(crlDistributionPointsExtension)); ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); ASN1Primitive asn1Primitive = aIn.readObject(); 
  1. 用之前获取的ASN1Primitive对象转化成ASN1Sequence,该对象包含多个AccessDescription,遍历每个AccessDescription,判断其是否为OCSP协议类型,并获取对应的AccessLocation字符串。在获取AccessLocation时,会跳过ldap协议地址。如果没有找到符合条件的AccessDescription,则返回null。

这里介绍一下什么是AccessDescription

代码如下

 ASN1Sequence AccessDescriptions = (ASN1Sequence) obj; for (int i = 0; i < AccessDescriptions.size(); i++) { 
    ASN1Sequence AccessDescription = (ASN1Sequence) AccessDescriptions.getObjectAt(i); if ( AccessDescription.size() != 2 ) { 
    continue; } else if (AccessDescription.getObjectAt(0) instanceof ASN1ObjectIdentifier) { 
    //获取accessMethod ASN1ObjectIdentifier id = (ASN1ObjectIdentifier)AccessDescription.getObjectAt(0); if (SecurityIDs.ID_OCSP.equals(id.getId())) { 
    //如果是OCSP类型,则获取accessLocation ASN1Primitive description = (ASN1Primitive)AccessDescription.getObjectAt(1); String AccessLocation = getStringFromGeneralName(description); // 区别于itext源码,不获取ldap协议地址 if (AccessLocation.startsWith("ldap")) { 
    continue; } if (AccessLocation == null) { 
    return "" ; } else { 
    return AccessLocation ; } } } } 

完整测试代码如下:

public static void main(String[] args) throws CertificateException, IOException, CRLException { 
    String rootCert = "MIIFuDCCBKCgAwIBAgIQche7n/HhSQ+guIZEOjNvGzANBgkqhkiG9w0BAQsFADAzMQswCQYDVGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFNIRUNBIEcyMB4XDTIzMDIwNTE3MDQ0MVoXDTIzMDUwNjE1NTk1OVowTzELMAkGA1UEBhMCQ04xGzAZBgNVBAoMEueUteWtkOetvueroOWJjeWPsDEjMCEGA1UEAwwa5rWL6K+VMDEwMTExMUA4NCoqKioqKioqMjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCHJ8088uY9p4XD77TrLu3h0tB5O4Xp32hW4zNttjHmhoXai7wngFooNXDzGwj9JnM99VE+qjQzkr8Th9pyIYeTk5eLTMmmMEo/p42uiHyGbTtn8B2g3wmPTApu2S4+MAkNbvPD5VDEfMb+1e/7oN39NTZv1mBoENpst6nwEdgQ7jla4ueOcKOWWmFT+3lGzx3tWm8lkqSnryaSjNtMynMK25PxiXLFV8dQ4Wk7DigQpveP+GH8ltTHoZqpZMcXy5qgUWeZSfNYQ1i3JajjKBdSzflrKBi3HeeQBzPSk6M12B6EE5usl1zCABXl6o29IREEy9d/zmmScyLtff+oYw19AgMBAAGjggKqMIICpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwfQYIKwYBBQUHAQEEcTBvMDgGCCsGAQUFBzABhixodHRwOi8vb2NzcDMuc2hlY2EuY29tL29jc3Avc2hlY2Evc2hlY2Eub2NzcDAzBggrBgEFBQcwAoYnaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9yb290L3NoZWNhZzIuZGVyMB8GA1UdIwQYMBaAFFaI3uMYQ4K3cqQm60SpYtCHxKwmMB0GA1UdDgQWBBS8nQ//lcMctfZGZ1AnPCwwRPZM6TALBgNVHQ8EBAMCBsAwgYYGBiqBHAHFOAR8MHowSQYIKoEcAcU4gRAEPWxkYXA6Ly9sZGFwMi5zaGVjYS5jb20vb3U9c2hlY2EgY2VydGlmaWNhdGUgY2hhaW4sbz1zaGVjYS5jb20wEQYIKoEcAcU4gRMEBTY2MDU1MBoGCCqBHAHFOIEUBA5RVDg0OTAyMzc0MDkyMzAJBgNVHRMEAjAAMEIGA1UdIAQ7MDkwNwYJKoEcAYbvOoEVMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly93d3cuc2hlY2EuY29tL3BvbGljeS8wgeAGA1UdHwSB2DCB1TA3oDWgM4YxaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9DQTIwMDExL1JBOTAzMS9DUkw1MTkxLmNybDCBmaCBlqCBk4aBkGxkYXA6Ly9sZGFwMi5zaGVjYS5jb206Mzg5L2NuPUNSTDUxOTEuY3JsLG91PVJBOTAzMSxvdT1DQTIwMDExLG91PWNybCxvPVVuaVRydXN0P2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludDANBgkqhkiG9w0BAQsFAAOCAQEAjQRnIYRE9SH4+leOjO9oUt++qhfefVzaZXdGQgxiUzIXv14vo9mls0COjz0YXoruEe6olh6X6rrdmaKrYw0iq2CJ3D1GkrFCutjX2P3r97Irale8w5J8hJ6dybd/rFZFZuTfYm7yWJLEcF+pAZXedGObwe4fOjS0J/A6KXqGsrdB/fJwvfHH5UIIWW3OihTr1TLEEuun/3oDbGdBDTud2+6tbiEN9daFV92TSko2DRQ/CisJoq5SCmI/dYZlAqeyr4jlLWHFfVUpHKuvpn/lHtYCU0FsGyu9ixs4/YdBw/QIDj5oES9yf/FFDzfTnS8twa8rRJKWdUKKa9sYEeJtVQ=="; CertificateFactory cf = CertificateFactory.getInstance("X.509", new BouncyCastleProvider()); X509Certificate certificate = (X509Certificate) cf .generateCertificate(new ByteArrayInputStream(Base64Utils.decode(rootCert))); byte[] crlDistributionPointsExtension = certificate.getExtensionValue("1.3.6.1.5.5.7.1.1"); ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(crlDistributionPointsExtension)); ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); ASN1Primitive asn1Primitive = aIn.readObject(); ASN1Sequence AccessDescriptions = (ASN1Sequence) asn1Primitive; for (int i = 0; i < AccessDescriptions.size(); i++) { 
    ASN1Sequence AccessDescription = (ASN1Sequence) AccessDescriptions.getObjectAt(i); if (AccessDescription.size() != 2) { 
    continue; } else if (AccessDescription.getObjectAt(0) instanceof ASN1ObjectIdentifier) { 
    ASN1ObjectIdentifier id = (ASN1ObjectIdentifier) AccessDescription.getObjectAt(0); if (SecurityIDs.ID_OCSP.equals(id.getId())) { 
    ASN1Primitive description = (ASN1Primitive) AccessDescription.getObjectAt(1); String AccessLocation = getStringFromGeneralName(description); // 区别于itext源码,不获取ldap协议地址 if (AccessLocation.startsWith("ldap")) { 
    continue; } if (AccessLocation == null) { 
    System.out.println(""); } else { 
    System.out.println(AccessLocation); } } } } } private static String getStringFromGeneralName(ASN1Primitive names) throws IOException { 
    ASN1TaggedObject taggedObject = (ASN1TaggedObject) names ; return new String(ASN1OctetString.getInstance(taggedObject, false).getOctets(), "ISO-8859-1"); } 

2.3、通过ocsp url去获取ocsp结果响应

通过上面的学习我们已经可以成功获取ocsp url了,那么接下来就来看看如何通过ocsp url获取ocsp响应结果,步骤如下

1. 生成ocsp请求OCSPReq

 // Generate the id for the certificate we are looking for CertificateID id = new CertificateID(new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1), new JcaX509CertificateHolder(issuerCert), serialNumber); 

在创建CertificateID时,首先获取一个用于计算SHA-1 CertHash的JcaDigestCalculator对象。在这里,先通过调用JcaDigestCalculatorProviderBuilderbuild()方法获取一个用于计算摘要的DigestCalculatorProvider实例,然后再调用该实例的get()方法并传入CertificateID.HASH_SHA1常量来获取SHA-1算法的JcaDigestCalculator实例。
接着使用new JcaX509CertificateHolder(issuerCert)将颁发者证书转换为Bouncy Castle库中的X509CertificateHolder对象。

X509CertificateHolder是Bouncy Castle库中用于表示X.509证书的一个重要类。它提供了许多有用的方法来获取证书的各种属性,例如:subject、issuer、Serial Number等等。通过将颁发者证书转换为X509CertificateHolder对象,我们可以方便地从该对象中提取Issuer信息以构建CertificateID。

在这个过程中,JcaX509CertificateHolder类是一种用于创建X509CertificateHolder对象的便捷方式,它实现了X509CertificateHolder接口并封装了一个X.509证书。当我们调用new JcaX509CertificateHolder(issuerCert)时,会创建一个新的JcaX509CertificateHolder对象,并使用issuerCert初始化该对象。最终返回的X509CertificateHolder对象包含有关颁发者证书的信息,可以用于创建CertificateID。

 // basic request generation with nonce OCSPReqBuilder gen = new OCSPReqBuilder(); gen.addRequest(id); 

③添加Nonce扩展
为了防止重放攻击,可以在OCSP请求中添加一个随机数Nonce。这里使用BouncyCastle库提供的id_pkix_ocsp_nonce扩展来实现,将随机数作为DER编码的OctetString类型数据加入到Nonce扩展中。这个随机数我们通过PdfEncryption.createDocumentId()来获取

Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(new DEROctetString(PdfEncryption.createDocumentId()).getEncoded())); gen.setRequestExtensions(new Extensions(new Extension[] { 
    ext })); 

最后,调用build()方法获取OCSPReq对象,该对象表示一个完整的OCSP请求信息。完整代码如下:

private static OCSPReq generateOCSPRequest(X509Certificate issuerCert, BigInteger serialNumber) throws OCSPException, IOException, OperatorException, CertificateEncodingException { 
    // Add provider BC Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // Generate the id for the certificate we are looking for CertificateID id = new CertificateID(new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1), new JcaX509CertificateHolder(issuerCert), serialNumber); // basic request generation with nonce OCSPReqBuilder gen = new OCSPReqBuilder(); gen.addRequest(id); Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(new DEROctetString(PdfEncryption.createDocumentId()).getEncoded())); gen.setRequestExtensions(new Extensions(new Extension[] { 
    ext })); return gen.build(); } 

2. 创建并配置一个HTTP连接,并且配置连接,用于向指定的URL发送OCSP请求消息,并等待响应消息。

byte[] array = request.getEncoded(); URL urlt = new URL(url); HttpURLConnection con = (HttpURLConnection) urlt.openConnection(); con.setRequestProperty("Content-Type", "application/ocsp-request"); //设置HTTP请求头属性,表示请求消息体的格式是OCSP请求。 con.setRequestProperty("Accept", "application/ocsp-response"); //设置HTTP请求头属性,表示接受响应消息体的格式是OCSP响应。 con.setDoOutput(true); //设置HTTP连接可以输出数据。 con.setConnectTimeout(3000); con.setReadTimeout(5000); 

3. 向已经建立的HTTP连接发送数据,并获取响应状态码(response code)和响应结果

 OutputStream out = con.getOutputStream(); //获取输出流,用于向服务器发送请求数据。 DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out)); //创建一个数据输出流对象,用于将字节数组写入到输出流中。 dataOut.write(array); //将OCSP请求消息体数组(array)中的数据写入到数据输出流中。 dataOut.flush(); //刷新数据输出流,将缓冲区中的数据推送到网络中。 dataOut.close(); //关闭数据输出流。 if (con.getResponseCode() / 100 != 2) { 
    throw new IOException(MessageLocalization.getComposedMessage("invalid.http.response.1", con.getResponseCode())); } // Get Response InputStream in = (InputStream) con.getContent(); return new OCSPResp(StreamUtil.inputStreamToArray(in)); 

完整获取回复OCSP回复代码如下:

 private static OCSPReq generateOCSPRequest(X509Certificate issuerCert, BigInteger serialNumber) throws OCSPException, IOException, OperatorException, CertificateEncodingException { 
    // Add provider BC Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // Generate the id for the certificate we are looking for CertificateID id = new CertificateID(new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1), new JcaX509CertificateHolder(issuerCert), serialNumber); // basic request generation with nonce OCSPReqBuilder gen = new OCSPReqBuilder(); gen.addRequest(id); Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(new DEROctetString(PdfEncryption.createDocumentId()).getEncoded())); gen.setRequestExtensions(new Extensions(new Extension[] { 
    ext })); return gen.build(); } public static OCSPResp getOcspResponse(X509Certificate checkCert, X509Certificate rootCert1, String url) throws GeneralSecurityException, OCSPException, IOException, OperatorException { 
    if (checkCert == null || rootCert1 == null) { 
    return null; } if (url == null) { 
    return null; } OCSPReq request = generateOCSPRequest(rootCert1, checkCert.getSerialNumber()); byte[] array = request.getEncoded(); URL urlt = new URL(url); HttpURLConnection con = (HttpURLConnection) urlt.openConnection(); con.setRequestProperty("Content-Type", "application/ocsp-request"); con.setRequestProperty("Accept", "application/ocsp-response"); con.setDoOutput(true); con.setConnectTimeout(3000); con.setReadTimeout(5000); OutputStream out = con.getOutputStream(); DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out)); dataOut.write(array); dataOut.flush(); dataOut.close(); if (con.getResponseCode() / 100 != 2) { 
    throw new IOException(MessageLocalization.getComposedMessage("invalid.http.response.1", con.getResponseCode())); } // Get Response InputStream in = (InputStream) con.getContent(); return new OCSPResp(StreamUtil.inputStreamToArray(in)); } 

4.通过ocspResponse判断证书是否吊销

 //ocsp 校验结果:0(吊销),1(生效),2(ocsp校验异常) OCSPResp ocspResponse = null; try { 
    ocspResponse =getOcspResponse(cert, root, ocspurl); //这个方法就是之前获取ocspResponse的方法 } catch (Exception e) { 
    logger.warn(e.getMessage(), e); return 2; } if (ocspResponse == null) { 
    logger.warn("未获取到ocsp响应"); return 2; } if (ocspResponse.getStatus() != OCSPResp.SUCCESSFUL) { 
    //判断此次连接返回的响应结果 logger.warn("获取ocsp响应未成功"); return 2; } BasicOCSPResp basicResponse = null; //获取到BasicOCSPResp  try { 
    basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject(); } catch (Exception e) { 
    logger.warn(e.getMessage(), e); return 2; } if (basicResponse != null) { 
    SingleResp[] responses = basicResponse.getResponses(); if (responses.length == 1) { 
    SingleResp resp = responses[0]; Object status = resp.getCertStatus(); if (status == CertificateStatus.GOOD) { 
    return 1; } else if (status instanceof RevokedStatus) { 
    return 0; } else { 
    logger.warn("ocsp校验结果:" + "ocsp.status.is.unknown"); return 2; } } } return 2; 

这里可能大家对BasicOCSPResp不太熟悉

然后这里再解释一下这里为什么要对

SingleResp[] responses = basicResponse.getResponses(); if (responses.length == 1) { 
    ..... } 

在OCSP响应消息中,BasicOCSPResp对象可以包含多个单个响应(SingleResponse)对象,每个单个响应对应一个待校验证书的OCSP响应消息。在常规情况下,一个OCSP请求只需要校验一个证书,因此BasicOCSPResp对象中只会包含一个单个响应。

但是,由于网络传输等原因,有时会发生OCSP响应消息体格式错误或者损坏的情况,导致BasicOCSPResp对象中可能包含多个单个响应或者不包含任何单个响应。这种情况在实际工程中出现的概率较低,但仍然有可能发生。

为了避免出现这种情况,代码中进行了如下判断:

SingleResp[] responses = basicResponse.getResponses(); 获取BasicOCSPResp对象中所有的单个响应。

if (responses.length == 1) {...} 判断单个响应数量是否为1,如果不是,则表示OCSP响应消息体格式错误或者损坏,无法进行后续校验操作,因此返回2表示校验异常。

 public static void main(String[] args) throws GeneralSecurityException, IOException, OperatorException, OCSPException { 
    String verifyCert = "MIIFuDCCBKCgAwIBAgIQche7n/HhSQ+guIZEOjNvGzANBgkqhkiG9w0BAQsFADAzMQswCQYDVGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFNIRUNBIEcyMB4XDTIzMDIwNTE3MDQ0MVoXDTIzMDUwNjE1NTk1OVowTzELMAkGA1UEBhMCQ04xGzAZBgNVBAoMEueUteWtkOetvueroOWJjeWPsDEjMCEGA1UEAwwa5rWL6K+VMDEwMTExMUA4NCoqKioqKioqMjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCHJ8088uY9p4XD77TrLu3h0tB5O4Xp32hW4zNttjHmhoXai7wngFooNXDzGwj9JnM99VE+qjQzkr8Th9pyIYeTk5eLTMmmMEo/p42uiHyGbTtn8B2g3wmPTApu2S4+MAkNbvPD5VDEfMb+1e/7oN39NTZv1mBoENpst6nwEdgQ7jla4ueOcKOWWmFT+3lGzx3tWm8lkqSnryaSjNtMynMK25PxiXLFV8dQ4Wk7DigQpveP+GH8ltTHoZqpZMcXy5qgUWeZSfNYQ1i3JajjKBdSzflrKBi3HeeQBzPSk6M12B6EE5usl1zCABXl6o29IREEy9d/zmmScyLtff+oYw19AgMBAAGjggKqMIICpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwfQYIKwYBBQUHAQEEcTBvMDgGCCsGAQUFBzABhixodHRwOi8vb2NzcDMuc2hlY2EuY29tL29jc3Avc2hlY2Evc2hlY2Eub2NzcDAzBggrBgEFBQcwAoYnaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9yb290L3NoZWNhZzIuZGVyMB8GA1UdIwQYMBaAFFaI3uMYQ4K3cqQm60SpYtCHxKwmMB0GA1UdDgQWBBS8nQ//lcMctfZGZ1AnPCwwRPZM6TALBgNVHQ8EBAMCBsAwgYYGBiqBHAHFOAR8MHowSQYIKoEcAcU4gRAEPWxkYXA6Ly9sZGFwMi5zaGVjYS5jb20vb3U9c2hlY2EgY2VydGlmaWNhdGUgY2hhaW4sbz1zaGVjYS5jb20wEQYIKoEcAcU4gRMEBTY2MDU1MBoGCCqBHAHFOIEUBA5RVDg0OTAyMzc0MDkyMzAJBgNVHRMEAjAAMEIGA1UdIAQ7MDkwNwYJKoEcAYbvOoEVMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly93d3cuc2hlY2EuY29tL3BvbGljeS8wgeAGA1UdHwSB2DCB1TA3oDWgM4YxaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9DQTIwMDExL1JBOTAzMS9DUkw1MTkxLmNybDCBmaCBlqCBk4aBkGxkYXA6Ly9sZGFwMi5zaGVjYS5jb206Mzg5L2NuPUNSTDUxOTEuY3JsLG91PVJBOTAzMSxvdT1DQTIwMDExLG91PWNybCxvPVVuaVRydXN0P2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludDANBgkqhkiG9w0BAQsFAAOCAQEAjQRnIYRE9SH4+leOjO9oUt++qhfefVzaZXdGQgxiUzIXv14vo9mls0COjz0YXoruEe6olh6X6rrdmaKrYw0iq2CJ3D1GkrFCutjX2P3r97Irale8w5J8hJ6dybd/rFZFZuTfYm7yWJLEcF+pAZXedGObwe4fOjS0J/A6KXqGsrdB/fJwvfHH5UIIWW3OihTr1TLEEuun/3oDbGdBDTud2+6tbiEN9daFV92TSko2DRQ/CisJoq5SCmI/dYZlAqeyr4jlLWHFfVUpHKuvpn/lHtYCU0FsGyu9ixs4/YdBw/QIDj5oES9yf/FFDzfTnS8twa8rRJKWdUKKa9sYEeJtVQ=="; CertificateFactory cf = CertificateFactory.getInstance("X.509", new BouncyCastleProvider()); X509Certificate certificate = (X509Certificate) cf .generateCertificate(new ByteArrayInputStream(Base64Utils.decode(verifyCert))); String rootCert = "MIIEGDCCAwCgAwIBAgIQag48Y/PJFceE2VmIFXZ9qDANBgkqhkiG9w0BAQsFADAzMQswCQYDVGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFVDQSBSb290MB4XDTE1MDMxMzAwMDAwMFoXDTI5MTIzMTAwMDAwMFowMzELMAkGA1UEBhMCQ04xETAPBgNVBAoMCFVuaVRydXN0MREwDwYDVDDAhTSEVDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN6IIHj7qZIYrz466zxTGCPURSI6GZqbFCTtxBrPnTE5SKtCIyqRNwj/+3A9f/cCyWYHptLeGeY80WORDGuZBBHiVTEsIHSnXZvfqCnQf9KmAisVOOTIGCPWJvCRnfMWLdAcENNaZDIxpkc31ZejALBNPHJDDhxmt6PqyvdX5/cF6gkXO2OOzCa/EF5+x9LwWUKAGR/b+x5j5vt637AQjNmt5Xym63sQdwEaAHqTuPCbcwl+Y1eKXmWuFUXcMk+JdbOhXmjqbOIhup5yrx+hyXc+dtRBJzuSEpvC7WkXLJInR2dqb+Bc2ReJd6zM1deM1MPRmqdJQKEDyT7lEXST53UCAwEAAaOCASYwggEiMEEGA1UdIAQ6MDgwNgYIKoEchu86gRUwKjAoBggrBgEFBQcCARYcaHR0cDovL3d3dy5zaGVjYS5jb20vcG9saWN5LzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBDBggrBgEFBQcBA3MDUwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9sZGFwMi5zaGVjYS5jb20vcm9vdC91Y2Fyb290LmRlcjAdBgNVHQ4EFgQUVoje4xhDgrdypCbrRKli0IfErCYwHwYDVR0jBBgwFoAU2x8182tM/0IxZJvNu1oeHUgQt+4wNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9yb290L3VjYXN1Yi5jcmwwDQYJKoZIhvcNAQELBQADggEBAHj7LkurkcVg8NqoXTg8skl96hhVN06jx2OuiEUFda8NVOfxvaCIc7Ep009/CHZnFUaQO3DYZejpTPAMlsJa18luc12xrOhLZxP4ht2TY+UfcskrjiyrrczJ+95dXT+ChYcGtDGfYXFKDOrgsxekEahSIs+fS/H4LA3Y3z8SeK4tFRigaWZQWV2kW+YNRAtXPoNYUPbPCq3UP4dLtm35DHPvdn/h1iVo5/GU+P02F+SBd6J4AO+wcVw5izs6LRXNRnfgSERM7vP8WLt+lX14umZXJPMPh+WoAH9WU6KnXFwLxpltCayueWsLOzDsX6sUbLcp/vPPrkA20CzeMerxY9E="; X509Certificate rootCertificate = (X509Certificate) cf .generateCertificate(new ByteArrayInputStream(Base64Utils.decode(rootCert))); byte[] authorityInfoAccesses = certificate.getExtensionValue(Extension.authorityInfoAccess.getId()); ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(authorityInfoAccesses)); ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); ASN1Primitive asn1Primitive = aIn.readObject(); ASN1Sequence AccessDescriptions = (ASN1Sequence) asn1Primitive; String AccessLocation = null; for (int i = 0; i < AccessDescriptions.size(); i++) { 
    ASN1Sequence AccessDescription = (ASN1Sequence) AccessDescriptions.getObjectAt(i); if (AccessDescription.size() != 2) { 
    continue; } else if (AccessDescription.getObjectAt(0) instanceof ASN1ObjectIdentifier) { 
    ASN1ObjectIdentifier id = (ASN1ObjectIdentifier) AccessDescription.getObjectAt(0); if (SecurityIDs.ID_OCSP.equals(id.getId())) { 
    ASN1Primitive description = (ASN1Primitive) AccessDescription.getObjectAt(1); AccessLocation = getStringFromGeneralName(description); // 区别于itext源码,不获取ldap协议地址 if (AccessLocation.startsWith("ldap")) { 
    continue; } if (AccessLocation == null) { 
    System.out.println(""); } else { 
    System.out.println(AccessLocation); } } } } OCSPResp ocspResponse = getOcspResponse(certificate, rootCertificate, AccessLocation); System.out.println(ocspResponse); BasicOCSPResp basicResponse = null; //获取到BasicOCSPResp try { 
    basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject(); } catch (Exception e) { 
    logger.warn(e.getMessage(), e); System.out.println("吊销校验异常"); } if (basicResponse != null) { 
    SingleResp[] responses = basicResponse.getResponses(); if (responses.length == 1) { 
    SingleResp resp = responses[0]; Object status = resp.getCertStatus(); if (status == CertificateStatus.GOOD) { 
    System.out.println("证书可以正常使用"); } else if (status instanceof RevokedStatus) { 
    System.out.println("该证书已吊销"); } else { 
    logger.warn("ocsp校验结果:" + "ocsp.status.is.unknown"); System.out.println("吊销校验异常"); } } } } 

在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/129905.html

(0)
上一篇 2025-08-20 20:33
下一篇 2025-08-20 20:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信