Class: LetsCert::Certificate
- Inherits:
-
Object
- Object
- LetsCert::Certificate
- Includes:
- Loggable
- Defined in:
- lib/letscert/certificate.rb
Overview
Class to handle ACME operations on certificates
Instance Attribute Summary collapse
- #cert ⇒ OpenSSL::X509::Certificate? readonly
-
#chain ⇒ Array<OpenSSL::X509::Certificate>
readonly
Certification chain.
- #client ⇒ Acme::Client? readonly
Instance Method Summary collapse
-
#get(account_key, key, options) ⇒ void
Get a new certificate, or renew an existing one.
-
#get_acme_client(account_key, options) {|@client| ... } ⇒ Acme::Client
Get ACME client.
-
#initialize(cert) ⇒ Certificate
constructor
A new instance of Certificate.
-
#revoke(account_key, options = {}) ⇒ Boolean
Revoke certificate.
-
#valid?(domains, valid_min = 0) ⇒ Boolean
Check if certificate is still valid for at least
valid_minseconds.
Methods included from Loggable
Constructor Details
#initialize(cert) ⇒ Certificate
Returns a new instance of Certificate.
44 45 46 47 |
# File 'lib/letscert/certificate.rb', line 44 def initialize(cert) @cert = cert @chain = [] end |
Instance Attribute Details
#cert ⇒ OpenSSL::X509::Certificate? (readonly)
36 37 38 |
# File 'lib/letscert/certificate.rb', line 36 def cert @cert end |
#chain ⇒ Array<OpenSSL::X509::Certificate> (readonly)
Certification chain. Only set by #get.
39 40 41 |
# File 'lib/letscert/certificate.rb', line 39 def chain @chain end |
#client ⇒ Acme::Client? (readonly)
41 42 43 |
# File 'lib/letscert/certificate.rb', line 41 def client @client end |
Instance Method Details
#get(account_key, key, options) ⇒ void
This method returns an undefined value.
Get a new certificate, or renew an existing one
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/letscert/certificate.rb', line 70 def get(account_key, key, ) logger.info { 'create key/cert/chain...' } check_roots([:roots]) logger.debug { "webroots are: #{[:roots].inspect}" } account_key = get_account_key(account_key, [:account_key_type], [:account_key_size]) client = get_acme_client(account_key, ) do_challenges client, [:roots] pkey = if [:reuse_key] raise Error, 'cannot reuse a non-existing key' if key.nil? logger.info { 'Reuse existing private key' } generate_certificate_from_pkey [:roots].keys, key else logger.info { 'Generate new private key' } generate_certificate [:roots].keys, end [:files] ||= [] [:files].each do |plugname| IOPlugin.registered[plugname].save(account_key: account_key, key: pkey, cert: @cert, chain: @chain) end end |
#get_acme_client(account_key, options) {|@client| ... } ⇒ Acme::Client
Get ACME client.
Client is only created on first call, then it is cached.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/letscert/certificate.rb', line 160 def get_acme_client(account_key, ) return @client if @client logger.debug { "connect to #{[:server]}" } @client = Acme::Client.new(private_key: account_key, endpoint: [:server]) yield @client if block_given? if [:email].nil? logger.warn { '--email was not provided. ACME CA will have no way to ' \ 'contact you!' } end begin logger.debug { "register with #{[:email]}" } registration = @client.register(contact: "mailto:#{[:email]}") rescue Acme::Client::Error::Malformed => ex raise if ex. != 'Registration key is already in use' else # Requesting ToS make acme-client throw an exception: Connection reset # by peer (Faraday::ConnectionFailed). To investigate... #if registration.term_of_service_uri # @logger.debug { "get terms of service" } # terms = registration.get_terms # if !terms.nil? # tos_digest = OpenSSL::Digest::SHA256.digest(terms) # if tos_digest != @options[:tos_sha256] # raise Error, 'Terms Of Service mismatch' # end @logger.debug { 'agree terms of service' } registration.agree_terms # end #end end @client end |
#revoke(account_key, options = {}) ⇒ Boolean
Revoke certificate
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/letscert/certificate.rb', line 109 def revoke(account_key, = {}) raise Error, 'no certification data to revoke' if @cert.nil? client = get_acme_client(account_key, ) result = client.revoke_certificate(@cert) if result logger.info { 'certificate is revoked' } else logger.warn { 'certificate is not revoked!' } end result end |
#valid?(domains, valid_min = 0) ⇒ Boolean
Check if certificate is still valid for at least valid_min seconds. Also checks that domains are certified by certificate.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/letscert/certificate.rb', line 130 def valid?(domains, valid_min = 0) if @cert.nil? logger.debug { 'no existing certificate' } return false end subjects = [] @cert.extensions.each do |ext| if ext.oid == 'subjectAltName' subjects += ext.value.split(/,\s*/).map { |s| s.sub(/DNS:/, '') } end end logger.debug { "cert SANs: #{subjects.join(', ')}" } # Check all domains are subjects of certificate unless domains.all? { |domain| subjects.include? domain } msg = 'At least one domain is not declared as a certificate subject. ' \ 'Backup and remove existing cert if you want to proceed.' raise Error, msg end !renewal_necessary?(valid_min) end |