certificate_authority 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|