模块 OpenSSL::OCSP

OpenSSL::OCSP 实现了在线证书状态协议的请求和响应。

创建和发送 OCSP 请求需要一个包含 authorityInfoAccess 扩展中 OCSP URL 的主题证书,以及主题证书的颁发者证书。首先,加载颁发者和主题证书

subject = OpenSSL::X509::Certificate.new subject_pem
issuer  = OpenSSL::X509::Certificate.new issuer_pem

要创建请求,我们需要为主题证书创建一个证书 ID,以便 CA 知道我们正在查询哪个证书

digest = OpenSSL::Digest.new('SHA1')
certificate_id =
  OpenSSL::OCSP::CertificateId.new subject, issuer, digest

然后创建一个请求,并将证书 ID 添加到其中

request = OpenSSL::OCSP::Request.new
request.add_certid certificate_id

在请求中添加一个随机数可以防止重放攻击,但并非所有 CA 都会处理随机数。

request.add_nonce

要将请求提交到 CA 进行验证,我们需要从主题证书中提取 OCSP URI

ocsp_uris = subject.ocsp_uris

require 'uri'

ocsp_uri = URI ocsp_uris[0]

要提交请求,我们将请求 POST 到 OCSP URI(根据 RFC 2560)。请注意,在此示例中,我们仅处理 HTTP 请求,而不处理任何重定向,因此这不足以用于严肃的用途。

require 'net/http'

http_response =
  Net::HTTP.start ocsp_uri.hostname, ocsp_uri.port do |http|
    http.post ocsp_uri.path, request.to_der,
              'content-type' => 'application/ocsp-request'
end

response = OpenSSL::OCSP::Response.new http_response.body
response_basic = response.basic

首先,我们检查响应是否具有有效的签名。如果没有有效的签名,我们无法信任它。如果在此处收到失败,则可能是您缺少系统证书存储区,或者可能缺少中间证书。

store = OpenSSL::X509::Store.new
store.set_default_paths

unless response_basic.verify [], store then
  raise 'response is not signed by a trusted certificate'
end

响应包含状态信息(成功/失败)。我们可以将状态显示为字符串

puts response.status_string #=> successful

接下来,我们需要知道响应的详细信息,以确定响应是否与我们的请求匹配。首先,我们检查随机数。同样,并非所有 CA 都支持随机数。有关返回值含义,请参见 Request#check_nonce

p request.check_nonce basic_response #=> value from -1 to 3

然后从基本响应中提取证书的状态信息。

single_response = basic_response.find_response(certificate_id)

unless single_response
  raise 'basic_response does not have the status for the certificate'
end

然后检查有效性。将来发布的任何状态都必须被拒绝。

unless single_response.check_validity
  raise 'this_update is in the future or next_update time has passed'
end

case single_response.cert_status
when OpenSSL::OCSP::V_CERTSTATUS_GOOD
  puts 'certificate is still valid'
when OpenSSL::OCSP::V_CERTSTATUS_REVOKED
  puts "certificate has been revoked at #{single_response.revocation_time}"
when OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
  puts 'responder doesn't know about the certificate'
end