Module: CsrPeek
- Defined in:
- lib/csr_peek.rb,
lib/csr_peek/csr.rb,
lib/csr_peek/names.rb,
lib/csr_peek/policy.rb,
lib/csr_peek/version.rb,
lib/csr_peek/key_facts.rb,
lib/csr_peek/extensions.rb,
lib/csr_peek/certificate.rb,
lib/csr_peek/inspectable.rb
Overview
CsrPeek reads certificate signing requests and X.509 certificates and hands back the useful parts safely: subject, common name, Subject Alternative Names, key type and size, and public-key fingerprints. Malformed input returns nil rather than raising, so a bad upload never becomes a 500.
csr = CsrPeek.parse(pem_string)
csr&.common_name # => "example.com"
csr&.dns_names # => ["example.com", "www.example.com"]
csr&.weak_key? # => false
When you want to know why something failed to parse, use the bang variants (parse!, parse_certificate!), which raise CsrPeek::ParseError with a reason.
Every name and SAN value comes straight from the (attacker-controlled) input and may contain control characters, newlines, or markup. Escape it before rendering it into HTML, logs, or shell - CsrPeek reports faithfully, it does not sanitize.
Defined Under Namespace
Modules: Extensions, Inspectable, KeyFacts, Names Classes: Certificate, Csr, Error, ParseError, Policy
Constant Summary collapse
- MAX_INPUT_BYTES =
Upper bound on the input we will look at, so untrusted data cannot exhaust memory or CPU. A real CSR or certificate is a few kilobytes; a full chain is tens. 1 MiB is very generous while still bounding the work.
1 << 20
- MAX_CHAIN_CERTIFICATES =
Cap on the number of certificates parse_certificates will return from one bundle, so a pathological input cannot make us parse unboundedly many.
100- BEGIN_CERTIFICATE =
"-----BEGIN CERTIFICATE-----"- END_CERTIFICATE =
"-----END CERTIFICATE-----"- EXT_REQUEST_OIDS =
OIDs under which a CSR carries its requested X.509 extensions.
%w[extReq msExtReq].freeze
- VERSION =
"0.1.0"
Class Method Summary collapse
-
.parse(input) ⇒ Object
Parse a PEM or DER CSR.
-
.parse!(input) ⇒ Object
Like #parse, but raises CsrPeek::ParseError instead of returning nil.
-
.parse_certificate(input) ⇒ Object
Parse a PEM or DER X.509 certificate.
-
.parse_certificate!(input) ⇒ Object
Like #parse_certificate, but raises CsrPeek::ParseError instead of nil.
-
.parse_certificates(input) ⇒ Object
Parse every certificate in a PEM bundle (a chain, a fullchain.pem), in file order, as an array of CsrPeek::Certificate.
Class Method Details
.parse(input) ⇒ Object
Parse a PEM or DER CSR. Returns a CsrPeek::Csr, or nil on invalid input.
54 55 56 57 58 |
# File 'lib/csr_peek.rb', line 54 def parse(input) parse!(input) rescue ParseError nil end |
.parse!(input) ⇒ Object
Like #parse, but raises CsrPeek::ParseError instead of returning nil.
61 62 63 64 65 66 67 68 69 |
# File 'lib/csr_peek.rb', line 61 def parse!(input) validate_size!(input) raise ParseError, "input is empty" if blank?(input) request = decode(input, "certificate signing request") do OpenSSL::X509::Request.new(input) end Csr.from_openssl(request) end |
.parse_certificate(input) ⇒ Object
Parse a PEM or DER X.509 certificate. Returns a CsrPeek::Certificate, or nil on invalid input.
73 74 75 76 77 |
# File 'lib/csr_peek.rb', line 73 def parse_certificate(input) parse_certificate!(input) rescue ParseError nil end |
.parse_certificate!(input) ⇒ Object
Like #parse_certificate, but raises CsrPeek::ParseError instead of nil.
80 81 82 83 84 85 86 87 88 |
# File 'lib/csr_peek.rb', line 80 def parse_certificate!(input) validate_size!(input) raise ParseError, "input is empty" if blank?(input) certificate = decode(input, "X.509 certificate") do OpenSSL::X509::Certificate.new(input) end Certificate.from_openssl(certificate) end |
.parse_certificates(input) ⇒ Object
Parse every certificate in a PEM bundle (a chain, a fullchain.pem), in file order, as an array of CsrPeek::Certificate. Unparseable blocks are skipped. A single PEM or DER certificate yields a one-element array; junk yields []. Use this instead of #parse_certificate, which only sees the first block.
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/csr_peek.rb', line 94 def parse_certificates(input) return [] unless within_size?(input) return [] if blank?(input) certificates = extract_pem_certificates(input.to_s) return certificates unless certificates.empty? # No PEM blocks at all: try the whole input as one (DER) certificate. cert = parse_certificate(input) cert ? [cert] : [] end |