Class: Acme::Client
- Inherits:
-
Object
show all
- Defined in:
- lib/acme/client.rb,
lib/acme/client.rb,
lib/acme/client/version.rb,
lib/acme/client/chain_identifier.rb
Defined Under Namespace
Modules: JWK, Resources, Util
Classes: CertificateRequest, ChainIdentifier, Error, FaradayMiddleware, SelfSignCertificate
Constant Summary
collapse
- DEFAULT_DIRECTORY =
'http://127.0.0.1:4000/directory'.freeze
- USER_AGENT =
"Acme::Client v#{Acme::Client::VERSION} (#{repo_url})".freeze
- CONTENT_TYPES =
{
pem: 'application/pem-certificate-chain'
}
- VERSION =
'2.0.11'.freeze
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#account ⇒ Object
-
#account_deactivate ⇒ Object
-
#account_key_change(new_private_key: nil, new_jwk: nil) ⇒ Object
-
#account_update(contact: nil, terms_of_service_agreed: nil) ⇒ Object
-
#authorization(url:) ⇒ Object
-
#caa_identities ⇒ Object
-
#certificate(url:, force_chain: nil) ⇒ Object
-
#challenge(url:) ⇒ Object
-
#deactivate_authorization(url:) ⇒ Object
-
#external_account_required ⇒ Object
-
#finalize(url:, csr:) ⇒ Object
-
#get_nonce ⇒ Object
-
#initialize(jwk: nil, kid: nil, private_key: nil, directory: DEFAULT_DIRECTORY, connection_options: {}, bad_nonce_retry: 0) ⇒ Client
constructor
A new instance of Client.
-
#kid ⇒ Object
-
#meta ⇒ Object
-
#new_account(contact:, terms_of_service_agreed: nil) ⇒ Object
-
#new_order(identifiers:, not_before: nil, not_after: nil) ⇒ Object
-
#order(url:) ⇒ Object
-
#request_challenge_validation(url:, key_authorization: nil) ⇒ Object
-
#revoke(certificate:, reason: nil) ⇒ Object
-
#terms_of_service ⇒ Object
-
#website ⇒ Object
Constructor Details
#initialize(jwk: nil, kid: nil, private_key: nil, directory: DEFAULT_DIRECTORY, connection_options: {}, bad_nonce_retry: 0) ⇒ Client
Returns a new instance of Client.
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
# File 'lib/acme/client.rb', line 34
def initialize(jwk: nil, kid: nil, private_key: nil, directory: DEFAULT_DIRECTORY, connection_options: {}, bad_nonce_retry: 0)
if jwk.nil? && private_key.nil?
raise ArgumentError, 'must specify jwk or private_key'
end
@jwk = if jwk
jwk
else
Acme::Client::JWK.from_private_key(private_key)
end
@kid, @connection_options = kid, connection_options
@bad_nonce_retry = bad_nonce_retry
@directory = Acme::Client::Resources::Directory.new(URI(directory), @connection_options)
@nonces ||= []
end
|
Instance Attribute Details
#jwk ⇒ Object
Returns the value of attribute jwk.
51
52
53
|
# File 'lib/acme/client.rb', line 51
def jwk
@jwk
end
|
#nonces ⇒ Object
Returns the value of attribute nonces.
51
52
53
|
# File 'lib/acme/client.rb', line 51
def nonces
@nonces
end
|
Instance Method Details
#account ⇒ Object
111
112
113
114
115
116
117
118
119
120
|
# File 'lib/acme/client.rb', line 111
def account
@kid ||= begin
response = post(endpoint_for(:new_account), payload: { onlyReturnExisting: true }, mode: :jwk)
response..fetch(:location)
end
response = post_as_get(@kid)
arguments = attributes_from_account_response(response)
Acme::Client::Resources::Account.new(self, url: @kid, **arguments)
end
|
#account_deactivate ⇒ Object
83
84
85
86
87
|
# File 'lib/acme/client.rb', line 83
def account_deactivate
response = post(kid, payload: { status: 'deactivated' })
arguments = attributes_from_account_response(response)
Acme::Client::Resources::Account.new(self, url: kid, **arguments)
end
|
#account_key_change(new_private_key: nil, new_jwk: nil) ⇒ Object
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
# File 'lib/acme/client.rb', line 89
def account_key_change(new_private_key: nil, new_jwk: nil)
if new_private_key.nil? && new_jwk.nil?
raise ArgumentError, 'must specify new_jwk or new_private_key'
end
old_jwk = jwk
new_jwk ||= Acme::Client::JWK.from_private_key(new_private_key)
= {
url: endpoint_for(:key_change)
}
inner_payload = {
account: kid,
oldKey: old_jwk.to_h
}
payload = JSON.parse(new_jwk.jws(header: , payload: inner_payload))
response = post(endpoint_for(:key_change), payload: payload, mode: :kid)
arguments = attributes_from_account_response(response)
@jwk = new_jwk
Acme::Client::Resources::Account.new(self, url: kid, **arguments)
end
|
#account_update(contact: nil, terms_of_service_agreed: nil) ⇒ Object
73
74
75
76
77
78
79
80
81
|
# File 'lib/acme/client.rb', line 73
def account_update(contact: nil, terms_of_service_agreed: nil)
payload = {}
payload[:contact] = Array(contact) if contact
payload[:termsOfServiceAgreed] = terms_of_service_agreed if terms_of_service_agreed
response = post(kid, payload: payload)
arguments = attributes_from_account_response(response)
Acme::Client::Resources::Account.new(self, url: kid, **arguments)
end
|
#authorization(url:) ⇒ Object
174
175
176
177
178
|
# File 'lib/acme/client.rb', line 174
def authorization(url:)
response = post_as_get(url)
arguments = attributes_from_authorization_response(response)
Acme::Client::Resources::Authorization.new(self, url: url, **arguments)
end
|
#caa_identities ⇒ Object
232
233
234
|
# File 'lib/acme/client.rb', line 232
def caa_identities
@directory.caa_identities
end
|
#certificate(url:, force_chain: nil) ⇒ Object
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# File 'lib/acme/client.rb', line 154
def certificate(url:, force_chain: nil)
response = download(url, format: :pem)
pem = response.body
return pem if force_chain.nil?
return pem if ChainIdentifier.new(pem).match_name?(force_chain)
alternative_urls = Array(response..dig('link', 'alternate'))
alternative_urls.each do |alternate_url|
response = download(alternate_url, format: :pem)
pem = response.body
if ChainIdentifier.new(pem).match_name?(force_chain)
return pem
end
end
raise Acme::Client::Error::ForcedChainNotFound, "Could not find any matching chain for `#{force_chain}`"
end
|
#challenge(url:) ⇒ Object
186
187
188
189
190
|
# File 'lib/acme/client.rb', line 186
def challenge(url:)
response = post_as_get(url)
arguments = attributes_from_challenge_response(response)
Acme::Client::Resources::Challenges.new(self, **arguments)
end
|
#deactivate_authorization(url:) ⇒ Object
180
181
182
183
184
|
# File 'lib/acme/client.rb', line 180
def deactivate_authorization(url:)
response = post(url, payload: { status: 'deactivated' })
arguments = attributes_from_authorization_response(response)
Acme::Client::Resources::Authorization.new(self, url: url, **arguments)
end
|
#external_account_required ⇒ Object
236
237
238
|
# File 'lib/acme/client.rb', line 236
def external_account_required
@directory.external_account_required
end
|
#finalize(url:, csr:) ⇒ Object
143
144
145
146
147
148
149
150
151
152
|
# File 'lib/acme/client.rb', line 143
def finalize(url:, csr:)
unless csr.respond_to?(:to_der)
raise ArgumentError, 'csr must respond to `#to_der`'
end
base64_der_csr = Acme::Client::Util.urlsafe_base64(csr.to_der)
response = post(url, payload: { csr: base64_der_csr })
arguments = attributes_from_order_response(response)
Acme::Client::Resources::Order.new(self, **arguments)
end
|
#get_nonce ⇒ Object
213
214
215
216
217
218
|
# File 'lib/acme/client.rb', line 213
def get_nonce
connection = new_connection(endpoint: endpoint_for(:new_nonce))
response = connection.head(nil, nil, 'User-Agent' => USER_AGENT)
nonces << response.['replay-nonce']
true
end
|
#kid ⇒ Object
122
123
124
|
# File 'lib/acme/client.rb', line 122
def kid
@kid ||= account.kid
end
|
220
221
222
|
# File 'lib/acme/client.rb', line 220
def meta
@directory.meta
end
|
#new_account(contact:, terms_of_service_agreed: nil) ⇒ Object
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
# File 'lib/acme/client.rb', line 53
def new_account(contact:, terms_of_service_agreed: nil)
payload = {
contact: Array(contact)
}
if terms_of_service_agreed
payload[:termsOfServiceAgreed] = terms_of_service_agreed
end
response = post(endpoint_for(:new_account), payload: payload, mode: :jws)
@kid = response..fetch(:location)
if response.body.nil? || response.body.empty?
account
else
arguments = attributes_from_account_response(response)
Acme::Client::Resources::Account.new(self, url: @kid, **arguments)
end
end
|
#new_order(identifiers:, not_before: nil, not_after: nil) ⇒ Object
126
127
128
129
130
131
132
133
134
135
|
# File 'lib/acme/client.rb', line 126
def new_order(identifiers:, not_before: nil, not_after: nil)
payload = {}
payload['identifiers'] = prepare_order_identifiers(identifiers)
payload['notBefore'] = not_before if not_before
payload['notAfter'] = not_after if not_after
response = post(endpoint_for(:new_order), payload: payload)
arguments = attributes_from_order_response(response)
Acme::Client::Resources::Order.new(self, **arguments)
end
|
#order(url:) ⇒ Object
137
138
139
140
141
|
# File 'lib/acme/client.rb', line 137
def order(url:)
response = post_as_get(url)
arguments = attributes_from_order_response(response)
Acme::Client::Resources::Order.new(self, **arguments.merge(url: url))
end
|
#request_challenge_validation(url:, key_authorization: nil) ⇒ Object
192
193
194
195
196
|
# File 'lib/acme/client.rb', line 192
def request_challenge_validation(url:, key_authorization: nil)
response = post(url, payload: {})
arguments = attributes_from_challenge_response(response)
Acme::Client::Resources::Challenges.new(self, **arguments)
end
|
#revoke(certificate:, reason: nil) ⇒ Object
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
# File 'lib/acme/client.rb', line 198
def revoke(certificate:, reason: nil)
der_certificate = if certificate.respond_to?(:to_der)
certificate.to_der
else
OpenSSL::X509::Certificate.new(certificate).to_der
end
base64_der_certificate = Acme::Client::Util.urlsafe_base64(der_certificate)
payload = { certificate: base64_der_certificate }
payload[:reason] = reason unless reason.nil?
response = post(endpoint_for(:revoke_certificate), payload: payload)
response.success?
end
|
#terms_of_service ⇒ Object
224
225
226
|
# File 'lib/acme/client.rb', line 224
def terms_of_service
@directory.terms_of_service
end
|
#website ⇒ Object
228
229
230
|
# File 'lib/acme/client.rb', line 228
def website
@directory.website
end
|