acmesmith 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18161ba6306c7d98accbbdfdec37599c67c8ba6e3d84a75e2ce52dc7cf2561b6
4
- data.tar.gz: 4fdb29425b0e3a9c998bd86ad755c46dd01244936d718531309ebac17d57e4bb
3
+ metadata.gz: 914be0de4c237bcd0db59a908f8ce70c3f7b364bb2ab0ac25b6e26ff95ecd7bc
4
+ data.tar.gz: 14b96550d4ef0274d1546e78b5121c6b9356e9c71e479a4ed17b4d9ff8fc669e
5
5
  SHA512:
6
- metadata.gz: 292b84e68ccaa7e7e3f946cdd7e3caaf281155085cd1813e2e4903f8a9737c67b35e8dd8b46ba95fa023865b6e329cd3be923da54a48f6220148d0cfa086e719
7
- data.tar.gz: 8c25c548b5c4dcc11afe50279773f9991f22f750b2ea23fbe7be5f38f8e7ece8d04dec7c2705d11c7ecaebdad7669c77fe1af9178f4bfda2d55f8ca4197167c6
6
+ metadata.gz: ce305e972fcb06a4bc97b2ceea2177cd759601c55064dd5e22ea15b0981ea838e7c11d8e5401d08c41ec1f249cfa0d7a6489f67b2c3e52cc8d67bd1871d77e69
7
+ data.tar.gz: dfabb130f347dc7829cbaad1b5de6aca553dfedc7172d53d163736baee2b07b00124ef7f5f31d9270df660f4b9381b05ed6ca6cfa2e9a96163f25d6fa2d83286
@@ -18,7 +18,7 @@ jobs:
18
18
  strategy:
19
19
  fail-fast: false
20
20
  matrix:
21
- ruby-version: ['2.7', '3.0', '3.1']
21
+ ruby-version: ['3.0', '3.1', '3.2']
22
22
  container:
23
23
  image: public.ecr.aws/sorah/ruby:${{ matrix.ruby-version }}-dev
24
24
  steps:
@@ -40,7 +40,7 @@ jobs:
40
40
  strategy:
41
41
  fail-fast: false
42
42
  matrix:
43
- ruby-version: ['2.7', '3.0', '3.1']
43
+ ruby-version: ['3.0', '3.1', '3.2']
44
44
 
45
45
  # FIXME: once GitHub Actions gains support of adding command line arguments to container
46
46
  # services:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## v2.6.0 (2023-10-05)
2
+
3
+ ### Enhancement
4
+
5
+ - order: Gains `--key-type`, `--rsa-key-size`, `--elliptic-curve` options to customize private key generation, and generating EC keys. [#58](https://github.com/sorah/acmesmith/pull/58)
6
+ - autorenew: Respect the existing key configuration when regenerating a fresh key pair for renewal. [#58](https://github.com/sorah/acmesmith/pull/58)
7
+
1
8
  ## v2.5.0 (2020-10-09)
2
9
 
3
10
  ### Enhancement
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM sorah/ruby:2.7-dev as builder
1
+ FROM sorah/ruby:3.2-dev as builder
2
2
 
3
3
  #RUN apt-get update \
4
4
  # && apt-get install -y libmysqlclient-dev git-core \
@@ -12,7 +12,7 @@ RUN sed -i -e 's|Acmesmith::VERSION|"0.0.0"|g' -e '/^require.*acmesmith.version/
12
12
 
13
13
  RUN bundle install --path /gems --jobs 100 --without development
14
14
 
15
- FROM sorah/ruby:2.7
15
+ FROM sorah/ruby:3.2
16
16
 
17
17
  #RUN apt-get update \
18
18
  # && apt-get install -y libmysqlclient20 \
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- acmesmith (2.5.0)
4
+ acmesmith (2.6.0)
5
5
  acme-client (>= 2.0.7, < 3)
6
6
  aws-sdk-acm
7
7
  aws-sdk-route53
@@ -11,43 +11,46 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- acme-client (2.0.11)
14
+ acme-client (2.0.14)
15
15
  faraday (>= 1.0, < 3.0.0)
16
- faraday-retry (~> 1.0)
16
+ faraday-retry (>= 1.0, < 3.0.0)
17
17
  aws-eventstream (1.2.0)
18
- aws-partitions (1.644.0)
19
- aws-sdk-acm (1.52.0)
20
- aws-sdk-core (~> 3, >= 3.127.0)
18
+ aws-partitions (1.832.0)
19
+ aws-sdk-acm (1.62.0)
20
+ aws-sdk-core (~> 3, >= 3.184.0)
21
21
  aws-sigv4 (~> 1.1)
22
- aws-sdk-core (3.159.0)
22
+ aws-sdk-core (3.185.0)
23
23
  aws-eventstream (~> 1, >= 1.0.2)
24
- aws-partitions (~> 1, >= 1.525.0)
25
- aws-sigv4 (~> 1.1)
24
+ aws-partitions (~> 1, >= 1.651.0)
25
+ aws-sigv4 (~> 1.5)
26
26
  jmespath (~> 1, >= 1.6.1)
27
- aws-sdk-kms (1.58.0)
28
- aws-sdk-core (~> 3, >= 3.127.0)
27
+ aws-sdk-kms (1.72.0)
28
+ aws-sdk-core (~> 3, >= 3.184.0)
29
29
  aws-sigv4 (~> 1.1)
30
- aws-sdk-route53 (1.65.0)
31
- aws-sdk-core (~> 3, >= 3.127.0)
30
+ aws-sdk-route53 (1.79.0)
31
+ aws-sdk-core (~> 3, >= 3.184.0)
32
32
  aws-sigv4 (~> 1.1)
33
- aws-sdk-s3 (1.114.0)
34
- aws-sdk-core (~> 3, >= 3.127.0)
33
+ aws-sdk-s3 (1.136.0)
34
+ aws-sdk-core (~> 3, >= 3.181.0)
35
35
  aws-sdk-kms (~> 1)
36
- aws-sigv4 (~> 1.4)
37
- aws-sigv4 (1.5.2)
36
+ aws-sigv4 (~> 1.6)
37
+ aws-sigv4 (1.6.0)
38
38
  aws-eventstream (~> 1, >= 1.0.2)
39
+ base64 (0.1.1)
39
40
  diff-lcs (1.4.4)
40
- faraday (2.6.0)
41
+ faraday (2.7.11)
42
+ base64
41
43
  faraday-net_http (>= 2.0, < 3.1)
42
44
  ruby2_keywords (>= 0.0.4)
43
- faraday-net_http (3.0.1)
44
- faraday-retry (1.0.3)
45
- jmespath (1.6.1)
46
- mini_portile2 (2.8.0)
47
- nokogiri (1.13.3)
45
+ faraday-net_http (3.0.2)
46
+ faraday-retry (2.2.0)
47
+ faraday (~> 2.0)
48
+ jmespath (1.6.2)
49
+ mini_portile2 (2.8.1)
50
+ nokogiri (1.14.3)
48
51
  mini_portile2 (~> 2.8.0)
49
52
  racc (~> 1.4)
50
- racc (1.6.0)
53
+ racc (1.6.2)
51
54
  rake (13.0.6)
52
55
  rspec (3.10.0)
53
56
  rspec-core (~> 3.10.0)
@@ -63,7 +66,7 @@ GEM
63
66
  rspec-support (~> 3.10.0)
64
67
  rspec-support (3.10.2)
65
68
  ruby2_keywords (0.0.5)
66
- thor (1.2.1)
69
+ thor (1.2.2)
67
70
 
68
71
  PLATFORMS
69
72
  ruby
@@ -25,7 +25,7 @@ module Acmesmith
25
25
 
26
26
  # @param certificate [OpenSSL::X509::Certificate, String]
27
27
  # @param chain [String, Array<String>, Array<OpenSSL::X509::Certificate>]
28
- # @param private_key [String, OpenSSL::PKey::RSA]
28
+ # @param private_key [String, OpenSSL::PKey::PKey]
29
29
  # @param key_passphrase [String, nil]
30
30
  # @param csr [String, OpenSSL::X509::Request, nil]
31
31
  def initialize(certificate, chain, private_key, key_passphrase = nil, csr = nil)
@@ -66,15 +66,15 @@ module Acmesmith
66
66
  self.key_passphrase = key_passphrase
67
67
  else
68
68
  begin
69
- @private_key = OpenSSL::PKey::RSA.new(@raw_private_key) { nil }
70
- rescue OpenSSL::PKey::RSAError
69
+ @private_key = OpenSSL::PKey.read(@raw_private_key) { nil }
70
+ rescue OpenSSL::PKey::PKeyError
71
71
  # may be encrypted
72
72
  end
73
73
  end
74
- when OpenSSL::PKey::RSA
74
+ when OpenSSL::PKey::PKey
75
75
  @private_key = private_key
76
76
  else
77
- raise TypeError, 'private_key is expected to be a String or OpenSSL::PKey::RSA'
77
+ raise TypeError, 'private_key is expected to be a String or OpenSSL::PKey::PKey'
78
78
  end
79
79
 
80
80
  @csr = case csr
@@ -100,19 +100,24 @@ module Acmesmith
100
100
  def key_passphrase=(pw)
101
101
  raise PrivateKeyDecrypted, 'private_key already given' if @private_key
102
102
 
103
- @private_key = OpenSSL::PKey::RSA.new(@raw_private_key, pw)
103
+ @private_key = OpenSSL::PKey.read(@raw_private_key, pw)
104
104
 
105
105
  @raw_private_key = nil
106
106
  nil
107
107
  end
108
108
 
109
- # @return [OpenSSL::PKey::RSA]
109
+ # @return [OpenSSL::PKey::PKey]
110
110
  # @raise [PassphraseRequired] if private_key is not yet decrypted
111
111
  def private_key
112
112
  return @private_key if @private_key
113
113
  raise PassphraseRequired, 'key_passphrase required'
114
114
  end
115
115
 
116
+ # @return [OpenSSL::PKey::PKey]
117
+ def public_key
118
+ @certificate.public_key
119
+ end
120
+
116
121
  # @return [String] leaf certificate + full certificate chain
117
122
  def fullchain
118
123
  "#{certificate.to_pem}\n#{issuer_pems}".gsub(/\n+/,?\n)
@@ -21,27 +21,9 @@ module Acmesmith
21
21
  key
22
22
  end
23
23
 
24
- def order(*identifiers, not_before: nil, not_after: nil)
25
- order = OrderingService.new(
26
- acme: acme,
27
- identifiers: identifiers,
28
- challenge_responder_rules: config.challenge_responders,
29
- chain_preferences: config.chain_preferences,
30
- not_before: not_before,
31
- not_after: not_after
32
- )
33
- order.perform!
34
- cert = order.certificate
35
-
36
- puts
37
- print " * securing into the storage ..."
38
- storage.put_certificate(cert, certificate_key_passphrase)
39
- puts " [ ok ]"
40
- puts
41
-
42
- execute_post_issue_hooks(cert)
43
-
44
- cert
24
+ def order(*identifiers, key_type: 'rsa', rsa_key_size: 2048, elliptic_curve: 'prime256v1', not_before: nil, not_after: nil)
25
+ private_key = generate_private_key(key_type: key_type, rsa_key_size: rsa_key_size, elliptic_curve: elliptic_curve)
26
+ order_with_private_key(*identifiers, private_key: private_key, not_before: not_before, not_after: not_after)
45
27
  end
46
28
 
47
29
  def authorize(*identifiers)
@@ -149,7 +131,7 @@ module Acmesmith
149
131
  puts " Not valid after: #{not_after}"
150
132
  next unless (cert.certificate.not_after.utc - Time.now.utc) < (days.to_i * 86400)
151
133
  puts " * Renewing: CN=#{cert.common_name}, SANs=#{cert.sans.join(',')}"
152
- order(cert.common_name, *cert.sans)
134
+ order_with_private_key(cert.common_name, *cert.sans, private_key: regenerate_private_key(cert.public_key))
153
135
  end
154
136
  end
155
137
 
@@ -158,7 +140,7 @@ module Acmesmith
158
140
  cert = storage.get_certificate(common_name)
159
141
  sans = cert.sans + add_sans
160
142
  puts " * SANs will be: #{sans.join(?,)}"
161
- order(cert.common_name, *sans)
143
+ order_with_private_key(cert.common_name, *sans, private_key: regenerate_private_key(cert.public_key))
162
144
  end
163
145
 
164
146
  private
@@ -197,5 +179,52 @@ module Acmesmith
197
179
  config['account_key_passphrase']
198
180
  end
199
181
  end
182
+
183
+ def order_with_private_key(*identifiers, private_key:, not_before: nil, not_after: nil)
184
+ order = OrderingService.new(
185
+ acme: acme,
186
+ identifiers: identifiers,
187
+ private_key: private_key,
188
+ challenge_responder_rules: config.challenge_responders,
189
+ chain_preferences: config.chain_preferences,
190
+ not_before: not_before,
191
+ not_after: not_after
192
+ )
193
+ order.perform!
194
+ cert = order.certificate
195
+
196
+ puts
197
+ print " * securing into the storage ..."
198
+ storage.put_certificate(cert, certificate_key_passphrase)
199
+ puts " [ ok ]"
200
+ puts
201
+
202
+ execute_post_issue_hooks(cert)
203
+
204
+ cert
205
+ end
206
+
207
+ def generate_private_key(key_type:, rsa_key_size:, elliptic_curve:)
208
+ case key_type
209
+ when 'rsa'
210
+ OpenSSL::PKey::RSA.generate(rsa_key_size)
211
+ when 'ec'
212
+ OpenSSL::PKey::EC.generate(elliptic_curve)
213
+ else
214
+ raise ArgumentError, "Key type #{key_type} is not supported"
215
+ end
216
+ end
217
+
218
+ # Generate a new key pair with the same type and key size / curve as existing one
219
+ def regenerate_private_key(template)
220
+ case template
221
+ when OpenSSL::PKey::RSA
222
+ OpenSSL::PKey::RSA.generate(template.n.num_bits)
223
+ when OpenSSL::PKey::EC
224
+ OpenSSL::PKey::EC.generate(template.group)
225
+ else
226
+ raise ArgumentError, "Unknown key type: #{template.class}"
227
+ end
228
+ end
200
229
  end
201
230
  end
@@ -32,8 +32,16 @@ module Acmesmith
32
32
 
33
33
  desc "order COMMON_NAME [SAN]", "order certificate for CN +COMMON_NAME+ with SANs +SAN+"
34
34
  method_option :show_certificate, type: :boolean, aliases: %w(-s), default: true, desc: 'show an issued certificate in PEM and text when exiting'
35
+ method_option :key_type, type: :string, enum: %w(rsa ec), default: 'rsa', desc: 'key type'
36
+ method_option :rsa_key_size, type: :numeric, default: 2048, desc: 'size of RSA key'
37
+ method_option :elliptic_curve, type: :string, default: 'prime256v1', desc: 'elliptic curve group for EC key'
35
38
  def order(common_name, *sans)
36
- cert = client.order(common_name, *sans)
39
+ cert = client.order(
40
+ common_name, *sans,
41
+ key_type: options[:key_type],
42
+ rsa_key_size: options[:rsa_key_size],
43
+ elliptic_curve: options[:elliptic_curve],
44
+ )
37
45
  if options[:show_certificate]
38
46
  puts cert.certificate.to_text
39
47
  puts cert.certificate.to_pem
@@ -8,20 +8,22 @@ module Acmesmith
8
8
 
9
9
  # @param acme [Acme::Client] ACME client
10
10
  # @param identifiers [Array<String>] Array of domain names for a ordering certificate. The first item will be a common name.
11
+ # @param private_key [OpenSSL::PKey::PKey] Private key
11
12
  # @param challenge_responder_rules [Array<Acmesmith::Config::ChallengeResponderRule>] responders
12
13
  # @param chain_preferences [Array<Acmesmith::Config::ChainPreference>] chain_preferences
13
14
  # @param not_before [Time]
14
15
  # @param not_after [Time]
15
- def initialize(acme:, identifiers:, challenge_responder_rules:, chain_preferences:, not_before: nil, not_after: nil)
16
+ def initialize(acme:, identifiers:, private_key:, challenge_responder_rules:, chain_preferences:, not_before: nil, not_after: nil)
16
17
  @acme = acme
17
18
  @identifiers = identifiers
19
+ @private_key = private_key
18
20
  @challenge_responder_rules = challenge_responder_rules
19
21
  @chain_preferences = chain_preferences
20
22
  @not_before = not_before
21
23
  @not_after = not_after
22
24
  end
23
25
 
24
- attr_reader :acme, :identifiers, :challenge_responder_rules, :chain_preferences, :not_before, :not_after
26
+ attr_reader :acme, :identifiers, :private_key, :challenge_responder_rules, :chain_preferences, :not_before, :not_after
25
27
 
26
28
  def perform!
27
29
  puts "=> Ordering a certificate for the following identifiers:"
@@ -107,7 +109,7 @@ module Acmesmith
107
109
 
108
110
  # @return [Acme::Client::CertificateRequest]
109
111
  def csr
110
- @csr ||= Acme::Client::CertificateRequest.new(subject: { common_name: common_name }, names: sans)
112
+ @csr ||= Acme::Client::CertificateRequest.new(subject: { common_name: common_name }, names: sans, private_key: private_key)
111
113
  end
112
114
  end
113
115
  end
@@ -1,3 +1,3 @@
1
1
  module Acmesmith
2
- VERSION = "2.5.0"
2
+ VERSION = "2.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acmesmith
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sorah Fukumori
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-08 00:00:00.000000000 Z
11
+ date: 2023-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acme-client
@@ -215,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
215
  - !ruby/object:Gem::Version
216
216
  version: '0'
217
217
  requirements: []
218
- rubygems_version: 3.4.0.dev
218
+ rubygems_version: 3.4.6
219
219
  signing_key:
220
220
  specification_version: 4
221
221
  summary: ACME client (Let's encrypt client) to manage certificate in multi server