main
1# frozen_string_literal: true
2
3require 'xml/kit/templatable'
4
5module Xml
6 module Kit
7 # {include:file:spec/xml/kit/certificate_spec.rb}
8 class Certificate
9 include Templatable
10 BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z).freeze
11 BEGIN_CERT = /-----BEGIN CERTIFICATE-----/.freeze
12 END_CERT = /-----END CERTIFICATE-----/.freeze
13 # The use can be `:signing` or `:encryption`. Use `nil` for both.
14 attr_reader :use
15
16 # The raw certificate value. This can be a Base64 encoded PEM or just a PEM format.
17 attr_reader :value
18
19 def initialize(value, use: nil)
20 @value = value
21 @use = use.nil? ? use : use.downcase.to_sym
22 end
23
24 # @return [Xml::Kit::Fingerprint] the certificate fingerprint.
25 def fingerprint
26 Fingerprint.new(value)
27 end
28
29 # Returns true if this certificate is for the specified use.
30 #
31 # @param use [Symbol] `:signing` or `:encryption`.
32 # @return [Boolean] true or false.
33 def for?(use)
34 return true if self.use.nil?
35
36 self.use == use.to_sym
37 end
38
39 # Returns true if this certificate is used for encryption.
40 #
41 # return [Boolean] true or false.
42 def encryption?
43 for?(:encryption)
44 end
45
46 # Returns true if this certificate is used for signing.
47 #
48 # return [Boolean] true or false.
49 def signing?
50 for?(:signing)
51 end
52
53 # Returns the x509 form.
54 #
55 # return [OpenSSL::X509::Certificate] the OpenSSL equivalent.
56 def x509
57 @x509 ||= self.class.to_x509(value)
58 end
59
60 # Returns the public key.
61 #
62 # @return [OpenSSL::PKey::RSA] the RSA public key.
63 def public_key
64 x509.public_key
65 end
66
67 def ==(other)
68 fingerprint == other.fingerprint
69 end
70
71 def eql?(other)
72 self == other
73 end
74
75 def hash
76 value.hash
77 end
78
79 def to_s
80 value
81 end
82
83 def to_h
84 { use: @use, fingerprint: fingerprint.to_s }
85 end
86
87 def inspect
88 to_h.inspect
89 end
90
91 def stripped
92 self.class.strip(x509.to_pem)
93 end
94
95 def to_key_pair(private_key, passphrase: nil, use: nil)
96 KeyPair.new(x509.to_pem, private_key.to_s, passphrase, use)
97 end
98
99 def expired?(time = Time.now)
100 x509.not_after <= time
101 end
102
103 def active?(time = Time.now)
104 x509.not_before <= time && !expired?(time)
105 end
106
107 def not_after
108 x509.not_after
109 end
110
111 def not_before
112 x509.not_before
113 end
114
115 def key_info
116 @key_info ||= KeyInfo.new(x509: x509)
117 end
118
119 class << self
120 def to_x509(value)
121 return value if value.is_a?(OpenSSL::X509::Certificate)
122
123 value = Base64.decode64(strip(value)) if base64?(value)
124 OpenSSL::X509::Certificate.new(value)
125 end
126
127 def base64?(value)
128 return unless value.is_a?(String)
129
130 sanitized_value = strip(value)
131 !!sanitized_value.match(BASE64_FORMAT)
132 end
133
134 def strip(value)
135 value
136 .gsub(BEGIN_CERT, '')
137 .gsub(END_CERT, '')
138 .gsub(/[\r\n]|\\r|\\n|\s/, '')
139 end
140 end
141 end
142 end
143end