acme-client 2.0.6 → 2.0.7

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