acme-client 2.0.6 → 2.0.7

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: '09b859082bb56c35a6c050801def72343fe9f9737213155705f691bb044a54cc'
4
- data.tar.gz: be387d14e92a862fb79668579d456050cdf0e8e5c36de6726bbd80e073410c41
3
+ metadata.gz: 718dac85c8139621711a030272097498d04414ca52c9d84544b8bac32179e8a1
4
+ data.tar.gz: 9e929450733261f291a62b96f5056ed89e8c235cae010805ddccfc3efb17188c
5
5
  SHA512:
6
- metadata.gz: 44f195eb5477138c33393704905537d3b2a66f774d5f1c645f88475cfe4b4be4bba082a15b8e99fbfc8266d74901fb38847ec83d440175db3a004ffbb085c9cd
7
- data.tar.gz: 76930a35156563532898381603c515f65fb6057b8895b5d2785c88897b75878a319067a26652dee0e33d6a992f75073011b72398a40580b7be493927e11c047e
6
+ metadata.gz: f6e77c41a67b3f2fe9d6e373d58e928bfc0ee2523ac7b0ce2beb93167179a0950045cbb0ec4f5d8616527108814001acd0da7909a8bb0df8dac9b6ddde8bfbba
7
+ data.tar.gz: 9eba181b543cfe437e2043970b2e74cd691944457d43d3c3ea052cf10d3134e59b51aa8ee3522c36f27b25c212797e44e0e29b86f96b13361e98ca07d054ccac
@@ -1,10 +1,6 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.1
5
- - 2.2
6
- - 2.3
7
- - 2.4
8
4
  - 2.5
9
5
  - 2.6
10
6
  - 2.7
@@ -1,3 +1,8 @@
1
+ ## `2.0.7`
2
+
3
+ * Add support for alternate certificate chain
4
+ * Change `Link` headers parsing to return array of value. This add support multiple entries at the same `rel`
5
+
1
6
  ## `2.0.6`
2
7
 
3
8
  * Allow Faraday up to `< 2.0`
data/README.md CHANGED
@@ -184,7 +184,7 @@ csr = Acme::Client::CertificateRequest.new(private_key: a_different_private_key,
184
184
  order.finalize(csr: csr)
185
185
  while order.status == 'processing'
186
186
  sleep(1)
187
- challenge.reload
187
+ order.reload
188
188
  end
189
189
  order.certificate # => PEM-formatted certificate
190
190
  ```
@@ -20,6 +20,7 @@ require 'acme/client/faraday_middleware'
20
20
  require 'acme/client/jwk'
21
21
  require 'acme/client/error'
22
22
  require 'acme/client/util'
23
+ require 'acme/client/chain_identifier'
23
24
 
24
25
  class Acme::Client
25
26
  DEFAULT_DIRECTORY = 'http://127.0.0.1:4000/directory'.freeze
@@ -127,9 +128,24 @@ class Acme::Client
127
128
  Acme::Client::Resources::Order.new(self, **arguments)
128
129
  end
129
130
 
130
- def certificate(url:)
131
+ def certificate(url:, force_chain: nil)
131
132
  response = download(url, format: :pem)
132
- response.body
133
+ pem = response.body
134
+
135
+ return pem if force_chain.nil?
136
+
137
+ return pem if ChainIdentifier.new(pem).match_name?(force_chain)
138
+
139
+ alternative_urls = Array(response.headers.dig('link', 'alternate'))
140
+ alternative_urls.each do |alternate_url|
141
+ response = download(alternate_url, format: :pem)
142
+ pem = response.body
143
+ if ChainIdentifier.new(pem).match_name?(force_chain)
144
+ return pem
145
+ end
146
+ end
147
+
148
+ raise Acme::Client::Error::ForcedChainNotFound, "Could not find any matching chain for `#{force_chain}`"
133
149
  end
134
150
 
135
151
  def authorization(url:)
@@ -0,0 +1,27 @@
1
+ class Acme::Client
2
+ class ChainIdentifier
3
+ def initialize(pem_certificate_chain)
4
+ @pem_certificate_chain = pem_certificate_chain
5
+ end
6
+
7
+ def match_name?(name)
8
+ issuers.any? do |issuer|
9
+ issuer.include?(name)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def issuers
16
+ x509_certificates.map(&:issuer).map(&:to_s)
17
+ end
18
+
19
+ def x509_certificates
20
+ @x509_certificates ||= splitted_pem_certificates.map { |pem| OpenSSL::X509::Certificate.new(pem) }
21
+ end
22
+
23
+ def splitted_pem_certificates
24
+ @pem_certificate_chain.each_line.slice_after(/END CERTIFICATE/).map(&:join)
25
+ end
26
+ end
27
+ end
@@ -7,6 +7,7 @@ class Acme::Client::Error < StandardError
7
7
  class UnsupportedChallengeType < ClientError; end
8
8
  class NotFound < ClientError; end
9
9
  class CertificateNotReady < ClientError; end
10
+ class ForcedChainNotFound < ClientError; end
10
11
 
11
12
  class ServerError < Acme::Client::Error; end
12
13
  class BadCSR < ServerError; end
@@ -82,18 +82,10 @@ class Acme::Client::FaradayMiddleware < Faraday::Middleware
82
82
  end
83
83
  end
84
84
 
85
- LINK_MATCH = /<(.*?)>;rel="([\w-]+)"/
86
-
87
85
  def decode_link_headers
88
86
  return unless env.response_headers.key?('Link')
89
87
  link_header = env.response_headers['Link']
90
-
91
- links = link_header.split(', ').map { |entry|
92
- _, link, name = *entry.match(LINK_MATCH)
93
- [name, link]
94
- }
95
-
96
- Hash[*links.flatten]
88
+ Acme::Client::Util.decode_link_headers(link_header)
97
89
  end
98
90
 
99
91
  def store_nonce
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Account
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def kid
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Authorization
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def deactivate
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Challenges::Base
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def challenge_type
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Order
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def reload
@@ -24,9 +24,9 @@ class Acme::Client::Resources::Order
24
24
  true
25
25
  end
26
26
 
27
- def certificate
27
+ def certificate(force_chain: nil)
28
28
  if certificate_url
29
- @client.certificate(url: certificate_url)
29
+ @client.certificate(url: certificate_url, force_chain: force_chain)
30
30
  else
31
31
  raise Acme::Client::Error::CertificateNotReady, 'No certificate_url to collect the order'
32
32
  end
@@ -3,6 +3,17 @@ module Acme::Client::Util
3
3
  Base64.urlsafe_encode64(data).sub(/[\s=]*\z/, '')
4
4
  end
5
5
 
6
+ LINK_MATCH = /<(.*?)>\s?;\s?rel="([\w-]+)"/
7
+
8
+ # See RFC 8288 - https://tools.ietf.org/html/rfc8288#section-3
9
+ def decode_link_headers(link_header)
10
+ link_header.split(',').each_with_object({}) { |entry, hash|
11
+ _, link, name = *entry.match(LINK_MATCH)
12
+ hash[name] ||= []
13
+ hash[name].push(link)
14
+ }
15
+ end
16
+
6
17
  # Sets public key on CSR or cert.
7
18
  #
8
19
  # obj - An OpenSSL::X509::Certificate or OpenSSL::X509::Request instance.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Acme
4
4
  class Client
5
- VERSION = '2.0.6'.freeze
5
+ VERSION = '2.0.7'.freeze
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acme-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.6
4
+ version: 2.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Barbier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-05 00:00:00.000000000 Z
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -124,6 +124,7 @@ files:
124
124
  - lib/acme/client.rb
125
125
  - lib/acme/client/certificate_request.rb
126
126
  - lib/acme/client/certificate_request/ec_key_patch.rb
127
+ - lib/acme/client/chain_identifier.rb
127
128
  - lib/acme/client/error.rb
128
129
  - lib/acme/client/faraday_middleware.rb
129
130
  - lib/acme/client/jwk.rb
@@ -162,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
163
  - !ruby/object:Gem::Version
163
164
  version: '0'
164
165
  requirements: []
165
- rubygems_version: 3.0.3
166
+ rubygems_version: 3.1.2
166
167
  signing_key:
167
168
  specification_version: 4
168
169
  summary: Client for the ACME protocol.