Skip to content

Commit cbad745

Browse files
committed
Inherit Certificate#name on renewal
1 parent aef54d0 commit cbad745

File tree

3 files changed

+31
-20
lines changed

3 files changed

+31
-20
lines changed

lib/acmesmith/certificate.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@ def self.split_pems(pems)
1717
# Return Acmesmith::Certificate by an issued certificate
1818
# @param pem_chain [String]
1919
# @param csr [Acme::Client::CertificateRequest]
20+
# @param name [String, nil]
2021
# @return [Acmesmith::Certificate]
21-
def self.by_issuance(pem_chain, csr)
22+
def self.by_issuance(pem_chain, csr, name: nil)
2223
pems = split_pems(pem_chain)
23-
new(pems[0], pems[1..-1], csr.private_key, nil, csr)
24+
new(pems[0], pems[1..-1], csr.private_key, nil, csr, name: name)
2425
end
2526

2627
# @param certificate [OpenSSL::X509::Certificate, String]
2728
# @param chain [String, Array<String>, Array<OpenSSL::X509::Certificate>]
2829
# @param private_key [String, OpenSSL::PKey::PKey]
2930
# @param key_passphrase [String, nil]
3031
# @param csr [String, OpenSSL::X509::Request, nil]
31-
def initialize(certificate, chain, private_key, key_passphrase = nil, csr = nil)
32+
# @param name [String, nil]
33+
def initialize(certificate, chain, private_key, key_passphrase = nil, csr = nil, name: nil)
34+
@name = name
3235
@certificate = case certificate
3336
when OpenSSL::X509::Certificate
3437
certificate
@@ -94,6 +97,8 @@ def initialize(certificate, chain, private_key, key_passphrase = nil, csr = nil)
9497
# @return [OpenSSL::X509::Request]
9598
attr_reader :csr
9699

100+
attr_writer :name
101+
97102
# Try to decrypt private_key if encrypted.
98103
# @param pw [String] passphrase for encrypted PEM
99104
# @raise [PrivateKeyDecrypted] if private_key is decrypted
@@ -132,7 +137,7 @@ def issuer_pems
132137
# Note that this value can contain colons (':') if name is taken from non-DNS subject alternative name.
133138
# @return [String] certificate name
134139
def name
135-
common_name || sans.first || all_sans.first
140+
@name || common_name || sans.first || all_sans.first
136141
end
137142

138143
# Returns a certificate common name taken from the certificate subject's CN field.

lib/acmesmith/client.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def authorize(*identifiers)
3131
end
3232

3333
def post_issue_hooks(name)
34-
cert = storage.get_certificate(name)
34+
cert = load_certificate_from_storage(name)
3535
execute_post_issue_hooks(cert)
3636
end
3737

@@ -125,7 +125,8 @@ def save(name, version: 'current', **kwargs)
125125
def autorenew(days: 30, remaining_life: nil, names: nil)
126126
(names || storage.list_certificates).each do |cn|
127127
puts "=> #{cn}"
128-
cert = storage.get_certificate(cn)
128+
cert = load_certificate_from_storage(cn)
129+
129130
not_after = cert.certificate.not_after.utc
130131

131132
lifetime = cert.certificate.not_after.utc - cert.certificate.not_before.utc
@@ -139,14 +140,14 @@ def autorenew(days: 30, remaining_life: nil, names: nil)
139140
puts " Not valid after: #{not_after} (lifetime=#{format_duration(lifetime+1)}, remaining=#{format_duration(remaining)}, #{"%0.2f" % (ratio.to_f*100)}%)"
140141
next unless has_to_renew
141142

142-
puts " * Renewing: CN=#{cert.name}, SANs=#{cert.sans.join(',')}"
143+
puts " * Renewing: #{cert.name.inspect}, SANs=#{cert.sans.join(',')}"
143144
order_with_private_key(cert.name, *cert.sans, private_key: regenerate_private_key(cert.public_key))
144145
end
145146
end
146147

147148
def add_san(name, *add_sans)
148-
puts "=> reissuing CN=#{name} with new SANs #{add_sans.join(?,)}"
149-
cert = storage.get_certificate(name)
149+
puts "=> reissuing #{name.inspect} with new SANs #{add_sans.join(?,)}"
150+
cert = load_certificate_from_storage(name)
150151
sans = cert.sans + add_sans
151152
puts " * SANs will be: #{sans.join(?,)}"
152153
order_with_private_key(cert.name, *sans, private_key: regenerate_private_key(cert.public_key))
@@ -212,10 +213,11 @@ def account_key_passphrase
212213
end
213214
end
214215

215-
def order_with_private_key(*identifiers, private_key:, not_before: nil, not_after: nil)
216+
def order_with_private_key(name, *identifiers, private_key:, not_before: nil, not_after: nil)
216217
order = OrderingService.new(
217218
acme: acme,
218-
identifiers: identifiers,
219+
common_name: name,
220+
identifiers: [name, *identifiers],
219221
private_key: private_key,
220222
challenge_responder_rules: config.challenge_responders,
221223
chain_preferences: config.chain_preferences,
@@ -258,5 +260,12 @@ def regenerate_private_key(template)
258260
raise ArgumentError, "Unknown key type: #{template.class}"
259261
end
260262
end
263+
264+
# Load certificate from storage, inherit name property to loaded certificate to ensure stability of #name during renewal.
265+
def load_certificate_from_storage(name)
266+
retval = storage.get_certificate(name)
267+
retval.name = name
268+
retval
269+
end
261270
end
262271
end

lib/acmesmith/ordering_service.rb

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ class OrderingService
77
class NotCompleted < StandardError; end
88

99
# @param acme [Acme::Client] ACME client
10-
# @param identifiers [Array<String>] Array of domain names for a ordering certificate. The first item will be a common name.
10+
# @param common_name [String] Common Name for a ordering certificate
11+
# @param identifiers [Array<String>] Array of domain names for a ordering certificate. common_name has to be explicitly included in this argument.
1112
# @param private_key [OpenSSL::PKey::PKey] Private key
1213
# @param challenge_responder_rules [Array<Acmesmith::Config::ChallengeResponderRule>] responders
1314
# @param chain_preferences [Array<Acmesmith::Config::ChainPreference>] chain_preferences
1415
# @param not_before [Time]
1516
# @param not_after [Time]
16-
def initialize(acme:, identifiers:, private_key:, challenge_responder_rules:, chain_preferences:, not_before: nil, not_after: nil)
17+
def initialize(acme:, common_name:, identifiers:, private_key:, challenge_responder_rules:, chain_preferences:, not_before: nil, not_after: nil)
1718
@acme = acme
19+
@common_name = common_name
1820
@identifiers = identifiers
1921
@private_key = private_key
2022
@challenge_responder_rules = challenge_responder_rules
@@ -23,7 +25,7 @@ def initialize(acme:, identifiers:, private_key:, challenge_responder_rules:, ch
2325
@not_after = not_after
2426
end
2527

26-
attr_reader :acme, :identifiers, :private_key, :challenge_responder_rules, :chain_preferences, :not_before, :not_after
28+
attr_reader :acme, :common_name, :identifiers, :private_key, :challenge_responder_rules, :chain_preferences, :not_before, :not_after
2729

2830
def perform!
2931
puts "=> Ordering a certificate for the following identifiers:"
@@ -43,7 +45,7 @@ def perform!
4345
finalize_order()
4446
wait_order_for_complete()
4547

46-
@certificate = Certificate.by_issuance(pem_chain, csr)
48+
@certificate = Certificate.by_issuance(pem_chain, csr, name: common_name)
4749

4850
puts
4951
puts "=> Certificate issued"
@@ -97,11 +99,6 @@ def order
9799
@order or raise "BUG: order not yet generated"
98100
end
99101

100-
# @return [String]
101-
def common_name
102-
identifiers.first
103-
end
104-
105102
# @return [Array<String>]
106103
def sans
107104
identifiers[1..-1]

0 commit comments

Comments
 (0)