Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helpers for accessing AIA extensions of certs #293

Merged
merged 4 commits into from
Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions History.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ Version 2.2.0 (not yet released)
for last finished message sent and received.
* Add `OpenSSL::Timestamp` module for handing timestamp requests and
responses.
* Add helper methods for `OpenSSL::X509::{Certificate,CRL}`:
* Add helper methods for `OpenSSL::X509::Certificate`:
`find_extension`, `subject_key_identifier`,
`authority_key_identifier` (`Certificate` only) and `crl_uris`
(`Certificate` only).
`authority_key_identifier`, `crl_uris`, `ca_issuer_uris` and
`ocsp_uris`.
* Add helper methods for `OpenSSL::X509::CRL`:
`find_extension` and `subject_key_identifier`.
* Remove `OpenSSL::PKCS7::SignerInfo#name` alias for `#issuer`.
* Add `OpenSSL::ECPoint#add` for adding points to an elliptic curve
group.
Expand Down
11 changes: 2 additions & 9 deletions ext/openssl/ossl_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1734,18 +1734,11 @@ Init_ossl_ocsp(void)
* To submit the request to the CA for verification we need to extract the
* OCSP URI from the subject certificate:
*
* authority_info_access = subject.extensions.find do |extension|
* extension.oid == 'authorityInfoAccess'
* end
*
* descriptions = authority_info_access.value.split "\n"
* ocsp = descriptions.find do |description|
* description.start_with? 'OCSP'
* end
* ocsp_uris = subject.ocsp_uris
*
* require 'uri'
*
* ocsp_uri = URI ocsp[/URI:(.*)/, 1]
* ocsp_uri = URI ocsp_uris[0]
*
* To submit the request we'll POST the request to the OCSP URI (per RFC
* 2560). Note that we only handle HTTP requests and don't handle any
Expand Down
50 changes: 50 additions & 0 deletions lib/openssl/x509.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,55 @@ def crl_uris
crl_uris&.map(&:value)
end
end

module AuthorityInfoAccess
include Helpers

# Get the information and services for the issuer from the certificate's
# authority information access extension exteension, as described in RFC5280
# Section 4.2.2.1.
#
# Returns an array of strings or nil or raises ASN1::ASN1Error.
def ca_issuer_uris
aia_asn1 = parse_aia_asn1
return nil if aia_asn1.nil?

ca_issuer = aia_asn1.value.select do |authority_info_access|
authority_info_access.value.first.value == "caIssuers"
end

ca_issuer&.map(&:value)&.map(&:last)&.map(&:value)
end

# Get the URIs for OCSP from the certificate's authority information access
# extension exteension, as described in RFC5280 Section 4.2.2.1.
#
# Returns an array of strings or nil or raises ASN1::ASN1Error.
def ocsp_uris
aia_asn1 = parse_aia_asn1
return nil if aia_asn1.nil?

ocsp = aia_asn1.value.select do |authority_info_access|
authority_info_access.value.first.value == "OCSP"
end

ocsp&.map(&:value)&.map(&:last)&.map(&:value)
end

private

def parse_aia_asn1
ext = find_extension("authorityInfoAccess")
return nil if ext.nil?

aia_asn1 = ASN1.decode(ext.value_der)
if ext.critical? || aia_asn1.tag_class != :UNIVERSAL || aia_asn1.tag != ASN1::SEQUENCE
raise ASN1::ASN1Error, "invalid extension"
end

aia_asn1
end
end
end

class Name
Expand Down Expand Up @@ -291,6 +340,7 @@ class Certificate
include Extension::SubjectKeyIdentifier
include Extension::AuthorityKeyIdentifier
include Extension::CRLDistributionPoints
include Extension::AuthorityInfoAccess

def pretty_print(q)
q.object_group(self) {
Expand Down
36 changes: 34 additions & 2 deletions test/test_x509cert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def test_extension
["authorityKeyIdentifier","issuer:always,keyid:always",false],
["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
["subjectAltName","email:ee1@ruby-lang.org",false],
["authorityInfoAccess","caIssuers;URI:http://www.example.com/caIssuers,OCSP;URI:http://www.example.com/ocsp",false],
]
ee1_cert = issue_cert(@ee1, @rsa1024, 2, ee1_exts, ca_cert, @rsa2048)
assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der)
Expand All @@ -114,16 +115,41 @@ def test_extension
cdp_cert.crl_uris
)

no_exts_cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
ef = OpenSSL::X509::ExtensionFactory.new
aia_cert = generate_cert(@ee1, @rsa1024, 4, ca_cert)
ef.subject_certificate = aia_cert
aia_cert.add_extension(
ef.create_extension(
"authorityInfoAccess",
"caIssuers;URI:http://www.example.com/caIssuers," \
"caIssuers;URI:ldap://ldap.example.com/cn=ca?authorityInfoAccessCaIssuers;binary," \
"OCSP;URI:http://www.example.com/ocsp," \
"OCSP;URI:ldap://ldap.example.com/cn=ca?authorityInfoAccessOcsp;binary",
false
)
)
aia_cert.sign(@rsa2048, "sha256")
assert_equal(
["http://www.example.com/caIssuers", "ldap://ldap.example.com/cn=ca?authorityInfoAccessCaIssuers;binary"],
aia_cert.ca_issuer_uris
)
assert_equal(
["http://www.example.com/ocsp", "ldap://ldap.example.com/cn=ca?authorityInfoAccessOcsp;binary"],
aia_cert.ocsp_uris
)

no_exts_cert = issue_cert(@ca, @rsa2048, 5, [], nil, nil)
assert_equal nil, no_exts_cert.authority_key_identifier
assert_equal nil, no_exts_cert.subject_key_identifier
assert_equal nil, no_exts_cert.crl_uris
assert_equal nil, no_exts_cert.ca_issuer_uris
assert_equal nil, no_exts_cert.ocsp_uris
end

def test_invalid_extension
integer = OpenSSL::ASN1::Integer.new(0)
invalid_exts_cert = generate_cert(@ee1, @rsa1024, 1, nil)
["subjectKeyIdentifier", "authorityKeyIdentifier", "crlDistributionPoints"].each do |ext|
["subjectKeyIdentifier", "authorityKeyIdentifier", "crlDistributionPoints", "authorityInfoAccess"].each do |ext|
invalid_exts_cert.add_extension(
OpenSSL::X509::Extension.new(ext, integer.to_der)
)
Expand All @@ -138,6 +164,12 @@ def test_invalid_extension
assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
invalid_exts_cert.crl_uris
}
assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
invalid_exts_cert.ca_issuer_uris
}
assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
invalid_exts_cert.ocsp_uris
}
end

def test_sign_and_verify_rsa_sha1
Expand Down