Module: Acme::Client::Util
Constant Summary collapse
- LINK_MATCH =
/<(.*?)>\s?;\s?rel="([\w-]+)"/
Instance Method Summary collapse
-
#ari_certificate_identifier(certificate) ⇒ Object
Generates a certificate identifier for ACME Renewal Information (ARI) as per RFC 9773.
-
#decode_link_headers(link_header) ⇒ Object
See RFC 8288 - tools.ietf.org/html/rfc8288#section-3.
-
#parse_retry_after(value) ⇒ Object
Parses a Retry-After header value into a Time.
-
#set_public_key(obj, priv) ⇒ Object
Sets public key on CSR or cert.
- #urlsafe_base64(data) ⇒ Object
Instance Method Details
#ari_certificate_identifier(certificate) ⇒ Object
Generates a certificate identifier for ACME Renewal Information (ARI) as per RFC 9773. The identifier is constructed by extracting the Authority Key Identifier (AKI) from the certificate extension, and the DER-encoded serial number (without tag and length bytes). Both values are base64url-encoded and concatenated with a period separator.
certificate - An OpenSSL::X509::Certificate instance or PEM string.
Returns a string in the format: base64url(AKI).base64url(serial)
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/acme/client/util.rb', line 62 def ari_certificate_identifier(certificate) cert = if certificate.is_a?(OpenSSL::X509::Certificate) certificate else OpenSSL::X509::Certificate.new(certificate) end aki_ext = cert.extensions.find { |ext| ext.oid == 'authorityKeyIdentifier' } raise ArgumentError, 'Certificate does not have an Authority Key Identifier extension' unless aki_ext aki_value = aki_ext.value hex_string = if aki_value =~ /keyid:([0-9A-Fa-f:]+)/ $1 elsif aki_value =~ /^[0-9A-Fa-f:]+$/ aki_value else raise ArgumentError, 'Could not parse Authority Key Identifier' end key_identifier = hex_string.split(':').map { |hex| hex.to_i(16).chr }.join serial_der = OpenSSL::ASN1::Integer.new(cert.serial).to_der serial_value = OpenSSL::ASN1.decode(serial_der).value.to_s(2) aki_b64 = urlsafe_base64(key_identifier) serial_b64 = urlsafe_base64(serial_value) "#{aki_b64}.#{serial_b64}" end |
#decode_link_headers(link_header) ⇒ Object
See RFC 8288 - tools.ietf.org/html/rfc8288#section-3
29 30 31 32 33 34 35 |
# File 'lib/acme/client/util.rb', line 29 def decode_link_headers(link_header) link_header.split(',').each_with_object({}) { |entry, hash| _, link, name = *entry.match(LINK_MATCH) hash[name] ||= [] hash[name].push(link) } end |
#parse_retry_after(value) ⇒ Object
Parses a Retry-After header value into a Time. RFC 7231 ยง7.1.3: the value is either delay-seconds or an HTTP-date. Returns a Time, or nil if the value is nil or unparseable.
9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/acme/client/util.rb', line 9 def parse_retry_after(value) return nil if value.nil? value = value.to_s Integer(value, 10).then { |seconds| Time.now + seconds } rescue ArgumentError, RangeError begin Time.httpdate(value) rescue ArgumentError nil end end |
#set_public_key(obj, priv) ⇒ Object
Sets public key on CSR or cert.
obj - An OpenSSL::X509::Certificate or OpenSSL::X509::Request instance. priv - An OpenSSL::PKey::EC or OpenSSL::PKey::RSA instance.
Returns nothing.
43 44 45 46 47 48 49 50 51 52 |
# File 'lib/acme/client/util.rb', line 43 def set_public_key(obj, priv) case priv when OpenSSL::PKey::EC obj.public_key = priv when OpenSSL::PKey::RSA obj.public_key = priv.public_key else raise ArgumentError, 'priv must be EC or RSA' end end |
#urlsafe_base64(data) ⇒ Object
22 23 24 |
# File 'lib/acme/client/util.rb', line 22 def urlsafe_base64(data) Base64.urlsafe_encode64(data).sub(/[\s=]*\z/, '') end |