letscert 0.2.2 → 0.2.3
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/README.md +11 -4
- data/lib/letscert/certificate.rb +7 -5
- data/lib/letscert/io_plugin.rb +5 -35
- data/lib/letscert.rb +1 -1
- data/spec/account_key.json +1 -0
- data/spec/io_plugin_spec.rb +104 -0
- data/spec/io_plugin_spec.rb~ +16 -0
- data/spec/loggable_spec.rb +32 -0
- data/spec/loggable_spec.rb~ +1 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helper.rb~ +1 -0
- data/spec/test.json +1 -0
- data/tasks/gem.rake +1 -0
- data/tasks/spec.rake +3 -0
- metadata +26 -5
- data/lib/letscert/certificate.rb~ +0 -8
- data/lib/letscert/loggable.rb~ +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13246f0cc94eb264f9dcc01fb55a2e55250d5e63
|
4
|
+
data.tar.gz: d543f27b0a2d55a1e0fc17950bb662bbfbb3a932
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebbdc254cd7cfea90f1b23308673a839baeaa564450dc18fa5340ed30a8fa5e93e84180fdbe66360f70db449671bd276352c3a2081a5aa8b76b58604f341df7e
|
7
|
+
data.tar.gz: 4ae2cdc6a2fa18eb7e9606c909e6260cee6878000ab5c601ce0482352e15333e8bd048a8b33d2614459d0c9ba36dc7c72d753936e21489a584fafb9894457720
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](https://badge.fury.io/rb/letscert)
|
2
|
+
|
1
3
|
# letscert
|
2
4
|
A simple `Let's Encrypt` client in ruby.
|
3
5
|
|
@@ -7,12 +9,17 @@ in Ruby.
|
|
7
9
|
|
8
10
|
# Usage
|
9
11
|
|
10
|
-
Generate a key pair and get signed certificate
|
12
|
+
Generate a key pair and get signed certificate:
|
13
|
+
```bash
|
14
|
+
letscert -d example.com:/var/www/example.com/html -f account_key.json -f key.pem -f cert.pem -f fullchain.pem
|
15
|
+
```
|
16
|
+
|
17
|
+
Generate a key pair and get a signed certificate for multi-domains:
|
11
18
|
```bash
|
12
|
-
letscert -d example.com
|
19
|
+
letscert -d example.com -d www.example.com --default_root /var/www/html -f account_key.json -f key.pem -f cert.pem -f fullchain.pem
|
13
20
|
```
|
14
21
|
|
15
|
-
|
22
|
+
Commands are the sames for certificate renewal.
|
16
23
|
|
17
24
|
# What `letscert` do
|
18
25
|
|
@@ -30,4 +37,4 @@ The command is the same for certificate renewal.
|
|
30
37
|
* 2 in case of errors.
|
31
38
|
|
32
39
|
# Todo
|
33
|
-
Add support to revocation.
|
40
|
+
Add support to revocation.
|
data/lib/letscert/certificate.rb
CHANGED
@@ -54,15 +54,17 @@ module LetsCert
|
|
54
54
|
key = data[:key]
|
55
55
|
else
|
56
56
|
logger.info { 'Generate new private key' }
|
57
|
-
key = OpenSSL::PKey::RSA.generate(
|
57
|
+
key = OpenSSL::PKey::RSA.generate(options[:cert_key_size])
|
58
58
|
end
|
59
59
|
|
60
|
-
csr = Acme::Client::CertificateRequest.new(names: roots.keys,
|
60
|
+
csr = Acme::Client::CertificateRequest.new(names: roots.keys,
|
61
|
+
private_key: key)
|
61
62
|
cert = client.new_certificate(csr)
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
options[:files].each do |plugname|
|
65
|
+
IOPlugin.registered[plugname].save(account_key: client.private_key,
|
66
|
+
key: key, cert: cert.x509,
|
67
|
+
chain: cert.x509_chain)
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
data/lib/letscert/io_plugin.rb
CHANGED
@@ -19,8 +19,7 @@
|
|
19
19
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
21
|
# SOFTWARE.
|
22
|
-
require 'json'
|
23
|
-
require 'base64'
|
22
|
+
require 'json/jwt'
|
24
23
|
require_relative 'loggable'
|
25
24
|
|
26
25
|
module LetsCert
|
@@ -145,43 +144,14 @@ module LetsCert
|
|
145
144
|
def load_jwk(data)
|
146
145
|
return nil if data.empty?
|
147
146
|
|
148
|
-
|
149
|
-
|
150
|
-
key = OpenSSL::PKey::RSA.new
|
151
|
-
key.n = OpenSSL::BN.new(Base64.strict_decode64(hsh['n']))
|
152
|
-
key.e = OpenSSL::BN.new(Base64.strict_decode64(hsh['e']))
|
153
|
-
key.d = OpenSSL::BN.new(Base64.strict_decode64(hsh['e']))
|
154
|
-
key.p = OpenSSL::BN.new(Base64.strict_decode64(hsh['p']))
|
155
|
-
key.q = OpenSSL::BN.new(Base64.strict_decode64(hsh['q']))
|
156
|
-
key.dmp1 = OpenSSL::BN.new(Base64.strict_decode64(hsh['dp']))
|
157
|
-
key.dmq1 = OpenSSL::BN.new(Base64.strict_decode64(hsh['dq']))
|
158
|
-
key.iqmp = OpenSSL::BN.new(Base64.strict_decode64(hsh['qi']))
|
159
|
-
|
160
|
-
key
|
147
|
+
JSON::JWK.new(JSON.parse(data)).to_key
|
161
148
|
end
|
162
149
|
|
163
150
|
# Dump crypto data (key) to a JSON-encoded string
|
164
|
-
# @param [OpenSSL::PKey]
|
151
|
+
# @param [OpenSSL::PKey] key
|
165
152
|
# @return [String]
|
166
|
-
def dump_jwk(
|
167
|
-
|
168
|
-
|
169
|
-
# Add and rename some fields to be compatible with simp_le
|
170
|
-
hsh['kty'] = 'RSA'
|
171
|
-
hsh['qi'] = hsh['iqmp'].dup
|
172
|
-
hsh['dp'] = hsh['dmp1'].dup
|
173
|
-
hsh['dq'] = hsh['dmq1'].dup
|
174
|
-
hsh.delete('iqmp')
|
175
|
-
hsh.delete('dmpl')
|
176
|
-
hsh.delete('dmql')
|
177
|
-
hsh.rehash
|
178
|
-
|
179
|
-
hsh.each_key do |key|
|
180
|
-
if hsh[key].is_a?(OpenSSL::BN)
|
181
|
-
hsh[key] = Base64.strict_encode64(hsh[key].to_s)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
hsh.to_json
|
153
|
+
def dump_jwk(key)
|
154
|
+
key.to_jwk.to_json
|
185
155
|
end
|
186
156
|
end
|
187
157
|
|
data/lib/letscert.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
{"kty":"RSA","e":"AQAB","n":"ugv1o2Fg5N-RBScXrLpRRoPgDjFgHcM_BD2fHZYzXJ4k1AnspAgS-soaWuoN0WK4mwRsSGwF7cemLEA1zZiCpc6Q3WUxLDsM8oF8P_S0euAEhe8FQPON3vtNqYth2yPqAQ4-me43mZyXS94yzTrCVjowlnZsdaA54uUW0AxOD_0","d":"Qo-6zzwspVXTFYvZ7YMvRtIxnAJQR_Wtmv_M6JHvSEiQFoiCcGEvISijazlnvizarSNU9kgnit2t9xD17tuMidbqdPC0x0mkJ6BszB7lau6Nzfz6ACSqtH8eKmkGDBJTnRqzg45Z6-3-gQ9vlmvc3T029Gv7xfA-XOt_cNgFnGE","p":"7nhrSRbf6FZ6LA2FKzcdFOTSI7UlgEwWsrSC_7pyzYyXJC44crlWUDxxJJXNeC9OyOEJOXlgn-0hShAWkl9-CQ","q":"x7kFmj6GYbh0SaLeg9CFhnY-y_UMO9wFxu0NLi2JgL31gvIDrKGJYiSZ3xESi6FwpmHX_vbtFXpZXBbvMy0_VQ"}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module LetsCert
|
5
|
+
|
6
|
+
describe IOPlugin do
|
7
|
+
|
8
|
+
it '.empty_data always returns the same hash' do
|
9
|
+
hsh = IOPlugin.empty_data
|
10
|
+
|
11
|
+
expect(hsh.keys.size).to eq(4)
|
12
|
+
[:account_key, :key, :cert, :chain].each do |key|
|
13
|
+
expect(hsh.keys).to include(key)
|
14
|
+
expect(hsh[key]).to be_nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it '.register registers known subclasses' do
|
19
|
+
names = %w(account_key.json key.pem key.der chain.pem fullchain.pem)
|
20
|
+
names += %w(cert.pem cert.der)
|
21
|
+
|
22
|
+
expect(IOPlugin.registered.size).to eq(names.size)
|
23
|
+
|
24
|
+
names.each do |name|
|
25
|
+
expect(IOPlugin.registered.keys).to include(name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it '.register may register new classes' do
|
30
|
+
class NewIO < IOPlugin;end
|
31
|
+
IOPlugin.register(NewIO, 'newio')
|
32
|
+
|
33
|
+
expect(IOPlugin.registered.keys).to include('newio')
|
34
|
+
expect(IOPlugin.registered['newio']).to be_a(NewIO)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
describe JWKIOPluginMixin do
|
40
|
+
|
41
|
+
class Test; include JWKIOPluginMixin; end
|
42
|
+
|
43
|
+
let(:test) { Test.new }
|
44
|
+
|
45
|
+
it "#load_jwk loads a RSA key from a JSON Web Key raw string" do
|
46
|
+
jwk = File.read(File.join(File.dirname(__FILE__), 'test.json'))
|
47
|
+
key = test.load_jwk(jwk)
|
48
|
+
|
49
|
+
expect(key).to be_a(OpenSSL::PKey::PKey)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "#dump_jwk dumps a RSA key to a JSON Web Key raw string" do
|
53
|
+
jwk = File.read(File.join(File.dirname(__FILE__), 'test.json'))
|
54
|
+
key = test.load_jwk(jwk)
|
55
|
+
|
56
|
+
jwk2 = test.dump_jwk(key)
|
57
|
+
expect(jwk2).to eq(jwk)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe AccountKey do
|
62
|
+
|
63
|
+
before(:all) { IOPlugin.logger = Logger.new('/dev/null') }
|
64
|
+
|
65
|
+
let(:ak) { IOPlugin.registered['account_key.json'] }
|
66
|
+
|
67
|
+
it 'persist account_key' do
|
68
|
+
persisted = ak.persisted
|
69
|
+
expect(persisted[:account_key]).to be(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "#load account key from account_key.json file" do
|
73
|
+
expect(ak).to be_a(AccountKey)
|
74
|
+
|
75
|
+
pwd = FileUtils.pwd
|
76
|
+
FileUtils.cd File.dirname(__FILE__)
|
77
|
+
|
78
|
+
begin
|
79
|
+
content = ak.load
|
80
|
+
expect(content).to be_a(Hash)
|
81
|
+
expect(content.keys.size).to eq(1)
|
82
|
+
expect(content[:account_key]).to be_a(OpenSSL::PKey::PKey)
|
83
|
+
rescue Exception
|
84
|
+
raise
|
85
|
+
ensure
|
86
|
+
FileUtils.cd pwd
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "#save account key to account_key.json file" do
|
91
|
+
data = { account_key: OpenSSL::PKey::RSA.new(1024) }
|
92
|
+
ak.save(data)
|
93
|
+
begin
|
94
|
+
expect(File.exist?('account_key.json')).to be_truthy
|
95
|
+
rescue Exception
|
96
|
+
raise
|
97
|
+
ensure
|
98
|
+
File.unlink('account_key.json')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
module LetsCert
|
4
|
+
|
5
|
+
describe IOPlugin do
|
6
|
+
|
7
|
+
it '.empty_data always returns the same hash' do
|
8
|
+
hsh = IOPlugin.empty_data
|
9
|
+
|
10
|
+
expect(hsh.keys.size).to eq(4)
|
11
|
+
expect(hsh.keys).to include(:account_key)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
module LetsCert
|
4
|
+
|
5
|
+
describe Loggable do
|
6
|
+
|
7
|
+
it 'extend a class to add loggability' do
|
8
|
+
class TestA; include Loggable; end
|
9
|
+
|
10
|
+
expect(TestA.methods).to include(:logger=)
|
11
|
+
|
12
|
+
my_logger = Logger.new(STDERR)
|
13
|
+
TestA.logger = my_logger
|
14
|
+
expect(TestA.new.logger).to eq(my_logger)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'extend a class and its subclasses to add loggability' do
|
18
|
+
class TestA; include Loggable; end
|
19
|
+
class TestB < TestA; end
|
20
|
+
|
21
|
+
expect(TestA.methods).to include(:logger=)
|
22
|
+
expect(TestB.methods).to include(:logger=)
|
23
|
+
|
24
|
+
my_logger = Logger.new(STDERR)
|
25
|
+
TestA.logger = my_logger
|
26
|
+
expect(TestA.new.logger).to eq(my_logger)
|
27
|
+
expect(TestB.new.logger).to eq(my_logger)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'spec_helper'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
data/spec/test.json
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"kty":"RSA","e":"AQAB","n":"ugv1o2Fg5N-RBScXrLpRRoPgDjFgHcM_BD2fHZYzXJ4k1AnspAgS-soaWuoN0WK4mwRsSGwF7cemLEA1zZiCpc6Q3WUxLDsM8oF8P_S0euAEhe8FQPON3vtNqYth2yPqAQ4-me43mZyXS94yzTrCVjowlnZsdaA54uUW0AxOD_0","d":"Qo-6zzwspVXTFYvZ7YMvRtIxnAJQR_Wtmv_M6JHvSEiQFoiCcGEvISijazlnvizarSNU9kgnit2t9xD17tuMidbqdPC0x0mkJ6BszB7lau6Nzfz6ACSqtH8eKmkGDBJTnRqzg45Z6-3-gQ9vlmvc3T029Gv7xfA-XOt_cNgFnGE","p":"7nhrSRbf6FZ6LA2FKzcdFOTSI7UlgEwWsrSC_7pyzYyXJC44crlWUDxxJJXNeC9OyOEJOXlgn-0hShAWkl9-CQ","q":"x7kFmj6GYbh0SaLeg9CFhnY-y_UMO9wFxu0NLi2JgL31gvIDrKGJYiSZ3xESi6FwpmHX_vbtFXpZXBbvMy0_VQ"}
|
data/tasks/gem.rake
CHANGED
data/tasks/spec.rake
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: letscert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acme-client
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.3.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json-jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: yard
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,13 +69,20 @@ files:
|
|
55
69
|
- lib/letscert.rb
|
56
70
|
- lib/letscert.rb~
|
57
71
|
- lib/letscert/certificate.rb
|
58
|
-
- lib/letscert/certificate.rb~
|
59
72
|
- lib/letscert/io_plugin.rb
|
60
73
|
- lib/letscert/loggable.rb
|
61
|
-
- lib/letscert/loggable.rb~
|
62
74
|
- lib/letscert/runner.rb
|
75
|
+
- spec/account_key.json
|
76
|
+
- spec/io_plugin_spec.rb
|
77
|
+
- spec/io_plugin_spec.rb~
|
78
|
+
- spec/loggable_spec.rb
|
79
|
+
- spec/loggable_spec.rb~
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
- spec/spec_helper.rb~
|
82
|
+
- spec/test.json
|
63
83
|
- tasks/gem.rake
|
64
84
|
- tasks/gem.rake~
|
85
|
+
- tasks/spec.rake
|
65
86
|
- tasks/yard.rake
|
66
87
|
homepage: https://github.com/sdaubert/letscert
|
67
88
|
licenses:
|
@@ -83,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
104
|
version: '0'
|
84
105
|
requirements: []
|
85
106
|
rubyforge_project:
|
86
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.4.5.1
|
87
108
|
signing_key:
|
88
109
|
specification_version: 4
|
89
110
|
summary: letscert, a simple Let's Encrypt client
|
data/lib/letscert/loggable.rb~
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module LetsCert
|
2
|
-
|
3
|
-
module Loggable
|
4
|
-
|
5
|
-
module ClassMethods
|
6
|
-
|
7
|
-
# Set logger
|
8
|
-
# @param [Logger] logger
|
9
|
-
def self.logger=(logger)
|
10
|
-
@@logger = logger
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
# Get logger instance
|
17
|
-
# @return [Logger]
|
18
|
-
def logger
|
19
|
-
@logger ||= self.class.class_variable_get(:@@logger)
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|