letscert 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/letscert.svg)](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
|