Class: CsrPeek::Certificate

Inherits:
Data
  • Object
show all
Includes:
Inspectable
Defined in:
lib/csr_peek/certificate.rb

Overview

A friendly, read-only view over an OpenSSL::X509::Certificate.

An immutable value object: build one with CsrPeek.parse_certificate (which returns nil for junk input) or Certificate.from_openssl. Parsed facts are resolved once into frozen members. This class inspects; it does not build trust chains or perform revocation checks.

Constant Summary

Constants included from Inspectable

Inspectable::MIN_DSA_BITS, Inspectable::MIN_EC_BITS, Inspectable::MIN_RSA_BITS, Inspectable::STRONG_UNSIZED_TYPES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Inspectable

#==, #acceptable_key?, #all_names, #common_name, #dns_names, #fingerprint, #hash, #inspect, #ip_addresses, #key_policy_violations, #spki_fingerprint, #weak_key?

Instance Attribute Details

#basic_constraintsObject (readonly)

Returns the value of attribute basic_constraints

Returns:

  • (Object)

    the current value of basic_constraints



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def basic_constraints
  @basic_constraints
end

#ec_curveObject (readonly)

Returns the value of attribute ec_curve

Returns:

  • (Object)

    the current value of ec_curve



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def ec_curve
  @ec_curve
end

#extended_key_usageObject (readonly)

Returns the value of attribute extended_key_usage

Returns:

  • (Object)

    the current value of extended_key_usage



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def extended_key_usage
  @extended_key_usage
end

#issuerObject (readonly)

Returns the value of attribute issuer

Returns:

  • (Object)

    the current value of issuer



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def issuer
  @issuer
end

#key_bitsObject (readonly)

Returns the value of attribute key_bits

Returns:

  • (Object)

    the current value of key_bits



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def key_bits
  @key_bits
end

#key_typeObject (readonly)

Returns the value of attribute key_type

Returns:

  • (Object)

    the current value of key_type



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def key_type
  @key_type
end

#key_usageObject (readonly)

Returns the value of attribute key_usage

Returns:

  • (Object)

    the current value of key_usage



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def key_usage
  @key_usage
end

#opensslObject (readonly)

Returns the value of attribute openssl

Returns:

  • (Object)

    the current value of openssl



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def openssl
  @openssl
end

#public_keyObject (readonly)

Returns the value of attribute public_key

Returns:

  • (Object)

    the current value of public_key



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def public_key
  @public_key
end

#subjectObject (readonly)

Returns the value of attribute subject

Returns:

  • (Object)

    the current value of subject



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def subject
  @subject
end

#subject_alt_namesObject (readonly)

Returns the value of attribute subject_alt_names

Returns:

  • (Object)

    the current value of subject_alt_names



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def subject_alt_names
  @subject_alt_names
end

#subject_componentsObject (readonly)

Returns the value of attribute subject_components

Returns:

  • (Object)

    the current value of subject_components



17
18
19
# File 'lib/csr_peek/certificate.rb', line 17

def subject_components
  @subject_components
end

Class Method Details

.from_openssl(cert) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/csr_peek/certificate.rb', line 24

def self.from_openssl(cert)
  pkey = load_public_key(cert)
  facts = KeyFacts.of(pkey)
  new(
    openssl: cert,
    public_key: pkey,
    key_type: facts[:type],
    key_bits: facts[:bits],
    ec_curve: facts[:curve],
    subject: Names.subject_to_h(cert.subject).freeze,
    subject_components: Names.components(cert.subject).freeze,
    subject_alt_names: Extensions.subject_alt_names(find_ext(cert, "subjectAltName")),
    issuer: Names.subject_to_h(cert.issuer).freeze,
    key_usage: Extensions.key_usages(find_ext(cert, "keyUsage")).freeze,
    extended_key_usage: Extensions.extended_key_usages(find_ext(cert, "extendedKeyUsage")).freeze,
    basic_constraints: Extensions.basic_constraints(find_ext(cert, "basicConstraints")).freeze
  )
end

Instance Method Details

#acceptable?(policy = Policy::BASELINE) ⇒ Boolean

True when both the key and the signature satisfy the issuance policy.

Returns:

  • (Boolean)


111
112
113
# File 'lib/csr_peek/certificate.rb', line 111

def acceptable?(policy = Policy::BASELINE)
  acceptable_key?(policy) && !weak_signature?(policy)
end

#ca?Boolean

Whether this certificate may act as a CA (basicConstraints CA:TRUE).

Returns:

  • (Boolean)


124
125
126
# File 'lib/csr_peek/certificate.rb', line 124

def ca?
  basic_constraints[:ca]
end

#expired?(now = Time.now) ⇒ Boolean

True when now is after not_after. "expired" means the window has passed; a certificate whose window has not begun is #not_yet_valid?, not expired.

Returns:

  • (Boolean)


84
85
86
# File 'lib/csr_peek/certificate.rb', line 84

def expired?(now = Time.now)
  now > not_after
end

#not_afterObject



73
74
75
# File 'lib/csr_peek/certificate.rb', line 73

def not_after
  openssl.not_after
end

#not_beforeObject



69
70
71
# File 'lib/csr_peek/certificate.rb', line 69

def not_before
  openssl.not_before
end

#not_yet_valid?(now = Time.now) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/csr_peek/certificate.rb', line 88

def not_yet_valid?(now = Time.now)
  now < not_before
end

#path_lengthObject

The pathLenConstraint from basicConstraints, or nil when unset.



129
130
131
# File 'lib/csr_peek/certificate.rb', line 129

def path_length
  basic_constraints[:path_length]
end

#policy_violations(policy = Policy::BASELINE) ⇒ Object

All policy reasons this certificate is unacceptable, as symbols; empty when it passes. Combines key violations with :weak_signature.



117
118
119
120
121
# File 'lib/csr_peek/certificate.rb', line 117

def policy_violations(policy = Policy::BASELINE)
  violations = key_policy_violations(policy)
  violations << :weak_signature if weak_signature?(policy)
  violations
end

#self_signed?Boolean

Returns:

  • (Boolean)


92
93
94
95
96
# File 'lib/csr_peek/certificate.rb', line 92

def self_signed?
  openssl.subject == openssl.issuer && openssl.verify(public_key)
rescue OpenSSL::X509::CertificateError, OpenSSL::PKey::PKeyError, TypeError
  false
end

#serialObject

Serial as lower-case hex, zero-padded to an even number of digits.



58
59
60
61
# File 'lib/csr_peek/certificate.rb', line 58

def serial
  hex = openssl.serial.to_s(16).downcase
  hex.length.odd? ? "0#{hex}" : hex
end

#signature_algorithmObject

The signature algorithm name, e.g. "sha256WithRSAEncryption".



99
100
101
102
103
# File 'lib/csr_peek/certificate.rb', line 99

def signature_algorithm
  openssl.signature_algorithm
rescue OpenSSL::X509::CertificateError
  nil
end

#to_hObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/csr_peek/certificate.rb', line 133

def to_h
  {
    subject: subject,
    issuer: issuer,
    common_name: common_name,
    serial: serial,
    version: version,
    not_before: not_before,
    not_after: not_after,
    expired: expired?,
    not_yet_valid: not_yet_valid?,
    self_signed: self_signed?,
    ca: ca?,
    path_length: path_length,
    key_usage: key_usage,
    extended_key_usage: extended_key_usage,
    dns_names: dns_names,
    ip_addresses: ip_addresses,
    all_names: all_names,
    key_type: key_type,
    key_bits: key_bits,
    weak_key: weak_key?,
    acceptable: acceptable?,
    signature_algorithm: signature_algorithm,
    weak_signature: weak_signature?,
    spki_fingerprint_sha256: spki_fingerprint(:sha256),
    fingerprint_sha256: fingerprint(:sha256)
  }
end

#valid_at?(now = Time.now) ⇒ Boolean

True when now falls within [not_before, not_after].

Returns:

  • (Boolean)


78
79
80
# File 'lib/csr_peek/certificate.rb', line 78

def valid_at?(now = Time.now)
  now.between?(not_before, not_after)
end

#versionObject

X.509 version as the human-facing number: 1, 2, or 3 (the DER field is zero-based, so v3 is stored as 2).



65
66
67
# File 'lib/csr_peek/certificate.rb', line 65

def version
  openssl.version + 1
end

#weak_signature?(policy = Policy::BASELINE) ⇒ Boolean

True when signed with a hash the policy rejects (MD5/SHA-1 by default).

Returns:

  • (Boolean)


106
107
108
# File 'lib/csr_peek/certificate.rb', line 106

def weak_signature?(policy = Policy::BASELINE)
  policy.weak_signature?(signature_algorithm)
end