acme-client 0.2.4 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e2bfc1b30ebefbb87d572378e91ac4d9e47552de
4
- data.tar.gz: 51bedd0f2e79328154c044f98c354b0bb2db4860
3
+ metadata.gz: b4f8844db80f42dfbebde13f4834adff81b31a6c
4
+ data.tar.gz: aac066e3a86278081ad327072318cf659065760d
5
5
  SHA512:
6
- metadata.gz: 03c36be67fa43961e1c61ce1ae9abaf0922042e4281240901b520f69bc38961e7690b7301cc95dc9be6962d9e23057f46fba3d154e56c33993f9abb767c29a14
7
- data.tar.gz: fb2079a8eb3c28772b01655e211a82391ee76f68ecc41cc41f95b7b800dc04aec3c6d4f9f36f8574a7d53b7bd78edf7b908a81296065bf0f29d289500a6e5bab
6
+ metadata.gz: 0d8ae92a14464b33d91f07e127c35016987c4b912cb65edfa2d416f1471127f3a6ea8df093ca349778a45cf745d5caeae734d8c2f2f93b6a029deb04d7841ff0
7
+ data.tar.gz: 7e16c99dfedf9620fb9f1879ce89fba8bbc6f547f4c589b2177e9ee96966074f35bb85ff4c0afa9edd94ec9d3203dc05f8fb5568c57754300e7ed2ea1a9b7752
data/.rubocop.yml ADDED
@@ -0,0 +1,85 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.2
3
+ Exclude:
4
+ - 'bin/*'
5
+ - 'vendor/**/*'
6
+
7
+ Rails:
8
+ Enabled: false
9
+
10
+ Style/FileName:
11
+ Exclude:
12
+ - 'lib/acme-client.rb'
13
+
14
+ Lint/AssignmentInCondition:
15
+ Enabled: false
16
+
17
+ Style/ClassAndModuleChildren:
18
+ Enabled: false
19
+
20
+ Style/Documentation:
21
+ Enabled: false
22
+
23
+ Style/MultilineOperationIndentation:
24
+ Enabled: false
25
+
26
+ Style/SignalException:
27
+ EnforcedStyle: only_raise
28
+
29
+ Style/AlignParameters:
30
+ EnforcedStyle: with_fixed_indentation
31
+
32
+ Style/FirstParameterIndentation:
33
+ EnforcedStyle: consistent
34
+
35
+ Style/TrailingCommaInArguments:
36
+ Enabled: false
37
+
38
+ Style/TrailingCommaInLiteral:
39
+ Enabled: false
40
+
41
+ Style/StringLiterals:
42
+ Enabled: single_quotes
43
+
44
+ Metrics/LineLength:
45
+ Max: 140
46
+
47
+ Metrics/ParameterLists:
48
+ Max: 5
49
+ CountKeywordArgs: false
50
+
51
+ Lint/EndAlignment:
52
+ AlignWith: variable
53
+
54
+ Style/ParallelAssignment:
55
+ Enabled: false
56
+
57
+ Style/ModuleFunction:
58
+ Enabled: false
59
+
60
+ Style/TrivialAccessors:
61
+ AllowPredicates: true
62
+
63
+ Lint/UnusedMethodArgument:
64
+ AllowUnusedKeywordArguments: true
65
+
66
+ Metrics/MethodLength:
67
+ Max: 15
68
+
69
+ Style/DoubleNegation:
70
+ Enabled: false
71
+
72
+ Style/IfUnlessModifier:
73
+ Enabled: false
74
+
75
+ Style/MultilineBlockChain:
76
+ Enabled: false
77
+
78
+ Style/BlockDelimiters:
79
+ EnforcedStyle: semantic
80
+
81
+ Style/Lambda:
82
+ Enabled: false
83
+
84
+ Style/AccessorMethodName:
85
+ Enabled: false
data/Gemfile CHANGED
@@ -3,5 +3,6 @@ gemspec
3
3
 
4
4
  group :development, :test do
5
5
  gem 'pry'
6
+ gem 'rubocop', '0.36.0'
6
7
  gem 'ruby-prof', require: false
7
8
  end
data/README.md CHANGED
@@ -85,7 +85,7 @@ File.write("fullchain.pem", certificate.fullchain_to_pem)
85
85
  # :Port => 8443,
86
86
  # :DocumentRoot => Dir.pwd,
87
87
  # :SSLEnable => true,
88
- # :SSLPrivateKey => OpenSSL::PKey::RSA.new( File.read('key.pem') ),
88
+ # :SSLPrivateKey => OpenSSL::PKey::RSA.new( File.read('privkey.pem') ),
89
89
  # :SSLCertificate => OpenSSL::X509::Certificate.new( File.read('cert.pem') )); trap('INT') { s.shutdown }; s.start"
90
90
  ```
91
91
 
@@ -94,6 +94,10 @@ File.write("fullchain.pem", certificate.fullchain_to_pem)
94
94
  - Recovery methods are not implemented.
95
95
  - proofOfPossession-01 is not implemented.
96
96
 
97
+ # Requirements
98
+
99
+ Ruby >= 2.1
100
+
97
101
  ## Development
98
102
 
99
103
  All the tests use VCR to mock the interaction with the server but if you
data/Rakefile CHANGED
@@ -1,6 +1,9 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
3
2
 
3
+ require 'rspec/core/rake_task'
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ require 'rubocop/rake_task'
7
+ RuboCop::RakeTask.new
8
+
9
+ task default: [:spec, :rubocop]
data/lib/acme/client.rb CHANGED
@@ -1,13 +1,13 @@
1
- require "acme-client"
1
+ require 'acme-client'
2
2
 
3
3
  class Acme::Client
4
- DEFAULT_ENDPOINT = 'http://127.0.0.1:4000'
4
+ DEFAULT_ENDPOINT = 'http://127.0.0.1:4000'.freeze
5
5
  DIRECTORY_DEFAULT = {
6
6
  'new-authz' => '/acme/new-authz',
7
7
  'new-cert' => '/acme/new-cert',
8
8
  'new-reg' => '/acme/new-reg',
9
9
  'revoke-cert' => '/acme/revoke-cert'
10
- }
10
+ }.freeze
11
11
 
12
12
  def initialize(private_key:, endpoint: DEFAULT_ENDPOINT, directory_uri: nil)
13
13
  @endpoint, @private_key, @directory_uri = endpoint, private_key, directory_uri
@@ -70,12 +70,13 @@ class Acme::Client
70
70
 
71
71
  private
72
72
 
73
- def fetch_chain(response, limit=10)
74
- if limit == 0 || response.headers["link"].nil? || response.headers["link"]["up"].nil?
73
+ def fetch_chain(response, limit = 10)
74
+ links = response.headers['link']
75
+ if limit.zero? || links.nil? || links['up'].nil?
75
76
  []
76
77
  else
77
- issuer = connection.get(response.headers["link"]["up"])
78
- [OpenSSL::X509::Certificate.new(issuer.body), *fetch_chain(issuer, limit-1)]
78
+ issuer = connection.get(links['up'])
79
+ [OpenSSL::X509::Certificate.new(issuer.body), *fetch_chain(issuer, limit - 1)]
79
80
  end
80
81
  end
81
82
 
@@ -24,6 +24,6 @@ class Acme::Client::Certificate
24
24
  end
25
25
 
26
26
  def common_name
27
- x509.subject.to_a.find {|name, _, _| name == "CN" }[1]
27
+ x509.subject.to_a.find { |name, _, _| name == 'CN' }[1]
28
28
  end
29
29
  end
@@ -4,43 +4,34 @@ class Acme::Client::CertificateRequest
4
4
  DEFAULT_KEY_LENGTH = 2048
5
5
  DEFAULT_DIGEST = OpenSSL::Digest::SHA256
6
6
  SUBJECT_KEYS = {
7
- common_name: "CN",
8
- country_name: "C",
9
- organization_name: "O",
10
- organizational_unit: "OU",
11
- state_or_province: "ST",
12
- locality_name: "L"
7
+ common_name: 'CN',
8
+ country_name: 'C',
9
+ organization_name: 'O',
10
+ organizational_unit: 'OU',
11
+ state_or_province: 'ST',
12
+ locality_name: 'L'
13
13
  }.freeze
14
14
 
15
15
  SUBJECT_TYPES = {
16
- "CN" => OpenSSL::ASN1::UTF8STRING,
17
- "C" => OpenSSL::ASN1::UTF8STRING,
18
- "O" => OpenSSL::ASN1::UTF8STRING,
19
- "OU" => OpenSSL::ASN1::UTF8STRING,
20
- "ST" => OpenSSL::ASN1::UTF8STRING,
21
- "L" => OpenSSL::ASN1::UTF8STRING
16
+ 'CN' => OpenSSL::ASN1::UTF8STRING,
17
+ 'C' => OpenSSL::ASN1::UTF8STRING,
18
+ 'O' => OpenSSL::ASN1::UTF8STRING,
19
+ 'OU' => OpenSSL::ASN1::UTF8STRING,
20
+ 'ST' => OpenSSL::ASN1::UTF8STRING,
21
+ 'L' => OpenSSL::ASN1::UTF8STRING
22
22
  }.freeze
23
23
 
24
24
  attr_reader :private_key, :common_name, :names, :subject
25
25
 
26
26
  def_delegators :csr, :to_pem, :to_der
27
27
 
28
- def initialize(common_name: nil,
29
- names: [],
30
- private_key: generate_private_key,
31
- subject: {},
32
- digest: DEFAULT_DIGEST.new)
33
- raise ArgumentError.new("Digest must be a OpenSSL::Digest") unless digest.is_a?(OpenSSL::Digest)
28
+ def initialize(common_name: nil, names: [], private_key: generate_private_key, subject: {}, digest: DEFAULT_DIGEST.new)
34
29
  @digest = digest
35
-
36
30
  @private_key = private_key
37
-
38
31
  @subject = normalize_subject(subject)
39
32
  @common_name = common_name || @subject[SUBJECT_KEYS[:common_name]] || @subject[:common_name]
40
-
41
33
  @names = names.to_a.dup
42
34
  normalize_names
43
-
44
35
  @subject[SUBJECT_KEYS[:common_name]] ||= @common_name
45
36
  validate_subject
46
37
  end
@@ -56,30 +47,34 @@ class Acme::Client::CertificateRequest
56
47
  end
57
48
 
58
49
  def normalize_subject(subject)
59
- @subject = subject.each_with_object({}) {|(key, value), subject|
60
- subject[SUBJECT_KEYS.fetch(key, key)] = value.to_s
61
- }
50
+ @subject = subject.each_with_object({}) do |(key, value), hash|
51
+ hash[SUBJECT_KEYS.fetch(key, key)] = value.to_s
52
+ end
62
53
  end
63
54
 
64
55
  def normalize_names
65
56
  if @common_name
66
57
  @names.unshift(@common_name) unless @names.include?(@common_name)
67
- elsif @names.empty?
68
- raise ArgumentError.new("No common name and no list of names given")
69
58
  else
59
+ raise ArgumentError, 'No common name and no list of names given' if @names.empty?
70
60
  @common_name = @names.first
71
61
  end
72
62
  end
73
63
 
74
64
  def validate_subject
65
+ validate_subject_attributes
66
+ validate_subject_common_name
67
+ end
68
+
69
+ def validate_subject_attributes
75
70
  extra_keys = @subject.keys - SUBJECT_KEYS.keys - SUBJECT_KEYS.values
76
- unless extra_keys.empty?
77
- raise ArgumentError.new("Unexpected subject attributes given: #{extra_keys.inspect}")
78
- end
71
+ return if extra_keys.empty?
72
+ raise ArgumentError, "Unexpected subject attributes given: #{extra_keys.inspect}"
73
+ end
79
74
 
80
- unless @common_name == @subject[SUBJECT_KEYS[:common_name]]
81
- raise ArgumentError.new("Conflicting common name given in arguments and subject")
82
- end
75
+ def validate_subject_common_name
76
+ return if @common_name == @subject[SUBJECT_KEYS[:common_name]]
77
+ raise ArgumentError, 'Conflicting common name given in arguments and subject'
83
78
  end
84
79
 
85
80
  def generate
@@ -103,11 +98,11 @@ class Acme::Client::CertificateRequest
103
98
  return if @names.size <= 1
104
99
 
105
100
  extension = OpenSSL::X509::ExtensionFactory.new.create_extension(
106
- "subjectAltName", @names.map {|name| "DNS:#{name}" }.join(", "), false
101
+ 'subjectAltName', @names.map { |name| "DNS:#{name}" }.join(', '), false
107
102
  )
108
103
  csr.add_attribute(
109
104
  OpenSSL::X509::Attribute.new(
110
- "extReq",
105
+ 'extReq',
111
106
  OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([extension])])
112
107
  )
113
108
  )
@@ -9,50 +9,76 @@ class Acme::Client::FaradayMiddleware < Faraday::Middleware
9
9
  def call(env)
10
10
  @env = env
11
11
  @env.body = crypto.generate_signed_jws(header: { nonce: pop_nonce }, payload: env.body)
12
- @app.call(env).on_complete {|env| on_complete(env) }
12
+ @app.call(env).on_complete { |response_env| on_complete(response_env) }
13
13
  end
14
14
 
15
15
  def on_complete(env)
16
- raise Acme::Client::Error::NotFound, env.url.to_s if env.status == 404
16
+ @env = env
17
17
 
18
- nonces << env.response_headers['replay-nonce']
18
+ raise_on_not_found!
19
+ store_nonce
20
+ env.body = decode_body
21
+ env.response_headers['Link'] = decode_link_headers
19
22
 
20
- content_type = env.response_headers['Content-Type']
23
+ return if env.success?
21
24
 
22
- if content_type == 'application/json' || content_type == 'application/problem+json'
23
- env.body = JSON.load(env.body)
24
- end
25
+ raise_on_error!
26
+ end
25
27
 
26
- if env.response_headers.key?('Link')
27
- link_header = env.response_headers['Link']
28
- links = link_header.split(', ').map do |entry|
29
- link = entry.match(/<(.*?)>;/).captures.first
30
- name = entry.match(/rel="([\w-]+)"/).captures.first
31
- [name, link]
32
- end
28
+ private
33
29
 
34
- env.response_headers['Link'] = Hash[*links.flatten]
35
- end
30
+ def raise_on_not_found!
31
+ raise Acme::Client::Error::NotFound, env.url.to_s if env.status == 404
32
+ end
36
33
 
37
- return if env.success?
34
+ def raise_on_error!
35
+ raise error_class, error_message
36
+ end
38
37
 
38
+ def error_message
39
+ if env.body.is_a? Hash
40
+ env.body['detail']
41
+ else
42
+ "Error message: #{env.body}"
43
+ end
44
+ end
45
+
46
+ def error_class
39
47
  error_name = env.body['type'].gsub('urn:acme:error:', '').classify
40
- error_class = if Acme::Client::Error.qualified_const_defined?(error_name)
48
+ if Acme::Client::Error.qualified_const_defined?(error_name)
41
49
  "Acme::Client::Error::#{error_name}".constantize
42
50
  else
43
51
  Acme::Client::Error
44
52
  end
53
+ end
45
54
 
46
- message = if env.body.is_a? Hash
47
- env.body['detail']
55
+ def decode_body
56
+ content_type = env.response_headers['Content-Type']
57
+
58
+ if content_type == 'application/json' || content_type == 'application/problem+json'
59
+ JSON.load(env.body)
48
60
  else
49
- "Error message: #{env.body}"
61
+ env.body
50
62
  end
63
+ end
64
+
65
+ LINK_MATCH = /<(.*?)>;rel="([\w-]+)"/
66
+
67
+ def decode_link_headers
68
+ return unless env.response_headers.key?('Link')
69
+ link_header = env.response_headers['Link']
51
70
 
52
- raise error_class, message
71
+ links = link_header.split(', ').map { |entry|
72
+ _, link, name = *entry.match(LINK_MATCH)
73
+ [name, link]
74
+ }
75
+
76
+ Hash[*links.flatten]
53
77
  end
54
78
 
55
- private
79
+ def store_nonce
80
+ nonces << env.response_headers['replay-nonce']
81
+ end
56
82
 
57
83
  def pop_nonce
58
84
  if nonces.empty?
@@ -3,7 +3,7 @@ class Acme::Client::Resources::Authorization
3
3
  DNS01 = Acme::Client::Resources::Challenges::DNS01
4
4
  TLSSNI01 = Acme::Client::Resources::Challenges::TLSSNI01
5
5
 
6
- attr_reader :domain, :status, :http01, :dns01, :tls_sni01
6
+ attr_reader :domain, :status, :expires, :http01, :dns01, :tls_sni01
7
7
 
8
8
  def initialize(client, response)
9
9
  @client = client
@@ -19,13 +19,13 @@ class Acme::Client::Resources::Authorization
19
19
  when 'http-01' then @http01 = HTTP01.new(@client, attributes)
20
20
  when 'dns-01' then @dns01 = DNS01.new(@client, attributes)
21
21
  when 'tls-sni-01' then @tls_sni01 = TLSSNI01.new(@client, attributes)
22
- else
23
- # no supported
22
+ # else no-op
24
23
  end
25
24
  end
26
25
  end
27
26
 
28
27
  def assign_attributes(body)
28
+ @expires = Time.iso8601(body['expires']) if body.key? 'expires'
29
29
  @domain = body['identifier']['value']
30
30
  @status = body['status']
31
31
  end
@@ -1,5 +1,4 @@
1
1
  class Acme::Client::Resources::Challenges::Base
2
-
3
2
  attr_reader :client, :status, :uri, :token, :error
4
3
 
5
4
  def initialize(client, attributes)
@@ -15,8 +14,17 @@ class Acme::Client::Resources::Challenges::Base
15
14
  status
16
15
  end
17
16
 
17
+ def request_verification
18
+ response = @client.connection.post(@uri, resource: 'challenge', type: challenge_type, keyAuthorization: authorization_key)
19
+ response.success?
20
+ end
21
+
18
22
  private
19
23
 
24
+ def challenge_type
25
+ self.class::CHALLENGE_TYPE
26
+ end
27
+
20
28
  def authorization_key
21
29
  "#{token}.#{crypto.thumbprint}"
22
30
  end
@@ -1,4 +1,5 @@
1
1
  class Acme::Client::Resources::Challenges::DNS01 < Acme::Client::Resources::Challenges::Base
2
+ CHALLENGE_TYPE = 'dns-01'.freeze
2
3
  RECORD_NAME = '_acme-challenge'.freeze
3
4
  RECORD_TYPE = 'TXT'.freeze
4
5
 
@@ -11,11 +12,6 @@ class Acme::Client::Resources::Challenges::DNS01 < Acme::Client::Resources::Chal
11
12
  end
12
13
 
13
14
  def record_content
14
- crypto.digest.hexdigest(authorization_key)
15
- end
16
-
17
- def request_verification
18
- response = @client.connection.post(@uri, { resource: 'challenge', type: 'dns-01', keyAuthorization: authorization_key })
19
- response.success?
15
+ Base64.urlsafe_encode64(crypto.digest.digest(authorization_key)).sub(/[\s=]*\z/, '')
20
16
  end
21
17
  end
@@ -1,4 +1,5 @@
1
1
  class Acme::Client::Resources::Challenges::HTTP01 < Acme::Client::Resources::Challenges::Base
2
+ CHALLENGE_TYPE = 'http-01'.freeze
2
3
  CONTENT_TYPE = 'text/plain'.freeze
3
4
 
4
5
  def content_type
@@ -12,9 +13,4 @@ class Acme::Client::Resources::Challenges::HTTP01 < Acme::Client::Resources::Cha
12
13
  def filename
13
14
  ".well-known/acme-challenge/#{token}"
14
15
  end
15
-
16
- def request_verification
17
- response = client.connection.post(@uri, { resource: 'challenge', type: 'http-01', keyAuthorization: authorization_key })
18
- response.success?
19
- end
20
16
  end
@@ -1,4 +1,6 @@
1
1
  class Acme::Client::Resources::Challenges::TLSSNI01 < Acme::Client::Resources::Challenges::Base
2
+ CHALLENGE_TYPE = 'tls-sni-01'.freeze
3
+
2
4
  def hostname
3
5
  digest = crypto.digest.hexdigest(authorization_key)
4
6
  "#{digest[0..31]}.#{digest[32..64]}.acme.invalid"
@@ -12,11 +14,6 @@ class Acme::Client::Resources::Challenges::TLSSNI01 < Acme::Client::Resources::C
12
14
  self_sign_certificate.private_key
13
15
  end
14
16
 
15
- def request_verification
16
- response = client.connection.post(@uri, { resource: 'challenge', type: 'tls-sni-01', keyAuthorization: authorization_key })
17
- response.success?
18
- end
19
-
20
17
  private
21
18
 
22
19
  def self_sign_certificate
@@ -9,22 +9,16 @@ class Acme::Client::Resources::Registration
9
9
  end
10
10
 
11
11
  def get_terms
12
- if @term_of_service_uri
13
- @client.connection.get(@term_of_service_uri).body
14
- end
12
+ return unless @term_of_service_uri
13
+
14
+ @client.connection.get(@term_of_service_uri).body
15
15
  end
16
16
 
17
17
  def agree_terms
18
- if @term_of_service_uri
19
- payload = {
20
- resource: "reg",
21
- agreement: @term_of_service_uri
22
- }
23
- response = @client.connection.post(@uri, { resource: 'reg', agreement: @term_of_service_uri })
24
- response.success?
25
- else
26
- true
27
- end
18
+ return true unless @term_of_service_uri
19
+
20
+ response = @client.connection.post(@uri, resource: 'reg', agreement: @term_of_service_uri)
21
+ response.success?
28
22
  end
29
23
 
30
24
  private
@@ -13,15 +13,9 @@ class Acme::Client::SelfSignCertificate
13
13
 
14
14
  def certificate
15
15
  @certificate ||= begin
16
- certificate = OpenSSL::X509::Certificate.new
17
- certificate.not_before = not_before
18
- certificate.not_after = not_after
19
- certificate.public_key = private_key.public_key
20
-
21
- extension_factory = OpenSSL::X509::ExtensionFactory.new
22
- extension_factory.subject_certificate = certificate
23
- extension_factory.issuer_certificate = certificate
16
+ certificate = generate_certificate
24
17
 
18
+ extension_factory = generate_extension_factory(certificate)
25
19
  subject_alt_name_entry = subject_alt_names.map { |d| "DNS: #{d}" }.join(',')
26
20
  subject_alt_name_extension = extension_factory.create_extension('subjectAltName', subject_alt_name_entry)
27
21
  certificate.add_extension(subject_alt_name_extension)
@@ -37,7 +31,7 @@ class Acme::Client::SelfSignCertificate
37
31
  end
38
32
 
39
33
  def default_not_before
40
- Time.now + 3600
34
+ Time.now - 3600
41
35
  end
42
36
 
43
37
  def default_not_after
@@ -47,4 +41,19 @@ class Acme::Client::SelfSignCertificate
47
41
  def digest
48
42
  OpenSSL::Digest::SHA256.new
49
43
  end
44
+
45
+ def generate_certificate
46
+ certificate = OpenSSL::X509::Certificate.new
47
+ certificate.not_before = not_before
48
+ certificate.not_after = not_after
49
+ certificate.public_key = private_key.public_key
50
+ certificate
51
+ end
52
+
53
+ def generate_extension_factory(certificate)
54
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
55
+ extension_factory.subject_certificate = certificate
56
+ extension_factory.issuer_certificate = certificate
57
+ extension_factory
58
+ end
50
59
  end
@@ -1,5 +1,5 @@
1
1
  module Acme
2
2
  class Client
3
- VERSION = '0.2.4'
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  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: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Barbier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-22 00:00:00.000000000 Z
11
+ date: 2016-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -153,6 +153,7 @@ extra_rdoc_files: []
153
153
  files:
154
154
  - ".gitignore"
155
155
  - ".rspec"
156
+ - ".rubocop.yml"
156
157
  - ".travis.yml"
157
158
  - Gemfile
158
159
  - LICENSE.txt
@@ -198,7 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
199
  version: '0'
199
200
  requirements: []
200
201
  rubyforge_project:
201
- rubygems_version: 2.4.5
202
+ rubygems_version: 2.4.5.1
202
203
  signing_key:
203
204
  specification_version: 4
204
205
  summary: Client for the ACME protocol.