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

这篇具有很好参考价值的文章主要介绍了数字证书的相关专业名词(下)---OCSP及其java中的应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

上篇文章我们了解了根证书和校验证书有效性中的一个比较重要的渠道–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();

其实我一开始看的时候会觉得中间两步是不是有点多余,我明明可以直接aIn.readObject()得到ASN1Primitive,为什么还要多转成一次ASN1OctetString呢。
我们实践一下
数字证书的相关专业名词(下)---OCSP及其java中的应用
通过上面忽略两步操作后,发现报了类型转化的异常,这个原因又是为什么呢
首先是我们获取的证书中的扩展信息,根据 X.509 标准,CRL 分发点扩展是由一个 OCTET STRING 构成的,其中包含了一个 ASN.1 序列。
如果 CRL 分发点扩展由 OCTET STRING构成,就像这个例子一样,直接将 OCTET STRING 转换为 ASN.1 序列(ASN1Sequence),则会抛出类型转换异常。因为 ASN.1 编码规范中定义,OCTET STRING 类型的数据是由一个长度和一个字节数组组成的。而 ASN.1 序列则是由一组有序的元素组成的,每个元素都有自己的标识符和数据值。
所以我们的目的是为了从OCTET STRING中拿到ASN.1 序列,而不是直接将OCTET STRING转化成序列
在第一次调用 ASN1InputStream.readObject() 方法时,返回的确实是一个 DEROctetString 对象。这是因为 ASN1InputStream.readObject() 方法会根据输入流中的数据类型返回对应的 ASN.1 原语对象。在这里,由于输入流中的数据类型是 OCTET STRING,因此返回的就是一个 DEROctetString 对象。然后为了转化成ASN1Sequence,我们将 OCTET STRING 中的字节流作为参数重新生成ASN1InputStream对象aIn。通过aIn.readObject()得到ASN.1原语对象,通过这样就可以从 OCTET STRING 中提取出 ASN.1 序列。

  1. 用之前获取的ASN1Primitive对象转化成ASN1Sequence,该对象包含多个AccessDescription,遍历每个AccessDescription,判断其是否为OCSP协议类型,并获取对应的AccessLocation字符串。在获取AccessLocation时,会跳过ldap协议地址。如果没有找到符合条件的AccessDescription,则返回null。

这里介绍一下什么是AccessDescription

AccessDescription是一个ASN.1结构,用于描述数字证书中的访问描述符信息。它通常用于在证书扩展中传递OCSP(Online Certificate Status Protocol)或者CA Issuers的地址信息。AccessDescription本质上是一个序列(Sequence),包含两个元素:
1.accessMethod:用于指定AccessDescription的类型,例如OCSP或者CA Issuers等。
2.accessLocation:用于存储对应的访问地址信息,可以是URI字符串或者其他通用名(GeneralName)的表示方式。

代码如下

            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 ;
	                    }
	                }
	            }
	        }

我们debug可以看一下
数字证书的相关专业名词(下)---OCSP及其java中的应用
在获取数字证书中的OCSP URL时,AccessDescription就是包含OCSP地址信息的ASN.1结构。具体而言,该字段的accessMethod值应该为“1.3.6.1.5.5.7.48.1”,即OCSP协议类型的标识符,而accessLocation则应该是一个URI字符串,表示OCSP服务器的地址。

完整测试代码如下:

public static void main(String[] args) throws CertificateException, IOException, CRLException {
		String rootCert = "MIIFuDCCBKCgAwIBAgIQche7n/HhSQ+guIZEOjNvGzANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFNIRUNBIEcyMB4XDTIzMDIwNTE3MDQ0MVoXDTIzMDUwNjE1NTk1OVowTzELMAkGA1UEBhMCQ04xGzAZBgNVBAoMEueUteWtkOetvueroOWJjeWPsDEjMCEGA1UEAwwa5rWL6K+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");
	}

执行后可以获取OCSP URL
数字证书的相关专业名词(下)---OCSP及其java中的应用

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

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

1. 生成ocsp请求OCSPReq

我们需要的内容有颁发者证书(根证书),待验证证书的序列号
步骤如下:
①创建CertificateID
首先根据输入的颁发者证书和待查询证书的序列号(serialNumber),生成代表该证书的唯一标识——CertificateID。,代码如下:

		// 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。

然后结合待验证证书的序列号加上前面我们获取的SHA-1算法的JcaDigestCalculator实例以及X509CertificateHolder就可以构成CertificateID。
②创建OCSPReqBuilder
然后创建OCSPReqBuilder对象,并使用addRequest方法向请求中添加待查询的证书信息,即上一步中生成的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()来获取

在PDF文档中,Nonce是一种用于生成加密密钥的随机数。为了确保生成的Nonce具有足够的熵(即随机性),应该使用高质量的随机数生成器来生成它。在iText 7中,可以使用PdfEncryption.createDocumentId()方法来获取一个具有足够熵的随机数作为Nonce,因为该方法使用了安全的随机数生成器。
确保生成的Nonce是具有足够熵的随机数非常重要,因为如果Nonce不够随机,则可能会出现加密弱点。攻击者可能会利用这些弱点来破解加密并访问被加密的内容。因此,使用安全的随机数生成器来生成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不太熟悉

BasicOCSPResp是OCSP协议中的一个类,用于表示OCSP响应消息体中的基本响应消息。它包含了指定证书的响应状态信息,以及生成响应的签名等元数据。
在OCSP响应消息体中,BasicOCSPResp是必须存在的。它的结构如下:
ResponderID: 响应者(OCSP服务器)的身份标识。
ProducedAt: 响应消息体生成的时间戳。
Responses: 包含了待验证证书的相关信息和响应状态信息。
ResponseExtensions: 可选,包含了响应消息的扩展字段。
SignatureAlgorithmIdentifier: 签名算法标识符。
Signature: 对响应消息体进行数字签名后的签名值。
BasicOCSPResp类提供了一些方法来获取响应消息体的各个部分的内容,如getResponderId()方法可以获取响应者的身份标识,getProducedAt()方法可以获取响应消息生成的时间戳等。通过这些方法,可以对OCSP响应消息体进行解析和处理。

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

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表示校验异常。

通过判断单个响应数量,可以确保OCSP响应消息体格式正确且只包含一个单个响应。
然后就可以通过刚才获取的ocspUrl去验证结果了。

	public static void main(String[] args) throws GeneralSecurityException, IOException, OperatorException, OCSPException {
		String verifyCert = "MIIFuDCCBKCgAwIBAgIQche7n/HhSQ+guIZEOjNvGzANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFNIRUNBIEcyMB4XDTIzMDIwNTE3MDQ0MVoXDTIzMDUwNjE1NTk1OVowTzELMAkGA1UEBhMCQ04xGzAZBgNVBAoMEueUteWtkOetvueroOWJjeWPsDEjMCEGA1UEAwwa5rWL6K+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/PJFceE2VmIFXZ9qDANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFVDQSBSb290MB4XDTE1MDMxMzAwMDAwMFoXDTI5MTIzMTAwMDAwMFowMzELMAkGA1UEBhMCQ04xETAPBgNVBAoMCFVuaVRydXN0MREwDwYDVQQDDAhTSEVDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN6IIHj7qZIYrz466zxTGCPURSI6GZqbFCTtxBrPnTE5SKtCIyqRNwj/+3A9f/cCyWYHptLeGeY80WORDGuZBBHiVTEsIHSnXZvfqCnQf9KmAisVOOTIGCPWJvCRnfMWLdAcENNaZDIxpkc31ZejALBNPHJDDhxmt6PqyvdX5/cF6gkXO2OOzCa/EF5+x9LwWUKAGR/b+x5j5vt637AQjNmt5Xym63sQdwEaAHqTuPCbcwl+Y1eKXmWuFUXcMk+JdbOhXmjqbOIhup5yrx+hyXc+dtRBJzuSEpvC7WkXLJInR2dqb+Bc2ReJd6zM1deM1MPRmqdJQKEDyT7lEXST53UCAwEAAaOCASYwggEiMEEGA1UdIAQ6MDgwNgYIKoEchu86gRUwKjAoBggrBgEFBQcCARYcaHR0cDovL3d3dy5zaGVjYS5jb20vcG9saWN5LzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBDBggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9sZGFwMi5zaGVjYS5jb20vcm9vdC91Y2Fyb290LmRlcjAdBgNVHQ4EFgQUVoje4xhDgrdypCbrRKli0IfErCYwHwYDVR0jBBgwFoAU2x8182tM/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("吊销校验异常");
				}
			}
		}
	}

数字证书的相关专业名词(下)---OCSP及其java中的应用文章来源地址https://www.toymoban.com/news/detail-469887.html

到了这里,关于数字证书的相关专业名词(下)---OCSP及其java中的应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python实现对称加密、数字签名、数字证书颁发

    一.开发目的: 理解开源密码库实现的基本架构,熟悉对称算法的加解密函数封装与调用,并能能够利用开源设计接口进行二次封装,并实现一个界面友好,功能正确的采用对称算法的文件加解密工具。 二.开发环境: 硬件环境: 处理器:Intel®Core™i5-1035G1 CPU @1.00GHz 1.19GHz2

    2024年02月13日
    浏览(41)
  • SSL数字证书服务

    SSL/TLS 证书允许Web浏览器使用安全套接字层/传输层安全 (SSL/TLS) 协议识别并建立与网站的加密网络连接。 SSL数字证书主要功能 SSL证书在浏览器或用户计算机与服务器或网站之间建立加密连接。这种连接可以保护传输中的敏感数据免遭非授权方的拦截,从而使在线交易能够完全

    2024年02月06日
    浏览(48)
  • 安全算法(三)消息验证码、数字签名和数字证书

    主要介绍了消息验证码、数字签名和数字证书三种加密方式。 消息认证码 消息认证码可以实现“认证”和“检测篡改”这两个功能。密文的内容在传输过程中可能会被篡改,这会导致解密后的内容发生变化,从而产生误会。消息认证码就是可以预防这种情况发生的机制。 假

    2024年01月22日
    浏览(48)
  • 非对称加密与数字证书

    非对称加密,是指不能从加密密钥推算出解密密钥。加密密钥不需要保密,可以公开,称之为 公钥 ,只需要保守解密秘钥称之为 私钥 。公钥和私钥是 成对 的。常见的非对称加密算法有:RSA、Elgamal、背包算法、Rabin、D-H、ECC。 所谓“成对”的含义:如果用公开密钥对数据进

    2024年02月09日
    浏览(52)
  • 【pfx数字证书制作及操作使用】

    公司业务扩展,要对外开放接口,由于数据的安全性,主要是公司是卖数据的,需要对接口进行验证,于是使用数字证书进行加密解密对数据进行处理,防止篡改接口请求,非法获取数据,于是整理数字证书使用与制做。 在options.config文件中设置证书有效期 CN=名称(一般填公

    2024年02月12日
    浏览(36)
  • 安装软件和驱动程序需要微软数字签名无法安装?数字签名和数字证书区别

    1.安装软件和驱动程序需要微软数字签名无法安装 windows安装exe安装包时: 1.1  解决方法: 1.2           这个问题需要安装驱动补丁进行更新驱动操作,如:Windows6.1-sha2补丁.msu 1.3           这种属于签名问题,解决方法: 1..进入“控制面板”,查看方式为大图标,点击“

    2024年02月05日
    浏览(74)
  • 区块链之加解密算法&数字证书

    目录 一.加解密算法 数字签名 对称加密 DES(Data EncryptionStandard) 3DES(Triple DES) AES(Advanced EncryptionStandard) RSA加密法 DSA(Digital SignatureAlgorithm) ECC(Elliptic CurvesCryptography) 非对称加密 签名与加密过程 非对称加密的应用 对称加密与非对称加密的结合 二.数字证书 图解 加密简单而言就是通

    2024年01月19日
    浏览(39)
  • Https、CA证书、数字签名

    Https Http协议 Http协议是目前应用比较多应用层协议,浏览器对于Http协议已经实现。Http协议基本的构成部分有 请求行 : 请求报文的第一行 请求头 : 从第二行开始为请求头内容的开始部分。每一个请求头都是由K-V键值对组成。 请求体: 请求头完成后以空行结束、从空行开始

    2024年02月12日
    浏览(46)
  • 阿里云ssl免费数字证书快过期 如何更换

    数字证书管理服务-ssl证书 此处记录本地的下载路径 /Users/dorsey/Downloads/10791167_lzzabc.cn_nginx/lzzabc.cn.pem  /Users/dorsey/Downloads/10791167_lzzabc.cn_nginx/lzzabc.cn.key 找到ssl存放位置    /www/https/lzzabc.cn.pem  /www/https/lzzabc.cn.key  mv admin.lzzabc.cn.pem admin2.lzzabc.cn.pem 命令为  scp 本地用户名@IP地址

    2024年02月14日
    浏览(42)
  • 密码学基础(三)——数字签名与证书

    数字签名:又叫公钥数字签名,或者电子印章。 数字信息社会用于取代传统社会手写签名的一种公钥加密领域的技术实现。 数字签名其实就是非对称加密的私钥加密,公钥解密的过程。 数字证书用来证明公钥拥有者的身份,验证数据来源,验证数据是否被修改。 数字证书中

    2024年02月16日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包