certificate_authority 0.1.1
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.
- data/Gemfile +9 -0
- data/Gemfile.lock +35 -0
- data/README.rdoc +68 -0
- data/Rakefile +52 -0
- data/VERSION.yml +5 -0
- data/certificate_authority.gemspec +90 -0
- data/lib/certificate_authority/certificate.rb +176 -0
- data/lib/certificate_authority/certificate_revocation_list.rb +59 -0
- data/lib/certificate_authority/distinguished_name.rb +39 -0
- data/lib/certificate_authority/extensions.rb +251 -0
- data/lib/certificate_authority/key_material.rb +62 -0
- data/lib/certificate_authority/ocsp_handler.rb +77 -0
- data/lib/certificate_authority/pkcs11_key_material.rb +65 -0
- data/lib/certificate_authority/serial_number.rb +9 -0
- data/lib/certificate_authority/signing_entity.rb +18 -0
- data/lib/certificate_authority.rb +19 -0
- data/lib/tasks/certificate_authority.rake +23 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/units/certificate_authority_spec.rb +4 -0
- data/spec/units/certificate_revocation_list_spec.rb +68 -0
- data/spec/units/certificate_spec.rb +351 -0
- data/spec/units/distinguished_name_spec.rb +38 -0
- data/spec/units/extensions_spec.rb +53 -0
- data/spec/units/key_material_spec.rb +96 -0
- data/spec/units/ocsp_handler_spec.rb +104 -0
- data/spec/units/serial_number_spec.rb +20 -0
- data/spec/units/signing_entity_spec.rb +4 -0
- data/spec/units/units_helper.rb +1 -0
- metadata +148 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (3.0.6)
|
5
|
+
activesupport (= 3.0.6)
|
6
|
+
builder (~> 2.1.2)
|
7
|
+
i18n (~> 0.5.0)
|
8
|
+
activesupport (3.0.6)
|
9
|
+
builder (2.1.2)
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
git (1.2.5)
|
12
|
+
i18n (0.5.0)
|
13
|
+
jeweler (1.5.2)
|
14
|
+
bundler (~> 1.0.0)
|
15
|
+
git (>= 1.2.5)
|
16
|
+
rake
|
17
|
+
rake (0.8.7)
|
18
|
+
rcov (0.9.9)
|
19
|
+
rspec (2.5.0)
|
20
|
+
rspec-core (~> 2.5.0)
|
21
|
+
rspec-expectations (~> 2.5.0)
|
22
|
+
rspec-mocks (~> 2.5.0)
|
23
|
+
rspec-core (2.5.1)
|
24
|
+
rspec-expectations (2.5.0)
|
25
|
+
diff-lcs (~> 1.1.2)
|
26
|
+
rspec-mocks (2.5.0)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
activemodel
|
33
|
+
jeweler (~> 1.5.2)
|
34
|
+
rcov
|
35
|
+
rspec
|
data/README.rdoc
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
= CertificateAuthority - Because it shouldn't be this damned complicated
|
2
|
+
|
3
|
+
This is meant to provide a programmer-friendly implementation of all the basic functionality contained in RFC-3280 to implement your own certificate authority.
|
4
|
+
|
5
|
+
You can generate root certificates, intermediate certificates, and terminal certificates. You can also generate/manage Certificate Revocation Lists (CRLs) and Online Certificate Status Protocol (OCSP) messages.
|
6
|
+
|
7
|
+
Because this library is built using the native Ruby bindings for OpenSSL it also supports PKCS#11 cryptographic hardware for secure maintenance of private key materials.
|
8
|
+
|
9
|
+
= The important parts
|
10
|
+
Coming soon.
|
11
|
+
|
12
|
+
= Examples
|
13
|
+
|
14
|
+
== Creating a self-signed certificate/root (probably what you want)
|
15
|
+
|
16
|
+
require 'certificate_authority'
|
17
|
+
root = CertificateAuthority::Certificate.new
|
18
|
+
root.subject.common_name "http://mydomain.com"
|
19
|
+
root.key_material.generate_key
|
20
|
+
root.signing_entity = true
|
21
|
+
root.sign!
|
22
|
+
|
23
|
+
== Creating an intermediate certificate (much less common use-case)
|
24
|
+
|
25
|
+
require 'certificate_authority'
|
26
|
+
root = CertificateAuthority::Certificate.new
|
27
|
+
root.subject.common_name "My snazzy root!"
|
28
|
+
root.key_material.generate_key
|
29
|
+
root.signing_entity = true
|
30
|
+
root.sign!
|
31
|
+
|
32
|
+
intermediate = CertificateAuthority::Certificate.new
|
33
|
+
intermediate.subject.common_name "My snazzy intermediate!"
|
34
|
+
intermediate.key_material.generate_key
|
35
|
+
intermediate.signing_entity = true
|
36
|
+
intermediate.parent = root
|
37
|
+
intermediate.sign!
|
38
|
+
|
39
|
+
== Creating a terminal (non-signing) cert
|
40
|
+
|
41
|
+
require 'certificate_authority'
|
42
|
+
plain_cert = CertificateAuthority::Certificate.new
|
43
|
+
plain_cert.subject.common_name "http://mydomain.com"
|
44
|
+
plain_cert.key_material.generate_key
|
45
|
+
plain_cert.parent = root # or intermediate
|
46
|
+
plain_cert.sign!
|
47
|
+
|
48
|
+
== Getting the certificate body
|
49
|
+
|
50
|
+
...
|
51
|
+
certificate.sign!
|
52
|
+
certificate.to_pem # <= Returns a PEM formatted string of your certificate
|
53
|
+
certificate.key_material.private_key.to_pem # <= If you need the private key (and it's in memory)
|
54
|
+
|
55
|
+
= Coming Soon
|
56
|
+
|
57
|
+
* More PKCS#11 hardware (I need driver support from the manufacturers)
|
58
|
+
* Configurable V3 extensions for all the extended functionality
|
59
|
+
|
60
|
+
== Meta
|
61
|
+
|
62
|
+
Written by Chris Chandler(http://chrischandler.name) of Flatterline(http://flatterline.com)
|
63
|
+
|
64
|
+
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
65
|
+
|
66
|
+
Main page: http://github.com/cchandler/certificateauthority
|
67
|
+
|
68
|
+
Issue tracking: https://github.com/cchandler/certificateauthority/issues
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'rspec'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
desc 'Default: run specs.'
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
require 'jeweler'
|
20
|
+
Jeweler::Tasks.new do |gem|
|
21
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
22
|
+
gem.name = "certificate_authority"
|
23
|
+
gem.homepage = "http://github.com/cchandler/certificate_authority"
|
24
|
+
gem.license = "MIT"
|
25
|
+
gem.summary = 'Ruby gem for managing the core functions outlined in RFC-3280 for PKI'
|
26
|
+
# gem.description = ''
|
27
|
+
gem.email = "chris@flatterline.com"
|
28
|
+
gem.authors = ["Chris Chandler"]
|
29
|
+
|
30
|
+
gem.add_dependency('activemodel', '3.0.6')
|
31
|
+
end
|
32
|
+
Jeweler::RubygemsDotOrgTasks.new
|
33
|
+
|
34
|
+
task :spec do
|
35
|
+
Rake::Task["spec:units"].invoke
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :spec do
|
39
|
+
desc "Run unit specs."
|
40
|
+
RSpec::Core::RakeTask.new(:units) do |t|
|
41
|
+
t.rspec_opts = ['--colour --format progress --tag ~pkcs11']
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Run integration specs."
|
45
|
+
RSpec::Core::RakeTask.new(:integrations) do |t|
|
46
|
+
t.rspec_opts = ['--colour --format progress']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
RSpec::Core::RakeTask.new(:doc) do |t|
|
51
|
+
t.rspec_opts = ['--format specdoc ']
|
52
|
+
end
|
data/VERSION.yml
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{certificate_authority}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Chris Chandler"]
|
12
|
+
s.date = %q{2011-04-20}
|
13
|
+
s.email = %q{chris@flatterline.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
"Gemfile",
|
19
|
+
"Gemfile.lock",
|
20
|
+
"README.rdoc",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION.yml",
|
23
|
+
"certificate_authority.gemspec",
|
24
|
+
"lib/certificate_authority.rb",
|
25
|
+
"lib/certificate_authority/certificate.rb",
|
26
|
+
"lib/certificate_authority/certificate_revocation_list.rb",
|
27
|
+
"lib/certificate_authority/distinguished_name.rb",
|
28
|
+
"lib/certificate_authority/extensions.rb",
|
29
|
+
"lib/certificate_authority/key_material.rb",
|
30
|
+
"lib/certificate_authority/ocsp_handler.rb",
|
31
|
+
"lib/certificate_authority/pkcs11_key_material.rb",
|
32
|
+
"lib/certificate_authority/serial_number.rb",
|
33
|
+
"lib/certificate_authority/signing_entity.rb",
|
34
|
+
"lib/tasks/certificate_authority.rake",
|
35
|
+
"spec/spec_helper.rb",
|
36
|
+
"spec/units/certificate_authority_spec.rb",
|
37
|
+
"spec/units/certificate_revocation_list_spec.rb",
|
38
|
+
"spec/units/certificate_spec.rb",
|
39
|
+
"spec/units/distinguished_name_spec.rb",
|
40
|
+
"spec/units/extensions_spec.rb",
|
41
|
+
"spec/units/key_material_spec.rb",
|
42
|
+
"spec/units/ocsp_handler_spec.rb",
|
43
|
+
"spec/units/serial_number_spec.rb",
|
44
|
+
"spec/units/signing_entity_spec.rb",
|
45
|
+
"spec/units/units_helper.rb"
|
46
|
+
]
|
47
|
+
s.homepage = %q{http://github.com/cchandler/certificate_authority}
|
48
|
+
s.licenses = ["MIT"]
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = %q{1.7.2}
|
51
|
+
s.summary = %q{Ruby gem for managing the core functions outlined in RFC-3280 for PKI}
|
52
|
+
s.test_files = [
|
53
|
+
"spec/spec_helper.rb",
|
54
|
+
"spec/units/certificate_authority_spec.rb",
|
55
|
+
"spec/units/certificate_revocation_list_spec.rb",
|
56
|
+
"spec/units/certificate_spec.rb",
|
57
|
+
"spec/units/distinguished_name_spec.rb",
|
58
|
+
"spec/units/extensions_spec.rb",
|
59
|
+
"spec/units/key_material_spec.rb",
|
60
|
+
"spec/units/ocsp_handler_spec.rb",
|
61
|
+
"spec/units/serial_number_spec.rb",
|
62
|
+
"spec/units/signing_entity_spec.rb",
|
63
|
+
"spec/units/units_helper.rb"
|
64
|
+
]
|
65
|
+
|
66
|
+
if s.respond_to? :specification_version then
|
67
|
+
s.specification_version = 3
|
68
|
+
|
69
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
70
|
+
s.add_runtime_dependency(%q<activemodel>, [">= 0"])
|
71
|
+
s.add_runtime_dependency(%q<rspec>, [">= 0"])
|
72
|
+
s.add_runtime_dependency(%q<jeweler>, ["~> 1.5.2"])
|
73
|
+
s.add_runtime_dependency(%q<rcov>, [">= 0"])
|
74
|
+
s.add_runtime_dependency(%q<activemodel>, ["= 3.0.6"])
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<activemodel>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
78
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
79
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
80
|
+
s.add_dependency(%q<activemodel>, ["= 3.0.6"])
|
81
|
+
end
|
82
|
+
else
|
83
|
+
s.add_dependency(%q<activemodel>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
85
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
86
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
87
|
+
s.add_dependency(%q<activemodel>, ["= 3.0.6"])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class Certificate
|
3
|
+
# include SigningEntity
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
attr_accessor :distinguished_name
|
7
|
+
attr_accessor :serial_number
|
8
|
+
attr_accessor :key_material
|
9
|
+
attr_accessor :not_before
|
10
|
+
attr_accessor :not_after
|
11
|
+
attr_accessor :revoked_at
|
12
|
+
attr_accessor :extensions
|
13
|
+
attr_accessor :openssl_body
|
14
|
+
|
15
|
+
alias :subject :distinguished_name #Same thing as the DN
|
16
|
+
|
17
|
+
attr_accessor :parent
|
18
|
+
|
19
|
+
validate do |certificate|
|
20
|
+
errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
|
21
|
+
errors.add :base, "Key material name must be valid" unless key_material.valid?
|
22
|
+
errors.add :base, "Serial number must be valid" unless serial_number.valid?
|
23
|
+
errors.add :base, "Extensions must be valid" unless extensions.each do |item|
|
24
|
+
return true unless item.respond_to?(:valid?)
|
25
|
+
item.valid?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
self.distinguished_name = DistinguishedName.new
|
31
|
+
self.serial_number = SerialNumber.new
|
32
|
+
self.key_material = MemoryKeyMaterial.new
|
33
|
+
self.not_before = Time.now
|
34
|
+
self.not_after = Time.now + 60 * 60 * 24 * 365 #One year
|
35
|
+
self.parent = self
|
36
|
+
self.extensions = load_extensions()
|
37
|
+
|
38
|
+
self.signing_entity = false
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def sign!(signing_profile={})
|
43
|
+
raise "Invalid certificate" unless valid?
|
44
|
+
merge_profile_with_extensions(signing_profile)
|
45
|
+
|
46
|
+
openssl_cert = OpenSSL::X509::Certificate.new
|
47
|
+
openssl_cert.version = 2
|
48
|
+
openssl_cert.not_before = self.not_before
|
49
|
+
openssl_cert.not_after = self.not_after
|
50
|
+
openssl_cert.public_key = self.key_material.public_key
|
51
|
+
|
52
|
+
openssl_cert.serial = self.serial_number.number
|
53
|
+
|
54
|
+
openssl_cert.subject = self.distinguished_name.to_x509_name
|
55
|
+
openssl_cert.issuer = parent.distinguished_name.to_x509_name
|
56
|
+
|
57
|
+
require 'tempfile'
|
58
|
+
t = Tempfile.new("bullshit_conf")
|
59
|
+
# t = File.new("/tmp/openssl.cnf")
|
60
|
+
## The config requires a file even though we won't use it
|
61
|
+
openssl_config = OpenSSL::Config.new(t.path)
|
62
|
+
|
63
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
64
|
+
factory.subject_certificate = openssl_cert
|
65
|
+
|
66
|
+
#NB: If the parent doesn't have an SSL body we're making this a self-signed cert
|
67
|
+
if parent.openssl_body.nil?
|
68
|
+
factory.issuer_certificate = openssl_cert
|
69
|
+
else
|
70
|
+
factory.issuer_certificate = parent.openssl_body
|
71
|
+
end
|
72
|
+
|
73
|
+
self.extensions.keys.each do |k|
|
74
|
+
config_extensions = extensions[k].config_extensions
|
75
|
+
openssl_config = merge_options(openssl_config,config_extensions)
|
76
|
+
end
|
77
|
+
|
78
|
+
# p openssl_config.sections
|
79
|
+
|
80
|
+
factory.config = openssl_config
|
81
|
+
|
82
|
+
self.extensions.keys.each do |k|
|
83
|
+
e = extensions[k]
|
84
|
+
next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it
|
85
|
+
ext = factory.create_ext(e.openssl_identifier, e.to_s)
|
86
|
+
openssl_cert.add_extension(ext)
|
87
|
+
end
|
88
|
+
|
89
|
+
digest = OpenSSL::Digest::Digest.new("SHA512")
|
90
|
+
self.openssl_body = openssl_cert.sign(parent.key_material.private_key,digest)
|
91
|
+
t.close! if t.is_a?(Tempfile)# We can get rid of the ridiculous temp file
|
92
|
+
self.openssl_body
|
93
|
+
end
|
94
|
+
|
95
|
+
def is_signing_entity?
|
96
|
+
self.extensions["basicConstraints"].ca
|
97
|
+
end
|
98
|
+
|
99
|
+
def signing_entity=(signing)
|
100
|
+
self.extensions["basicConstraints"].ca = signing
|
101
|
+
end
|
102
|
+
|
103
|
+
def revoked?
|
104
|
+
!self.revoked_at.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_pem
|
108
|
+
raise "Certificate has no signed body" if self.openssl_body.nil?
|
109
|
+
self.openssl_body.to_pem
|
110
|
+
end
|
111
|
+
|
112
|
+
def is_root_entity?
|
113
|
+
self.parent == self && is_signing_entity?
|
114
|
+
end
|
115
|
+
|
116
|
+
def is_intermediate_entity?
|
117
|
+
(self.parent != self) && is_signing_entity?
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def merge_profile_with_extensions(signing_profile={})
|
123
|
+
return self.extensions if signing_profile["extensions"].nil?
|
124
|
+
signing_config = signing_profile["extensions"]
|
125
|
+
signing_config.keys.each do |k|
|
126
|
+
extension = self.extensions[k]
|
127
|
+
items = signing_config[k]
|
128
|
+
items.keys.each do |profile_item_key|
|
129
|
+
if extension.respond_to?("#{profile_item_key}=".to_sym)
|
130
|
+
extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
|
131
|
+
else
|
132
|
+
p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def load_extensions
|
139
|
+
extension_hash = {}
|
140
|
+
|
141
|
+
temp_extensions = []
|
142
|
+
basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
|
143
|
+
temp_extensions << basic_constraints
|
144
|
+
crl_distribution_points = CertificateAuthority::Extensions::CrlDistributionPoints.new
|
145
|
+
temp_extensions << crl_distribution_points
|
146
|
+
subject_key_identifier = CertificateAuthority::Extensions::SubjectKeyIdentifier.new
|
147
|
+
temp_extensions << subject_key_identifier
|
148
|
+
authority_key_identifier = CertificateAuthority::Extensions::AuthorityKeyIdentifier.new
|
149
|
+
temp_extensions << authority_key_identifier
|
150
|
+
authority_info_access = CertificateAuthority::Extensions::AuthorityInfoAccess.new
|
151
|
+
temp_extensions << authority_info_access
|
152
|
+
key_usage = CertificateAuthority::Extensions::KeyUsage.new
|
153
|
+
temp_extensions << key_usage
|
154
|
+
extended_key_usage = CertificateAuthority::Extensions::ExtendedKeyUsage.new
|
155
|
+
temp_extensions << extended_key_usage
|
156
|
+
subject_alternative_name = CertificateAuthority::Extensions::SubjectAlternativeName.new
|
157
|
+
temp_extensions << subject_alternative_name
|
158
|
+
certificate_policies = CertificateAuthority::Extensions::CertificatePolicies.new
|
159
|
+
temp_extensions << certificate_policies
|
160
|
+
|
161
|
+
temp_extensions.each do |extension|
|
162
|
+
extension_hash[extension.openssl_identifier] = extension
|
163
|
+
end
|
164
|
+
|
165
|
+
extension_hash
|
166
|
+
end
|
167
|
+
|
168
|
+
def merge_options(config,hash)
|
169
|
+
hash.keys.each do |k|
|
170
|
+
config[k] = hash[k]
|
171
|
+
end
|
172
|
+
config
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module CertificateAuthority
|
2
|
+
class CertificateRevocationList
|
3
|
+
include ActiveModel::Validations
|
4
|
+
|
5
|
+
attr_accessor :certificates
|
6
|
+
attr_accessor :parent
|
7
|
+
attr_accessor :crl_body
|
8
|
+
attr_accessor :next_update
|
9
|
+
|
10
|
+
validate do |crl|
|
11
|
+
errors.add :next_update, "Next update must be a positive value" if crl.next_update < 0
|
12
|
+
errors.add :parent, "A parent entity must be set" if crl.parent.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
self.certificates = []
|
17
|
+
self.next_update = 60 * 60 * 4 # 4 hour default
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(cert)
|
21
|
+
raise "Only revoked certificates can be added to a CRL" unless cert.revoked?
|
22
|
+
self.certificates << cert
|
23
|
+
end
|
24
|
+
|
25
|
+
def sign!
|
26
|
+
raise "No parent entity has been set!" if self.parent.nil?
|
27
|
+
raise "Invalid CRL" unless self.valid?
|
28
|
+
|
29
|
+
revocations = self.certificates.collect do |certificate|
|
30
|
+
revocation = OpenSSL::X509::Revoked.new
|
31
|
+
x509_cert = OpenSSL::X509::Certificate.new(certificate.to_pem)
|
32
|
+
revocation.serial = x509_cert.serial
|
33
|
+
revocation.time = certificate.revoked_at
|
34
|
+
revocation
|
35
|
+
end
|
36
|
+
|
37
|
+
crl = OpenSSL::X509::CRL.new
|
38
|
+
revocations.each do |revocation|
|
39
|
+
crl.add_revoked(revocation)
|
40
|
+
end
|
41
|
+
|
42
|
+
crl.version = 1
|
43
|
+
crl.last_update = Time.now
|
44
|
+
crl.next_update = Time.now + self.next_update
|
45
|
+
|
46
|
+
signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem)
|
47
|
+
digest = OpenSSL::Digest::Digest.new("SHA512")
|
48
|
+
crl.issuer = signing_cert.subject
|
49
|
+
self.crl_body = crl.sign(self.parent.key_material.private_key, digest)
|
50
|
+
|
51
|
+
self.crl_body
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_pem
|
55
|
+
raise "No signed CRL body" if self.crl_body.nil?
|
56
|
+
self.crl_body.to_pem
|
57
|
+
end
|
58
|
+
end#CertificateRevocationList
|
59
|
+
end
|