赞
踩
上篇文章我们主要了解了PKI中的数字证书和PKCS,这篇文章我们主要了解一下根证书,以及OCSP和CRL。
在java中,我们可以通过证书的X509形式类X509Certificate去获取CRL地址,步骤如下
X509Certificate cert = ... // 从某处获取证书对象
byte[] crlDistributionPointsExtension = cert.getExtensionValue("2.5.29.31");
其中2.5.29.31是 X.509标准中定义的证书扩展之一 ,也称为CRL Distribution Points扩展。该扩展用于
指定证书撤销列表(CRL)的位置 ,以便验证人员可以在验证证书时检查该证书是否已被撤销。CRL Distribution
Points扩展包含一个或多个分发点,每个分发点都包含一个或多个CRL地址,其中可以获取到对应证书的CRL列表。java中也可以通过以下方法获取,同样也是2.5.29.31
X509Extensions.CRLDistributionPoints.getId()
ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(crlDistributionPointsExtension));
ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets()));
ASN1Primitive asn1Primitive = aIn.readObject();
3.从CRL分发点扩展中获取CRL地址.
①首先通过之前的ASN1Primitive对象获取CRLDistPoint,CRLDistPoint是X.509证书中的一个扩展字段,它用于指定可撤销证书列表(CRL)的分发点。
②接着通过CRLDistPoint类中的getDistributionPoints()方法用于获取CRL分发点。具体来说,它返回一个DistributionPoint数组,其中包含了所有在CRLDistPoint扩展字段中找到的CRL分发点。
③然后通过DistributionPoint数组作增强for循环,对每一个CRL分发点进行操作,首先DistributionPoint取出DistributionPointName
DistributionPointName是Java中表示CRL分发点名称的类。它用于表示CRLDistPoint扩展字段中的一个完整的CRL分发点。
CRL分发点可以是以下三种类型之一:
名称(Name):这种类型的CRL分发点包含了CRL的发行者的名称,例如"CN=Example Root CA, O=Example Inc.,
C=US"。
完全限定名(Full Name):这种类型的CRL分发点包含了CRL的URI地址,例如"http://example.com/crl.crl"。
相对名称(Relative Name):这种类型的CRL分发点包含了相对于证书颁发机构的位置的路径,例如"crl/root-ca.crl"。
DistributionPointName类提供了几个方法来获取和设置CRL分发点名称的类型和值。其中最常用的方法是getName()
,它返回一个GeneralName对象,可以通过该对象来访问具体的CRL分发点值;然后就是getType()
,它返回一个int类型的type,含义是CRL分发点类型。如果CRL分发点类型为"名称"或者"完全限定名",那么GeneralName对象将被转换为GeneralNames对象,从而可以访问其中的所有名称;如果CRL分发点类型为"相对名称",那么GeneralName对象将被转换为RelativeDistinguishedName对象,从而可以访问具体的相对路径。
④那么我们这边只需要取type为完全限定名类型的分发点即可,因为我们的目的是为了获取URI地址,取出对应的完全限定名类型的分发点后,通过getName()并且强转成GeneralNames,然后通过getNames()返回一个由
GeneralName 对象组成的数组,所有通用名称。
⑤最后通过每一个通用名称去进行筛选,把符合条件的取出即是我们最后的CRL地址
我们这边通过2个条件去进行筛选
1.GeneralName.getTagNo()是否为GeneralName.uniformResourceIdentifier
GeneralName.getTagNo() 是 org.bouncycastle.asn1.x509.GeneralName
类中的一个方法,用于获取通用名称类型的标记号。通用名称是 X.509 标准中定义的一种标识实体的方式,例如主体名称、主体替代名称或分发点名称等。
在 ASN.1 编码规范中,每个通用名称类型都有一个唯一的标记号来标识它。getTagNo() 方法返回当前通用名称对象的标记号。
GeneralName name = ... // 获取 GeneralName 对象
int tagNo = name.getTagNo();
switch (tagNo) {
case GeneralName.dNSName:
// 处理 DNS 名称
break;
case GeneralName.iPAddress:
// 处理 IP 地址
break;
case GeneralName.directoryName:
// 处理目录名称
break;
// 其他通用名称类型
}
而GeneralName.uniformResourceIdentifier表示 X.509
证书的通用名称类型之一,它表示一个统一资源标识符(URI)。URI 是一种标识某个资源的字符串,它可以指向任何类型的资源,例如 Web
页面、电子邮件地址、文件等。
通常情况下,URI 使用的是 URL(Uniform Resource Locator)或 URN(Uniform Resource Name)格式。
URL 定位到一个资源,并且可以直接访问该资源,而 URN 只是一个命名方式,不包含具体位置信息。
在 X.509证书中,URI 通常被用作 CRL 分发点名称(CRL Distribution Point Name)或颁发者名称(Issuer
Name)等标识信息的一部分。
所以我们判断我们的通用名称标记类型是否是URI,不是则直接去找下一个通用名称,是的话接着判断该URI是否是LDAP协议,我们这边只需要http协议的URI,所以不需要LDAP协议的,最终均满足条件的就是最终的CRL
URL。
获取CRL的URL代码如下:
CRLDistPoint dist = CRLDistPoint.getInstance(asn1Primitive); DistributionPoint[] dists = dist.getDistributionPoints(); for (DistributionPoint p : dists) { DistributionPointName distributionPointName = p.getDistributionPoint(); if (DistributionPointName.FULL_NAME != distributionPointName.getType()) { continue; } GeneralNames generalNames = (GeneralNames)distributionPointName.getName(); GeneralName[] names = generalNames.getNames(); for (GeneralName name : names) { if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { continue; } DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject)name.toASN1Primitive(), false); String result = derStr.getString(); // 区别于itext源码,不获取ldap协议地址 if (result.startsWith("ldap")) { continue; } System.out.println(derStr); } }
完整获取CRI的例子如下:
public static void main(String[] args) throws CertificateException, IOException { String rootCert = "MIIFrzCCBJegAwIBAgIQebTsANjzNmcwIHFPVk62NTANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxETAPBgNVBAMMCFNIRUNBIEcyMB4XDTE2MDkwNTE2MDAwMFoXDTE3MDkwNTE2MDAwMFowQzELMAkGA1UEBhMCQ04xIDAeBgkqhkiG9w0BCQEWEXBvc3RAcWl5dWVzdW8uY29tMRIwEAYDVQQDDAnlsJrkv67mmbowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjK48pdmxvXMwyzRiozXayAdSClELgslZvmmg0e58royRj3UrfijEHtSiWHchGRZn6YQrZe6sj5TSlQvNSUxFN/DCX1eFnDRzr9fuA5UEGiOeZQkMJFZej0v/sRzJmsRJEGWAcXXmPzRAp1/iTR4WYtf6TzOq7xiKmz23JbGgFdlFLMQxxVJSpRuJPV8TT5nOZtoVlBkVveIDPUMC+oxOKKstqYi+qPc3iaOXq8Q1fgLPE+mutuEpjep7fxL8e8DQx0ON2BIRicNdWzGY16oFfnxEQt8jf39dCLLjDfPwBa6VxGUc13HWfMMG/lDhJJXCx1S6EhCXXpJzKkzPZtvJrAgMBAAGjggKtMIICqTAfBgNVHSMEGDAWgBRWiN7jGEOCt3KkJutEqWLQh8SsJjAdBgNVHQ4EFgQUPVpnYHAUzuNerenuuN9DGbKX86EwCwYDVR0PBAQDAgbAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDBBBgNVHSAEOjA4MDYGCCqBHAHFOIEVMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly93d3cuc2hlY2EuY29tL3BvbGljeS8wCQYDVR0TBAIwADCB3AYDVR0fBIHUMIHRMIGXoIGUoIGRhoGObGRhcDovL2xkYXAyLnNoZWNhLmNvbTozODkvY249Q1JMMC5jcmwsb3U9UkEyMDE2MDkwNSxvdT1DQTMxLG91PWNybCxvPVVuaVRydXN0P2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludDA1oDOgMYYvaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9DQTMxL1JBMjAxNjA5MDUvQ1JMMC5jcmwwfQYIKwYBBQUHAQEEcTBvMDUGCCsGAQUFBzABhilodHRwOi8vb2NzcDMuc2hlY2EuY29tL1NoZWNhZzIvc2hlY2Eub2NzcDA2BggrBgEFBQcwAoYqaHR0cDovL2xkYXAyLnNoZWNhLmNvbS9yb290L3NoZWNhZzJzdWIuZGVyMIGOBgYqgRwBxTgEgYMwgYAwSQYIKoEcAcU4gRAEPWxkYXA6Ly9sZGFwMi5zaGVjYS5jb20vb3U9c2hlY2EgY2VydGlmaWNhdGUgY2hhaW4sbz1zaGVjYS5jb20wEQYIKoEcAcU4gRMEBTY1Nzk5MCAGCCqBHAHFOIEUBBRTRjMyMDcyMTE5ODkwNzIzMDIxWDANBgkqhkiG9w0BAQsFAAOCAQEAADBN2mmMSYiKgNqgYVJtQ5KHmzP2smphBZurYRpU2sSffdXQTxtfh0g7L4klTHDSQEBa+/dNpt/g6oaMPxXwXMQA5IYWXNpcHjEPi66v4c/melPGWvWwsuQcyBC4y5uI3+tjh9kjQrU3JKaw161nSOJ86kX5bfQwPR5kyZEW15Xuuq3wRsJl8Gv6oriPenXALIFtj0JqI4PGDx7FqWzUZowxVlX38InJ9Z+YZ+UhGRpNBZ7j7yF0Y+ApTApKCnTkUPo0ST7kY9C8ripL3bOJXv6WWt+40WJirLLcJEs4YEQ864ca3beHDACbaueJJera1iMi726pfz8L5zCmRjGK7w=="; CertificateFactory cf = CertificateFactory.getInstance("X.509", new BouncyCastleProvider()); X509Certificate certificate = (X509Certificate) cf .generateCertificate(new ByteArrayInputStream(Base64Utils.decode(rootCert))); byte[] crlDistributionPointsExtension = certificate.getExtensionValue("2.5.29.31"); ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(crlDistributionPointsExtension)); ASN1OctetString octs = (ASN1OctetString) aIn.readObject(); aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets())); ASN1Primitive asn1Primitive = aIn.readObject(); CRLDistPoint dist = CRLDistPoint.getInstance(asn1Primitive); DistributionPoint[] dists = dist.getDistributionPoints(); for (DistributionPoint p : dists) { DistributionPointName distributionPointName = p.getDistributionPoint(); if (DistributionPointName.FULL_NAME != distributionPointName.getType()) { continue; } GeneralNames generalNames = (GeneralNames)distributionPointName.getName(); GeneralName[] names = generalNames.getNames(); for (GeneralName name : names) { if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { continue; } DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject)name.toASN1Primitive(), false); String result = derStr.getString(); // 区别于itext源码,不获取ldap协议地址 if (result.startsWith("ldap")) { continue; } System.out.println(derStr); } } }
发现可以获取成功
然后我们可以将CRL URL转化为CRL类
public static CRL getCRL(String url) throws IOException, CertificateException, CRLException {
if (url == null) {
return null;
}
InputStream is = new URL(url).openStream();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return cf.generateCRL(is);
}
调用其isRevoked()方法判断证书是否吊销,参数接收的是要验证的证书的X509形式类
crl.isRevoked(certificate)
CRL是撤销证书列表的缩写,它记录了已经被吊销的证书序列号或其他标识信息。CRL的缺点包括:
为了克服这些缺点,现在一些颁发机构采用了基于在线协议的证书吊销列表(OCSP)来验证证书的有效性。OCSP是一种更加高效的方式,它只返回特定证书的状态,而不需要下载整个CRL。这个我们后面会详细了解。
从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同学们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门程度而已,能力越强机会才越多。
因为入门学习阶段知识点比较杂,所以我讲得比较笼统,大家如果有不懂的地方可以找我咨询,我保证知无不言言无不尽,需要相关资料也可以找我要,我的网盘里一大堆资料都在吃灰呢。
干货主要有:
①1000+CTF历届题库(主流和经典的应该都有了)
②CTF技术文档(最全中文版)
③项目源码(四五十个有趣且经典的练手项目及源码)
④ CTF大赛、web安全、渗透测试方面的视频(适合小白学习)
⑤ 网络安全学习路线图(告别不入流的学习)
⑥ CTF/渗透测试工具镜像文件大全
⑦ 2023密码学/隐身术/PWN技术手册大全
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。