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 +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
|