eassl3 3.0.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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +57 -0
- data/README.md +58 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/eassl.gemspec +29 -0
- data/lib/eassl/authority_certificate.rb +59 -0
- data/lib/eassl/certificate.rb +109 -0
- data/lib/eassl/certificate_authority.rb +46 -0
- data/lib/eassl/certificate_name.rb +40 -0
- data/lib/eassl/key.rb +70 -0
- data/lib/eassl/serial.rb +33 -0
- data/lib/eassl/signing_request.rb +91 -0
- data/lib/eassl/version.rb +3 -0
- data/lib/eassl.rb +71 -0
- metadata +111 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 975e8d4b5b91a9681f9b719d7bbd5db546433203
|
|
4
|
+
data.tar.gz: 0f421b863e64b983a436716b3242593c30e74313
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4fe2c4f90e9ca7f5b4bf97d53a511d42b1fd2e6fa9549929a1d66f84e5b8250692a15039be41dce41b88d1fe873c76a7cfa97cacdbd28310973dac7fde365186
|
|
7
|
+
data.tar.gz: d84e442c0a7d8e64b6026312960379fc8e84baec3303d4f3bd63db01351db195c306b9eb27fefab183566e6a52f609f6ebbc7ebe4d3d8a735650e90569f54735
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Contributor Code of Conduct
|
|
2
|
+
|
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
|
4
|
+
|
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
|
6
|
+
|
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
|
8
|
+
|
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
|
10
|
+
|
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
|
12
|
+
|
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
eassl3 (3.0.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
diff-lcs (1.2.5)
|
|
10
|
+
rake (10.4.2)
|
|
11
|
+
rspec (3.3.0)
|
|
12
|
+
rspec-core (~> 3.3.0)
|
|
13
|
+
rspec-expectations (~> 3.3.0)
|
|
14
|
+
rspec-mocks (~> 3.3.0)
|
|
15
|
+
rspec-core (3.3.1)
|
|
16
|
+
rspec-support (~> 3.3.0)
|
|
17
|
+
rspec-expectations (3.3.0)
|
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
19
|
+
rspec-support (~> 3.3.0)
|
|
20
|
+
rspec-mocks (3.3.0)
|
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
22
|
+
rspec-support (~> 3.3.0)
|
|
23
|
+
rspec-support (3.3.0)
|
|
24
|
+
|
|
25
|
+
PLATFORMS
|
|
26
|
+
ruby
|
|
27
|
+
|
|
28
|
+
DEPENDENCIES
|
|
29
|
+
bundler (~> 1.8)
|
|
30
|
+
eassl3!
|
|
31
|
+
rake (~> 10.0)
|
|
32
|
+
rspec
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.co.jp>.
|
|
2
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
|
3
|
+
(see COPYING.txt file), or the conditions below:
|
|
4
|
+
|
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
|
6
|
+
software without restriction, provided that you duplicate all of the
|
|
7
|
+
original copyright notices and associated disclaimers.
|
|
8
|
+
|
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
|
10
|
+
you do at least ONE of the following:
|
|
11
|
+
|
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
|
13
|
+
make them Freely Available, such as by posting said
|
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
|
15
|
+
the author to include your modifications in the software.
|
|
16
|
+
|
|
17
|
+
b) use the modified software only within your corporation or
|
|
18
|
+
organization.
|
|
19
|
+
|
|
20
|
+
c) rename any non-standard executables so the names do not conflict
|
|
21
|
+
with standard executables, which must also be provided.
|
|
22
|
+
|
|
23
|
+
d) make other distribution arrangements with the author.
|
|
24
|
+
|
|
25
|
+
3. You may distribute the software in object code or executable
|
|
26
|
+
form, provided that you do at least ONE of the following:
|
|
27
|
+
|
|
28
|
+
a) distribute the executables and library files of the software,
|
|
29
|
+
together with instructions (in the manual page or equivalent)
|
|
30
|
+
on where to get the original distribution.
|
|
31
|
+
|
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
|
33
|
+
the software.
|
|
34
|
+
|
|
35
|
+
c) give non-standard executables non-standard names, with
|
|
36
|
+
instructions on where to get the original software distribution.
|
|
37
|
+
|
|
38
|
+
d) make other distribution arrangements with the author.
|
|
39
|
+
|
|
40
|
+
4. You may modify and include the part of the software into any other
|
|
41
|
+
software (possibly commercial). But some files in the distribution
|
|
42
|
+
are not written by the author, so that they are not under this terms.
|
|
43
|
+
|
|
44
|
+
They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
|
|
45
|
+
files under the ./missing directory. See each file for the copying
|
|
46
|
+
condition.
|
|
47
|
+
|
|
48
|
+
5. The scripts and library files supplied as input to or produced as
|
|
49
|
+
output from the software do not automatically fall under the
|
|
50
|
+
copyright of the software, but belong to whomever generated them,
|
|
51
|
+
and may be sold commercially, and may be aggregated with this
|
|
52
|
+
software.
|
|
53
|
+
|
|
54
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
|
55
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
56
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
57
|
+
PURPOSE.
|
data/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Eassl
|
|
2
|
+
|
|
3
|
+
EaSSL is a library aimed at making openSSL certificate generation and management easier and more ruby-ish.
|
|
4
|
+
|
|
5
|
+
Forked from https://github.com/chrisa/eassl and patched using available pull requests for that project and additional development for better utilizing CSR details and using SHA512 for signing by default. Rcov and Jeweler were switched out in favor of Rspec and Bundler.
|
|
6
|
+
|
|
7
|
+
Ruby license, inherited from the rubyforge project.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add this line to your application's Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'eassl3'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
And then execute:
|
|
18
|
+
|
|
19
|
+
$ bundle
|
|
20
|
+
|
|
21
|
+
Or install it yourself as:
|
|
22
|
+
|
|
23
|
+
$ gem install eassl3
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Generating a CSR and private key:
|
|
28
|
+
options = {
|
|
29
|
+
:department => 'web sites',
|
|
30
|
+
:common_name => 'www.mydomain.com',
|
|
31
|
+
:organization, => 'My Org'
|
|
32
|
+
:email => 'test@test.com',
|
|
33
|
+
:city => 'Fargo',
|
|
34
|
+
:state => 'North Dakota',
|
|
35
|
+
:country => 'USA',
|
|
36
|
+
:subject_alt_name => ['www.mydomain.com', 'mydomain.com']
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ea_key = EaSSL::Key.new
|
|
40
|
+
ea_name = EaSSL::CertificateName.new(options)
|
|
41
|
+
ea_csr = EaSSL::SigningRequest.new(:name => ea_name, :key => ea_key)
|
|
42
|
+
|
|
43
|
+
csr = ea_csr.ssl.to_s
|
|
44
|
+
key = ea_key.private_key.to_s
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
49
|
+
|
|
50
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
51
|
+
|
|
52
|
+
## Contributing
|
|
53
|
+
|
|
54
|
+
1. Fork it ( https://github.com/[my-github-username]/eassl/fork )
|
|
55
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
56
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
57
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
58
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "eassl"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/eassl.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'eassl/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "eassl3"
|
|
8
|
+
spec.version = Eassl::VERSION
|
|
9
|
+
spec.authors = ["Peter Bell", "Paul Nicholson", "Paul Meserve", "Chris Andrews"]
|
|
10
|
+
spec.email = ["bellpeterm+github@gmail.com"]
|
|
11
|
+
|
|
12
|
+
# if spec.respond_to?(:metadata)
|
|
13
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
|
|
14
|
+
# end
|
|
15
|
+
|
|
16
|
+
spec.summary = %q{EaSSL is a library aimed at making openSSL certificate generation and management easier and more ruby-ish.}
|
|
17
|
+
spec.description = %q{This gem is a drop-in replacement for eassl 0.1.1643}
|
|
18
|
+
spec.homepage = "https://github.com/bellpeterm/eassl"
|
|
19
|
+
spec.license = "Ruby"
|
|
20
|
+
|
|
21
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
22
|
+
spec.bindir = "exe"
|
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
24
|
+
spec.require_paths = ["lib"]
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
28
|
+
spec.add_development_dependency "rspec"
|
|
29
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'eassl'
|
|
3
|
+
module EaSSL
|
|
4
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
5
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
6
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
class AuthorityCertificate
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = {
|
|
11
|
+
:key => nil, #required
|
|
12
|
+
:name => {}, #required, CertificateName
|
|
13
|
+
}.update(options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ssl
|
|
17
|
+
unless @ssl
|
|
18
|
+
cert = OpenSSL::X509::Certificate.new
|
|
19
|
+
cert.not_before = Time.now
|
|
20
|
+
cert.subject = cert.issuer = CertificateName.new({ :common_name => "CA" }.update(@options[:name])).name
|
|
21
|
+
cert.not_after = cert.not_before + (365 * 5) * 24 * 60 * 60
|
|
22
|
+
cert.public_key = @options[:key].public_key
|
|
23
|
+
cert.serial = 1
|
|
24
|
+
cert.version = 2 # X509v3
|
|
25
|
+
|
|
26
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
|
27
|
+
ef.subject_certificate = cert
|
|
28
|
+
ef.issuer_certificate = cert
|
|
29
|
+
cert.extensions = [
|
|
30
|
+
ef.create_extension("basicConstraints","CA:TRUE"),
|
|
31
|
+
ef.create_extension("keyUsage", "cRLSign, keyCertSign"),
|
|
32
|
+
ef.create_extension("subjectKeyIdentifier", "hash"),
|
|
33
|
+
ef.create_extension("nsComment", "Ruby/OpenSSL/EaSSL Generated Certificate"),
|
|
34
|
+
]
|
|
35
|
+
cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always"))
|
|
36
|
+
cert.sign(@options[:key].private_key, OpenSSL::Digest::SHA1.new)
|
|
37
|
+
@ssl = cert
|
|
38
|
+
end
|
|
39
|
+
@ssl
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def method_missing(method)
|
|
43
|
+
ssl.send(method)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def load(pem_string)
|
|
47
|
+
begin
|
|
48
|
+
@ssl = OpenSSL::X509::Certificate.new(pem_string)
|
|
49
|
+
rescue
|
|
50
|
+
raise "CertificateLoader: Error loading certificate"
|
|
51
|
+
end
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.load(pem_file_path)
|
|
56
|
+
new({}).load(File.read(pem_file_path))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'eassl'
|
|
3
|
+
module EaSSL
|
|
4
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
5
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
6
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
class Certificate
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = {
|
|
11
|
+
:days_valid => (365 * 5),
|
|
12
|
+
:signing_request => nil, #required
|
|
13
|
+
:ca_certificate => nil, #required
|
|
14
|
+
:comment => "Ruby/OpenSSL/EaSSL Generated Certificate",
|
|
15
|
+
:type => "server",
|
|
16
|
+
:subject_alt_name => nil, #optional e.g. [ "*.example.com", "example.com" ]
|
|
17
|
+
:override_req => true
|
|
18
|
+
}.update(options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def ssl
|
|
22
|
+
unless @ssl
|
|
23
|
+
@ssl = OpenSSL::X509::Certificate.new
|
|
24
|
+
@ssl.not_before = Time.now
|
|
25
|
+
@ssl.subject = @options[:signing_request].subject
|
|
26
|
+
@ssl.issuer = @options[:ca_certificate]? @options[:ca_certificate].subject : @ssl.subject
|
|
27
|
+
@ssl.not_after = @ssl.not_before + @options[:days_valid] * 24 * 60 * 60
|
|
28
|
+
@ssl.public_key = @options[:signing_request].public_key
|
|
29
|
+
@ssl.serial = @options[:serial] || 2
|
|
30
|
+
@ssl.version = 2 # X509v3
|
|
31
|
+
|
|
32
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
|
33
|
+
ef.subject_certificate = @ssl
|
|
34
|
+
ef.issuer_certificate = @options[:ca_certificate]? @options[:ca_certificate].ssl : @ssl
|
|
35
|
+
@ssl.extensions = [ ef.create_extension("subjectKeyIdentifier", "hash") ]
|
|
36
|
+
@ssl.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always"))
|
|
37
|
+
|
|
38
|
+
extensions = Array.new
|
|
39
|
+
|
|
40
|
+
extensions << ef.create_extension("basicConstraints","CA:FALSE")
|
|
41
|
+
extensions << ef.create_extension("nsComment", @options[:comment])
|
|
42
|
+
|
|
43
|
+
case @options[:type]
|
|
44
|
+
when 'server'
|
|
45
|
+
extensions << ef.create_extension("keyUsage", "digitalSignature,keyEncipherment")
|
|
46
|
+
extensions << ef.create_extension("extendedKeyUsage", "serverAuth")
|
|
47
|
+
when 'client'
|
|
48
|
+
extensions << ef.create_extension("keyUsage", "nonRepudiation,digitalSignature,keyEncipherment")
|
|
49
|
+
extensions << ef.create_extension("extendedKeyUsage", "clientAuth,emailProtection")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#add subject alternate names
|
|
53
|
+
if @options[:subject_alt_name]
|
|
54
|
+
subjectAltName = @options[:subject_alt_name].map { |d| "DNS: #{d}" }.join(',')
|
|
55
|
+
extensions << ef.create_extension("subjectAltName", subjectAltName)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if sr = @options[:signing_request]
|
|
59
|
+
sr.extensions.each do |ext|
|
|
60
|
+
if @options[:override_req] # CA extensions take precedence in merge, default behavior
|
|
61
|
+
extensions << ext unless extensions.any? { |e| e.oid == ext.oid }
|
|
62
|
+
else # Req extensions take precedence in merge
|
|
63
|
+
extensions.delete_if { |e| e.oid == ext.oid }
|
|
64
|
+
extensions << ext
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
extensions.each do |ext|
|
|
70
|
+
@ssl.add_extension(ext)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
@ssl
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def sign(ca_key, digest=OpenSSL::Digest::SHA1.new)
|
|
78
|
+
ssl.sign(ca_key.private_key, digest)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def to_pem
|
|
82
|
+
ssl.to_pem
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns a SHA1 fingerprint of the certificate in the OpenSSL style
|
|
86
|
+
def sha1_fingerprint
|
|
87
|
+
Digest::SHA1.hexdigest(ssl.to_der).upcase.gsub(/(..)/, '\1:').chop
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# This method is used to intercept and pass-thru calls to openSSL methods and instance
|
|
91
|
+
# variables.
|
|
92
|
+
def method_missing(method)
|
|
93
|
+
ssl.send(method)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def self.load(pem_file_path)
|
|
97
|
+
new({}).load(File.read(pem_file_path))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def load(pem_string)
|
|
101
|
+
begin
|
|
102
|
+
@ssl = OpenSSL::X509::Certificate.new(pem_string)
|
|
103
|
+
rescue
|
|
104
|
+
raise "CertificateLoader: Error loading certificate"
|
|
105
|
+
end
|
|
106
|
+
self
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'eassl'
|
|
3
|
+
module EaSSL
|
|
4
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
5
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
6
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
class CertificateAuthority
|
|
9
|
+
attr_reader :key, :certificate, :serial
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
if options[:key] && options[:certificate] && options[:serial]
|
|
12
|
+
@key = options[:key]
|
|
13
|
+
@certificate = options[:certificate]
|
|
14
|
+
@serial = options[:serial]
|
|
15
|
+
else
|
|
16
|
+
options[:name] ||= {}
|
|
17
|
+
@key = Key.new({:password => 'ca_ssl_password'}.update(options))
|
|
18
|
+
@certificate = AuthorityCertificate.new(:key => @key, :name => options[:name])
|
|
19
|
+
@serial = Serial.new(:next => 1)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.load(options)
|
|
24
|
+
key = Key.load(File.join(options[:ca_path], 'cakey.pem'), options[:ca_password])
|
|
25
|
+
certificate = AuthorityCertificate.load(File.join(options[:ca_path], 'cacert.pem'))
|
|
26
|
+
serial = Serial.load(File.join(options[:ca_path], 'serial.txt'))
|
|
27
|
+
self.new(:key => key, :certificate => certificate, :serial => serial)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_certificate(signing_request, type='server', days_valid=nil, digest=OpenSSL::Digest::SHA512.new)
|
|
31
|
+
options = {
|
|
32
|
+
:signing_request => signing_request,
|
|
33
|
+
:ca_certificate => @certificate,
|
|
34
|
+
:serial => @serial.issue_serial,
|
|
35
|
+
:type => type
|
|
36
|
+
}
|
|
37
|
+
if days_valid
|
|
38
|
+
options[:days_valid] = days_valid
|
|
39
|
+
end
|
|
40
|
+
cert = Certificate.new(options)
|
|
41
|
+
@serial.save!
|
|
42
|
+
cert.sign(@key, digest)
|
|
43
|
+
cert
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'eassl'
|
|
3
|
+
module EaSSL
|
|
4
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
5
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
6
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
class CertificateName
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ssl
|
|
14
|
+
name_mapping = [
|
|
15
|
+
['C', :country, OpenSSL::ASN1::PRINTABLESTRING],
|
|
16
|
+
['ST', :state, OpenSSL::ASN1::PRINTABLESTRING],
|
|
17
|
+
['L', :city, OpenSSL::ASN1::PRINTABLESTRING],
|
|
18
|
+
['O', :organization, OpenSSL::ASN1::UTF8STRING],
|
|
19
|
+
['OU', :department, OpenSSL::ASN1::UTF8STRING],
|
|
20
|
+
['CN', :common_name, OpenSSL::ASN1::UTF8STRING],
|
|
21
|
+
['emailAddress', :email, OpenSSL::ASN1::IA5STRING]
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
name = []
|
|
25
|
+
name_mapping.each do |k|
|
|
26
|
+
name << [k[0], @options[k[1]], k[2]] if @options[k[1]]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
OpenSSL::X509::Name.new(name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def name
|
|
33
|
+
ssl
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def options
|
|
37
|
+
@options
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/eassl/key.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'eassl'
|
|
3
|
+
module EaSSL
|
|
4
|
+
# == EaSSL::Key creates and manages openSSL keys
|
|
5
|
+
#
|
|
6
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
7
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
8
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
9
|
+
# License:: Distributes under the same terms as Ruby
|
|
10
|
+
#
|
|
11
|
+
# ==== Usage
|
|
12
|
+
#
|
|
13
|
+
# ===== Availible Methods - including methods provided by openSSL::PKey:
|
|
14
|
+
# * public_key
|
|
15
|
+
# * private_key
|
|
16
|
+
# * to_text
|
|
17
|
+
class Key
|
|
18
|
+
# Create new Key using the provided options or using the defaults
|
|
19
|
+
def initialize(options = {}) #:params: options
|
|
20
|
+
@options = {
|
|
21
|
+
:bits => 2048,
|
|
22
|
+
:password => 'ssl_password',
|
|
23
|
+
}.update(options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def ssl
|
|
27
|
+
unless @ssl
|
|
28
|
+
# <Should use some kind of logger on this>
|
|
29
|
+
# $stderr.puts "Generating #{@options[:bits]} bit key\n"
|
|
30
|
+
@ssl = OpenSSL::PKey::RSA::new(@options[:bits])
|
|
31
|
+
end
|
|
32
|
+
@ssl
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# This method is used to intercept and pass-thru calls to openSSL methods and instance
|
|
36
|
+
# variables.
|
|
37
|
+
def method_missing(method) # :nodoc:
|
|
38
|
+
ssl.send(method)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def private_key
|
|
42
|
+
ssl
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the length of the key in bits
|
|
46
|
+
def length
|
|
47
|
+
ssl.n.num_bytes * 8
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Export the encrypted key, returns a string
|
|
51
|
+
def to_pem
|
|
52
|
+
ssl.export(OpenSSL::Cipher::DES.new('EDE3-CBC'), @options[:password])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Decrypt and load a PEM encoded Key from the file system with the provided password.
|
|
56
|
+
def self.load(pem_file_path, password=nil)
|
|
57
|
+
new.load(File.read(pem_file_path), password)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Decrypt and load a PEM encoded Key from provided string with the provided password.
|
|
61
|
+
def load(pem_string, password=nil)
|
|
62
|
+
begin
|
|
63
|
+
@ssl = OpenSSL::PKey::RSA::new(pem_string, password || @options[:password])
|
|
64
|
+
rescue
|
|
65
|
+
raise "KeyLoader: Error decrypting key with password"
|
|
66
|
+
end
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/eassl/serial.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'eassl'
|
|
2
|
+
module EaSSL
|
|
3
|
+
# Author:: Chris Andrews (mailto:chris@nodnol.org)
|
|
4
|
+
# Copyright:: Copyright (c) 2011 Chris Andrews
|
|
5
|
+
# License:: Distributes under the same terms as Ruby
|
|
6
|
+
class Serial
|
|
7
|
+
attr_reader :next
|
|
8
|
+
def initialize(options = {})
|
|
9
|
+
@next = options[:next]
|
|
10
|
+
@path = options[:path]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.load(serial_file_path)
|
|
14
|
+
hex_string = (File.read(serial_file_path))
|
|
15
|
+
self.new(:next => Integer("0x#{hex_string}"), :path => serial_file_path)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def save!
|
|
19
|
+
if @path
|
|
20
|
+
hex_string = sprintf("%04X", @next)
|
|
21
|
+
File.open(@path, 'w') do |io|
|
|
22
|
+
io.write "#{hex_string}\n"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def issue_serial
|
|
28
|
+
@next = @next + 1
|
|
29
|
+
@next - 1
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'eassl'
|
|
3
|
+
module EaSSL
|
|
4
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
5
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
6
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
class SigningRequest
|
|
9
|
+
attr_reader :extensions
|
|
10
|
+
|
|
11
|
+
def initialize(options = {})
|
|
12
|
+
@options = {
|
|
13
|
+
:name => {}, #required, CertificateName
|
|
14
|
+
:key => nil, #required
|
|
15
|
+
:digest => OpenSSL::Digest::SHA512.new,
|
|
16
|
+
}.update(options)
|
|
17
|
+
@options[:key] ||= Key.new(@options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def ssl
|
|
21
|
+
unless @ssl
|
|
22
|
+
@ssl = OpenSSL::X509::Request.new
|
|
23
|
+
@ssl.version = 0
|
|
24
|
+
@ssl.subject = CertificateName.new(@options[:name].options).name
|
|
25
|
+
@ssl.public_key = key.public_key
|
|
26
|
+
|
|
27
|
+
@extensions = Array.new
|
|
28
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
|
29
|
+
|
|
30
|
+
case @options[:type]
|
|
31
|
+
when 'subordinate'
|
|
32
|
+
@extensions << ef.create_extension("basicConstraints","CA:TRUE")
|
|
33
|
+
when 'server'
|
|
34
|
+
@extensions << ef.create_extension("basicConstraints","CA:FALSE")
|
|
35
|
+
@extensions << ef.create_extension("keyUsage", "digitalSignature,keyEncipherment")
|
|
36
|
+
@extensions << ef.create_extension("extendedKeyUsage", "serverAuth")
|
|
37
|
+
when 'client'
|
|
38
|
+
@extensions << ef.create_extension("basicConstraints","CA:FALSE")
|
|
39
|
+
@extensions << ef.create_extension("keyUsage", "nonRepudiation,digitalSignature,keyEncipherment")
|
|
40
|
+
@extensions << ef.create_extension("extendedKeyUsage", "clientAuth,emailProtection")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if @options[:subject_alt_name]
|
|
44
|
+
subjectAltName = @options[:subject_alt_name].map { |d| "DNS: #{d}" }.join(',')
|
|
45
|
+
@extensions << ef.create_extension("subjectAltName", subjectAltName)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if @extensions.count > 0
|
|
49
|
+
seq = OpenSSL::ASN1::Sequence.new(extensions)
|
|
50
|
+
set = OpenSSL::ASN1::Set.new([seq])
|
|
51
|
+
attr = OpenSSL::X509::Attribute.new('extReq', set)
|
|
52
|
+
@ssl.add_attribute(attr)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
@ssl.sign(key.private_key, @options[:digest])
|
|
56
|
+
end
|
|
57
|
+
@ssl
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def key
|
|
61
|
+
@options[:key]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def options
|
|
65
|
+
@options
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def to_pem
|
|
69
|
+
ssl.to_pem
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# This method is used to intercept and pass-thru calls to openSSL methods and instance
|
|
73
|
+
# variables.
|
|
74
|
+
def method_missing(method)
|
|
75
|
+
ssl.send(method)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.load(pem_file_path)
|
|
79
|
+
new.load(File.read(pem_file_path))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def load(pem_string)
|
|
83
|
+
begin
|
|
84
|
+
@ssl = OpenSSL::X509::Request.new(pem_string)
|
|
85
|
+
rescue
|
|
86
|
+
raise "SigningRequestLoader: Error loading signing request"
|
|
87
|
+
end
|
|
88
|
+
self
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/eassl.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
$:.unshift File.expand_path(File.dirname(__FILE__))
|
|
4
|
+
# = About EaSSL
|
|
5
|
+
#
|
|
6
|
+
# Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
|
|
7
|
+
# Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
|
|
8
|
+
# Copyright:: Copyright (c) 2006 WebPower Design
|
|
9
|
+
# License:: Distributes under the same terms as Ruby
|
|
10
|
+
#
|
|
11
|
+
# By requiring <tt>eassl</tt>, you can load the full set of EaSSL classes.
|
|
12
|
+
#
|
|
13
|
+
# For a full list of features and instructions, see the #README.
|
|
14
|
+
#
|
|
15
|
+
# EaSSL is a module containing all of the great EaSSL classes for creating
|
|
16
|
+
# and managing openSSL keys, signing request, and certificates.
|
|
17
|
+
#
|
|
18
|
+
# * EaSSL::Key: the class for loading and creating SSL keys
|
|
19
|
+
# * EaSSL::SigningRequest: the class for creating SSL signing requests
|
|
20
|
+
|
|
21
|
+
module EaSSL
|
|
22
|
+
VERSION = '2.0.0'
|
|
23
|
+
|
|
24
|
+
def self.generate_self_signed(options)
|
|
25
|
+
ca = CertificateAuthority.new({:bits => 1024}.update(options[:ca_options]||{}))
|
|
26
|
+
sr = SigningRequest.new(options)
|
|
27
|
+
cert = ca.create_certificate(sr)
|
|
28
|
+
[ca, sr, cert]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.config_webrick(webrick_config, options = {})
|
|
32
|
+
hostname = `hostname`.strip
|
|
33
|
+
eassl_host_dir = "#{File.expand_path('~')}/.eassl/#{hostname}"
|
|
34
|
+
ca_cert_file = "#{eassl_host_dir}/ca.crt"
|
|
35
|
+
ca_key_file = "#{eassl_host_dir}/ca.key"
|
|
36
|
+
server_key_file = "#{eassl_host_dir}/server.key"
|
|
37
|
+
server_cert_file = "#{eassl_host_dir}/server.crt"
|
|
38
|
+
FileUtils.rm_rf(eassl_host_dir) if options[:force_regeneration]
|
|
39
|
+
|
|
40
|
+
if File.exist?(server_cert_file)
|
|
41
|
+
key = Key.load(server_key_file, 'countinghouse1234')
|
|
42
|
+
cert = Certificate.load(server_cert_file)
|
|
43
|
+
else
|
|
44
|
+
ca, sr, cert = self.generate_self_signed({:name => {:common_name => hostname}, :bits => 1024}.update(options))
|
|
45
|
+
key = sr.key
|
|
46
|
+
FileUtils.makedirs(eassl_host_dir)
|
|
47
|
+
File.open(%(#{ca_cert_file}.pem), "w", 0777) {|f| f << ca.certificate.to_pem }
|
|
48
|
+
File.open(%(#{ca_cert_file}.der), "w", 0777) {|f| f << ca.certificate.to_der }
|
|
49
|
+
File.open(ca_key_file, "w", 0777) {|f| f << ca.key.to_pem }
|
|
50
|
+
File.open(server_key_file, "w", 0777) {|f| f << key.to_pem }
|
|
51
|
+
File.open(server_cert_file, "w", 0777) {|f| f << cert.to_pem }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
webrick_config.update({
|
|
55
|
+
:SSLEnable => true,
|
|
56
|
+
:SSLPrivateKey => key.ssl,
|
|
57
|
+
:SSLCertificate => cert.ssl,
|
|
58
|
+
:SSLExtraChainCert => [Certificate.load(%(#{ca_cert_file}.pem)).ssl],
|
|
59
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
|
60
|
+
:SSLStartImmediately => true,
|
|
61
|
+
})
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
require 'eassl/key'
|
|
66
|
+
require 'eassl/certificate_name'
|
|
67
|
+
require 'eassl/signing_request'
|
|
68
|
+
require 'eassl/certificate'
|
|
69
|
+
require 'eassl/authority_certificate'
|
|
70
|
+
require 'eassl/certificate_authority'
|
|
71
|
+
require 'eassl/serial'
|
metadata
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: eassl3
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 3.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Peter Bell
|
|
8
|
+
- Paul Nicholson
|
|
9
|
+
- Paul Meserve
|
|
10
|
+
- Chris Andrews
|
|
11
|
+
autorequire:
|
|
12
|
+
bindir: exe
|
|
13
|
+
cert_chain: []
|
|
14
|
+
date: 2015-06-18 00:00:00.000000000 Z
|
|
15
|
+
dependencies:
|
|
16
|
+
- !ruby/object:Gem::Dependency
|
|
17
|
+
name: bundler
|
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
|
19
|
+
requirements:
|
|
20
|
+
- - "~>"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '1.8'
|
|
23
|
+
type: :development
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.8'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
requirements:
|
|
34
|
+
- - "~>"
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '10.0'
|
|
37
|
+
type: :development
|
|
38
|
+
prerelease: false
|
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - "~>"
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '10.0'
|
|
44
|
+
- !ruby/object:Gem::Dependency
|
|
45
|
+
name: rspec
|
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '0'
|
|
51
|
+
type: :development
|
|
52
|
+
prerelease: false
|
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
description: This gem is a drop-in replacement for eassl 0.1.1643
|
|
59
|
+
email:
|
|
60
|
+
- bellpeterm+github@gmail.com
|
|
61
|
+
executables: []
|
|
62
|
+
extensions: []
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- ".gitignore"
|
|
66
|
+
- ".rspec"
|
|
67
|
+
- ".travis.yml"
|
|
68
|
+
- CODE_OF_CONDUCT.md
|
|
69
|
+
- Gemfile
|
|
70
|
+
- Gemfile.lock
|
|
71
|
+
- LICENSE.txt
|
|
72
|
+
- README.md
|
|
73
|
+
- Rakefile
|
|
74
|
+
- bin/console
|
|
75
|
+
- bin/setup
|
|
76
|
+
- eassl.gemspec
|
|
77
|
+
- lib/eassl.rb
|
|
78
|
+
- lib/eassl/authority_certificate.rb
|
|
79
|
+
- lib/eassl/certificate.rb
|
|
80
|
+
- lib/eassl/certificate_authority.rb
|
|
81
|
+
- lib/eassl/certificate_name.rb
|
|
82
|
+
- lib/eassl/key.rb
|
|
83
|
+
- lib/eassl/serial.rb
|
|
84
|
+
- lib/eassl/signing_request.rb
|
|
85
|
+
- lib/eassl/version.rb
|
|
86
|
+
homepage: https://github.com/bellpeterm/eassl
|
|
87
|
+
licenses:
|
|
88
|
+
- Ruby
|
|
89
|
+
metadata: {}
|
|
90
|
+
post_install_message:
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubyforge_project:
|
|
106
|
+
rubygems_version: 2.4.5
|
|
107
|
+
signing_key:
|
|
108
|
+
specification_version: 4
|
|
109
|
+
summary: EaSSL is a library aimed at making openSSL certificate generation and management
|
|
110
|
+
easier and more ruby-ish.
|
|
111
|
+
test_files: []
|