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 +4 -4
- data/.github/workflows/build.yml +2 -2
- data/CHANGELOG.md +7 -0
- data/Dockerfile +2 -2
- data/Gemfile.lock +28 -25
- data/lib/acmesmith/certificate.rb +12 -7
- data/lib/acmesmith/client.rb +52 -23
- data/lib/acmesmith/command.rb +9 -1
- data/lib/acmesmith/ordering_service.rb +5 -3
- data/lib/acmesmith/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 914be0de4c237bcd0db59a908f8ce70c3f7b364bb2ab0ac25b6e26ff95ecd7bc
|
4
|
+
data.tar.gz: 14b96550d4ef0274d1546e78b5121c6b9356e9c71e479a4ed17b4d9ff8fc669e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce305e972fcb06a4bc97b2ceea2177cd759601c55064dd5e22ea15b0981ea838e7c11d8e5401d08c41ec1f249cfa0d7a6489f67b2c3e52cc8d67bd1871d77e69
|
7
|
+
data.tar.gz: dfabb130f347dc7829cbaad1b5de6aca553dfedc7172d53d163736baee2b07b00124ef7f5f31d9270df660f4b9381b05ed6ca6cfa2e9a96163f25d6fa2d83286
|
data/.github/workflows/build.yml
CHANGED
@@ -18,7 +18,7 @@ jobs:
|
|
18
18
|
strategy:
|
19
19
|
fail-fast: false
|
20
20
|
matrix:
|
21
|
-
ruby-version: ['
|
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: ['
|
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
|
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
|
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.
|
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.
|
14
|
+
acme-client (2.0.14)
|
15
15
|
faraday (>= 1.0, < 3.0.0)
|
16
|
-
faraday-retry (
|
16
|
+
faraday-retry (>= 1.0, < 3.0.0)
|
17
17
|
aws-eventstream (1.2.0)
|
18
|
-
aws-partitions (1.
|
19
|
-
aws-sdk-acm (1.
|
20
|
-
aws-sdk-core (~> 3, >= 3.
|
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.
|
22
|
+
aws-sdk-core (3.185.0)
|
23
23
|
aws-eventstream (~> 1, >= 1.0.2)
|
24
|
-
aws-partitions (~> 1, >= 1.
|
25
|
-
aws-sigv4 (~> 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.
|
28
|
-
aws-sdk-core (~> 3, >= 3.
|
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.
|
31
|
-
aws-sdk-core (~> 3, >= 3.
|
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.
|
34
|
-
aws-sdk-core (~> 3, >= 3.
|
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.
|
37
|
-
aws-sigv4 (1.
|
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.
|
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.
|
44
|
-
faraday-retry (
|
45
|
-
|
46
|
-
|
47
|
-
|
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.
|
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.
|
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::
|
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
|
70
|
-
rescue OpenSSL::PKey::
|
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::
|
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::
|
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
|
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::
|
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)
|
data/lib/acmesmith/client.rb
CHANGED
@@ -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
|
-
|
26
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/acmesmith/command.rb
CHANGED
@@ -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(
|
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
|
data/lib/acmesmith/version.rb
CHANGED
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.
|
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:
|
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.
|
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
|