acmesmith 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
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