如何使用Python请求库检查OCSP客户端证书吊销? [英] How to check OCSP client certificate revocation using Python Requests library?

查看:158
本文介绍了如何使用Python请求库检查OCSP客户端证书吊销?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用Python请求库向EJBCA OSCP响应器发出简单请求,以撤消证书吊销状态?

示例:

 #确定证书是否已被吊销ocsp_url = req_cert.extensions [2] .value [0] .access_location.valueocsp_headers = {"whatGoes:here?"ocsp_body = {这里怎么办?"}ocsp_response = requests.get(ocsp_url,ocsp_headers,ocsp_body)如果(ocsp_response =='已撤销'):返回func.HttpResponse(证书无效(已撤销).") 

解决方案

基本上,它涉及以下步骤:

  • 获取主机名的相应证书
  • 如果证书中包含相应的条目,则可以通过AuthorityInformationAccessOID.CA_ISSUERS查询扩展名,如果成功,它将为您提供指向颁发者证书的链接
  • 通过此链接获取颁发者证书
  • 类似地,您通过AuthorityInformationAccessOID.OCSP获得相应的OCSP服务器
  • 使用有关当前证书,issuer_cert和ocsp服务器的信息,您可以向OCSPRequestBuilder馈送以创建OCSP请求
  • 使用 requests.get 获取OCSP响应
  • 从OCSP响应中检索 certificate_status

要检索主机名和端口的证书,可以使用以下正确答案:

服务器在TLS握手期间提供服务器证书以及通常一个或多个中间证书.通常"一词是有意使用的:某些服务器配置为不提供中间证书.然后,浏览器使用AIA提取来构建证书链.

证书颁发机构信息访问扩展中最多可以出现两个条目:用于下载颁发者证书的条目以及到OCSP服务器的链接.

这些条目也可能丢失,但是检查100个最受欢迎服务器的证书的简短测试脚本显示,这些条目通常包含在公共证书颁发机构颁发的证书中.

CA Issuers条目也可能会丢失,但是虽然可以使用有关OCSP服务器的信息,但是可以对它进行测试,例如.使用自签名证书的OpenSSL:

在这种情况下,您必须从TLS握手中的链中确定颁发者证书,该证书直接位于链中服务器证书之后,也请参见上图.

仅出于完整性考虑:有时还会发生另一种情况,尤其是与自签名证书一起使用时:如果未使用中间证书,则必须使用相应的根证书(例如,本地信任存储中可用)作为发行者证书.

How do I make a simple request for certificate revocation status to an EJBCA OSCP Responder using the Python requests library?

Example:

# Determine if certificate has been revoked

    ocsp_url = req_cert.extensions[2].value[0].access_location.value
    ocsp_headers = {"whatGoes: here?"}
    ocsp_body = {"What goes here?"}
    ocsp_response = requests.get(ocsp_url, ocsp_headers, ocsp_body)

    if (ocsp_response == 'revoked'):
       return func.HttpResponse(
           "Certificate is not valid (Revoked)."
       )

解决方案

Basically it involves the following steps:

  • retrieve the corresponding cert for a hostname
  • if a corresponding entry is contained in the certificate, you can query the extensions via AuthorityInformationAccessOID.CA_ISSUERS, which will provide you with a link to the issuer certificate if successful
  • retrieve the issuer cert with this link
  • similarly you get via AuthorityInformationAccessOID.OCSP the corresponding OCSP server
  • with this information about the current cert, the issuer_cert and the ocsp server you can feed OCSPRequestBuilder to create an OCSP request
  • use requests.get to get the OCSP response
  • from the OCSP response retrieve the certificate_status

To retrieve a cert for a hostname and port, you can use this fine answer: https://stackoverflow.com/a/49132495. The OCSP handling in Python is documented here: https://cryptography.io/en/latest/x509/ocsp.html.

Code

If you convert the above points into a self-contained example, it looks something like this:

import base64
import ssl
import requests
from urllib.parse import urljoin

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.x509 import ocsp
from cryptography.x509.ocsp import OCSPResponseStatus
from cryptography.x509.oid import ExtensionOID, AuthorityInformationAccessOID


def get_cert_for_hostname(hostname, port):
    conn = ssl.create_connection((hostname, port))
    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    sock = context.wrap_socket(conn, server_hostname=hostname)
    certDER = sock.getpeercert(True)
    certPEM = ssl.DER_cert_to_PEM_cert(certDER)
    return x509.load_pem_x509_certificate(certPEM.encode('ascii'), default_backend())


def get_issuer(cert):
    aia = cert.extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_INFORMATION_ACCESS).value
    issuers = [ia for ia in aia if ia.access_method == AuthorityInformationAccessOID.CA_ISSUERS]
    if not issuers:
        raise Exception(f'no issuers entry in AIA')
    return issuers[0].access_location.value


def get_ocsp_server(cert):
    aia = cert.extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_INFORMATION_ACCESS).value
    ocsps = [ia for ia in aia if ia.access_method == AuthorityInformationAccessOID.OCSP]
    if not ocsps:
        raise Exception(f'no ocsp server entry in AIA')
    return ocsps[0].access_location.value


def get_issuer_cert(ca_issuer):
    issuer_response = requests.get(ca_issuer)
    if issuer_response.ok:
        issuerDER = issuer_response.content
        issuerPEM = ssl.DER_cert_to_PEM_cert(issuerDER)
        return x509.load_pem_x509_certificate(issuerPEM.encode('ascii'), default_backend())
    raise Exception(f'fetching issuer cert  failed with response status: {issuer_response.status_code}')


def get_oscp_request(ocsp_server, cert, issuer_cert):
    builder = ocsp.OCSPRequestBuilder()
    builder = builder.add_certificate(cert, issuer_cert, SHA256())
    req = builder.build()
    req_path = base64.b64encode(req.public_bytes(serialization.Encoding.DER))
    return urljoin(ocsp_server + '/', req_path.decode('ascii'))


def get_ocsp_cert_status(ocsp_server, cert, issuer_cert):
    ocsp_resp = requests.get(get_oscp_request(ocsp_server, cert, issuer_cert))
    if ocsp_resp.ok:
        ocsp_decoded = ocsp.load_der_ocsp_response(ocsp_resp.content)
        if ocsp_decoded.response_status == OCSPResponseStatus.SUCCESSFUL:
            return ocsp_decoded.certificate_status
        else:
            raise Exception(f'decoding ocsp response failed: {ocsp_decoded.response_status}')
    raise Exception(f'fetching ocsp cert status failed with response status: {ocsp_resp.status_code}')


def get_cert_status_for_host(hostname, port):
    print('   hostname:', hostname, "port:", port)
    cert = get_cert_for_hostname(hostname, port)
    ca_issuer = get_issuer(cert)
    print('   issuer ->', ca_issuer)
    issuer_cert = get_issuer_cert(ca_issuer)
    ocsp_server = get_ocsp_server(cert)
    print('   ocsp_server ->', ocsp_server)
    return get_ocsp_cert_status(ocsp_server, cert, issuer_cert)

Test 1: Good Certificate

A test call like the following with a good certificate

status = get_cert_status_for_host('software7.com', 443)
print('software7.com:', status, '\n')

results in the following output:

   hostname: software7.com port: 443
   issuer -> http://cacerts.digicert.com/EncryptionEverywhereDVTLSCA-G1.crt
   ocsp_server -> http://ocsp.digicert.com
software7.com: OCSPCertStatus.GOOD 

Test 2: Revoked Certificate

Of course you also have to do a counter test with a revoked cert. Here revoked.badssl.com is the first choice:

status = get_cert_status_for_host('revoked.badssl.com', 443)
print('revoked.badssl.com:', status, '\n')

This gives as output:

   hostname: revoked.badssl.com port: 443
   issuer -> http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt
   ocsp_server -> http://ocsp.digicert.com
revoked.badssl.com: OCSPCertStatus.REVOKED 

AIA Retrieval of the Issuer Certificate

A typical scenario for a certificate relationship looks as follows:

The server provides the server certificate and usually one or more intermediate certificates during the TLS handshake. The word 'usually' is used intentionally: some servers are configured not to deliver intermediate certificates. The browsers then use AIA fetching to build the certification chain.

Up to two entries can be present in the Certificate Authority Information Access extension: The entry for downloading the issuer certificate and the link to the OCSP server.

These entries may also be missing, but a short test script that checks the certs of the 100 most popular servers shows that these entries are usually included in certificates issued by public certification authorities.

The CA Issuers entry may also be missing, but while the information about an OCSP server is available, it can be tested e.g. with OpenSSL using a self-signed certificate:

In this case you would have to determine the issuer certificate from the chain in the TLS handshake, it is the certificate that comes directly after the server certificate in the chain, see also the figure above.

Just for the sake of completeness: There is another case that can sometimes occur especially in conjunction with self-signed certificates: If no intermediate certificates are used, the corresponding root certificate (e.g. available in the local trust store) must be used as issuer certificate.

这篇关于如何使用Python请求库检查OCSP客户端证书吊销?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆