certificate_authority 0.1.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -8
- data/Gemfile.lock +71 -27
- data/README.rdoc +184 -89
- data/Rakefile +6 -41
- data/certificate_authority.gemspec +22 -81
- data/lib/certificate_authority.rb +7 -6
- data/lib/certificate_authority/certificate.rb +151 -71
- data/lib/certificate_authority/certificate_revocation_list.rb +46 -26
- data/lib/certificate_authority/core_extensions.rb +46 -0
- data/lib/certificate_authority/distinguished_name.rb +84 -17
- data/lib/certificate_authority/extensions.rb +483 -96
- data/lib/certificate_authority/key_material.rb +75 -21
- data/lib/certificate_authority/ocsp_handler.rb +99 -29
- data/lib/certificate_authority/pkcs11_key_material.rb +13 -15
- data/lib/certificate_authority/revocable.rb +14 -0
- data/lib/certificate_authority/serial_number.rb +18 -5
- data/lib/certificate_authority/signing_entity.rb +5 -7
- data/lib/certificate_authority/signing_request.rb +91 -0
- data/lib/certificate_authority/validations.rb +31 -0
- data/lib/certificate_authority/version.rb +3 -0
- metadata +96 -94
- data/VERSION.yml +0 -5
- data/spec/spec_helper.rb +0 -4
- data/spec/units/certificate_authority_spec.rb +0 -4
- data/spec/units/certificate_revocation_list_spec.rb +0 -68
- data/spec/units/certificate_spec.rb +0 -351
- data/spec/units/distinguished_name_spec.rb +0 -38
- data/spec/units/extensions_spec.rb +0 -53
- data/spec/units/key_material_spec.rb +0 -96
- data/spec/units/ocsp_handler_spec.rb +0 -104
- data/spec/units/serial_number_spec.rb +0 -20
- data/spec/units/signing_entity_spec.rb +0 -4
- data/spec/units/units_helper.rb +0 -1
@@ -3,60 +3,114 @@ module CertificateAuthority
|
|
3
3
|
def public_key
|
4
4
|
raise "Required implementation"
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def private_key
|
8
8
|
raise "Required implementation"
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def is_in_hardware?
|
12
12
|
raise "Required implementation"
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def is_in_memory?
|
16
16
|
raise "Required implementation"
|
17
17
|
end
|
18
|
+
|
19
|
+
def self.from_x509_key_pair(pair,password=nil)
|
20
|
+
if password.nil?
|
21
|
+
key = OpenSSL::PKey::RSA.new(pair)
|
22
|
+
else
|
23
|
+
key = OpenSSL::PKey::RSA.new(pair,password)
|
24
|
+
end
|
25
|
+
mem_key = MemoryKeyMaterial.new
|
26
|
+
mem_key.public_key = key.public_key
|
27
|
+
mem_key.private_key = key
|
28
|
+
mem_key
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.from_x509_public_key(public_key_pem)
|
32
|
+
key = OpenSSL::PKey::RSA.new(public_key_pem)
|
33
|
+
signing_request_key = SigningRequestKeyMaterial.new
|
34
|
+
signing_request_key.public_key = key.public_key
|
35
|
+
signing_request_key
|
36
|
+
end
|
18
37
|
end
|
19
|
-
|
38
|
+
|
20
39
|
class MemoryKeyMaterial
|
21
40
|
include KeyMaterial
|
22
|
-
include
|
23
|
-
|
41
|
+
include Validations
|
42
|
+
|
24
43
|
attr_accessor :keypair
|
25
44
|
attr_accessor :private_key
|
26
45
|
attr_accessor :public_key
|
27
|
-
|
46
|
+
|
28
47
|
def initialize
|
29
48
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
49
|
+
|
50
|
+
def validate
|
51
|
+
if private_key.nil?
|
52
|
+
errors.add :private_key, "cannot be blank"
|
53
|
+
end
|
54
|
+
if public_key.nil?
|
55
|
+
errors.add :public_key, "cannot be blank"
|
56
|
+
end
|
36
57
|
end
|
37
|
-
|
58
|
+
|
38
59
|
def is_in_hardware?
|
39
60
|
false
|
40
61
|
end
|
41
|
-
|
62
|
+
|
42
63
|
def is_in_memory?
|
43
64
|
true
|
44
65
|
end
|
45
|
-
|
46
|
-
def generate_key(modulus_bits=
|
66
|
+
|
67
|
+
def generate_key(modulus_bits=2048)
|
47
68
|
self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
|
48
69
|
self.private_key = keypair
|
49
70
|
self.public_key = keypair.public_key
|
50
71
|
self.keypair
|
51
72
|
end
|
52
|
-
|
73
|
+
|
53
74
|
def private_key
|
54
75
|
@private_key
|
55
76
|
end
|
56
|
-
|
77
|
+
|
78
|
+
def public_key
|
79
|
+
@public_key
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class SigningRequestKeyMaterial
|
84
|
+
include KeyMaterial
|
85
|
+
include Validations
|
86
|
+
|
87
|
+
def validate
|
88
|
+
errors.add :public_key, "cannot be blank" if public_key.nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_accessor :public_key
|
92
|
+
|
93
|
+
def initialize(request=nil)
|
94
|
+
if request.is_a? OpenSSL::X509::Request
|
95
|
+
raise "Invalid certificate signing request" unless request.verify request.public_key
|
96
|
+
self.public_key = request.public_key
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def is_in_hardware?
|
101
|
+
false
|
102
|
+
end
|
103
|
+
|
104
|
+
def is_in_memory?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def private_key
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
57
112
|
def public_key
|
58
113
|
@public_key
|
59
114
|
end
|
60
|
-
|
61
115
|
end
|
62
|
-
end
|
116
|
+
end
|
@@ -1,77 +1,147 @@
|
|
1
1
|
module CertificateAuthority
|
2
|
+
class OCSPResponseBuilder
|
3
|
+
attr_accessor :ocsp_response
|
4
|
+
attr_accessor :verification_mechanism
|
5
|
+
attr_accessor :ocsp_request_reader
|
6
|
+
attr_accessor :parent
|
7
|
+
attr_accessor :next_update
|
8
|
+
|
9
|
+
GOOD = OpenSSL::OCSP::V_CERTSTATUS_GOOD
|
10
|
+
REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED
|
11
|
+
|
12
|
+
NO_REASON=0
|
13
|
+
KEY_COMPROMISED=OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
|
14
|
+
UNSPECIFIED=OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED
|
15
|
+
|
16
|
+
def build_response()
|
17
|
+
raise "Requires a parent for signing" if @parent.nil?
|
18
|
+
if @verification_mechanism.nil?
|
19
|
+
## If no verification callback is provided we're marking it GOOD
|
20
|
+
@verification_mechanism = lambda {|cert_id| [GOOD,NO_REASON] }
|
21
|
+
end
|
22
|
+
|
23
|
+
@ocsp_request_reader.ocsp_request.certid.each do |cert_id|
|
24
|
+
result,reason = verification_mechanism.call(cert_id.serial)
|
25
|
+
|
26
|
+
## cert_id, status, reason, rev_time, this update, next update, ext
|
27
|
+
## - unit of time is seconds
|
28
|
+
## - rev_time is currently set to "now"
|
29
|
+
@ocsp_response.add_status(cert_id,
|
30
|
+
result, reason,
|
31
|
+
0, 0, @next_update, nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
@ocsp_response.sign(OpenSSL::X509::Certificate.new(@parent.to_pem), @parent.key_material.private_key, nil, nil)
|
35
|
+
OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, @ocsp_response)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.from_request_reader(request_reader,verification_mechanism=nil)
|
39
|
+
response_builder = OCSPResponseBuilder.new
|
40
|
+
response_builder.ocsp_request_reader = request_reader
|
41
|
+
|
42
|
+
ocsp_response = OpenSSL::OCSP::BasicResponse.new
|
43
|
+
ocsp_response.copy_nonce(request_reader.ocsp_request)
|
44
|
+
response_builder.ocsp_response = ocsp_response
|
45
|
+
response_builder.next_update = 60*15 #Default of 15 minutes
|
46
|
+
response_builder
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class OCSPRequestReader
|
51
|
+
attr_accessor :raw_ocsp_request
|
52
|
+
attr_accessor :ocsp_request
|
53
|
+
|
54
|
+
def serial_numbers
|
55
|
+
@ocsp_request.certid.collect do |cert_id|
|
56
|
+
cert_id.serial
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.from_der(request_body)
|
61
|
+
reader = OCSPRequestReader.new
|
62
|
+
reader.raw_ocsp_request = request_body
|
63
|
+
reader.ocsp_request = OpenSSL::OCSP::Request.new(request_body)
|
64
|
+
|
65
|
+
reader
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
## DEPRECATED
|
2
70
|
class OCSPHandler
|
3
|
-
include
|
4
|
-
|
71
|
+
include Validations
|
72
|
+
|
5
73
|
attr_accessor :ocsp_request
|
6
74
|
attr_accessor :certificate_ids
|
7
|
-
|
75
|
+
|
8
76
|
attr_accessor :certificates
|
9
77
|
attr_accessor :parent
|
10
|
-
|
78
|
+
|
11
79
|
attr_accessor :ocsp_response_body
|
12
|
-
|
13
|
-
validate
|
80
|
+
|
81
|
+
def validate
|
14
82
|
errors.add :parent, "A parent entity must be set" if parent.nil?
|
83
|
+
all_certificates_available
|
15
84
|
end
|
16
|
-
|
17
|
-
|
85
|
+
|
18
86
|
def initialize
|
19
87
|
self.certificates = {}
|
20
88
|
end
|
21
|
-
|
89
|
+
|
22
90
|
def <<(cert)
|
23
91
|
self.certificates[cert.serial_number.number.to_s] = cert
|
24
92
|
end
|
25
|
-
|
93
|
+
|
26
94
|
def extract_certificate_serials
|
27
|
-
|
28
|
-
|
29
|
-
|
95
|
+
openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request)
|
96
|
+
|
97
|
+
if openssl_request.certid.nil?
|
98
|
+
raise "Invalid openssl request"
|
99
|
+
end
|
30
100
|
self.certificate_ids = openssl_request.certid.collect do |cert_id|
|
31
101
|
cert_id.serial
|
32
102
|
end
|
33
|
-
|
103
|
+
|
34
104
|
self.certificate_ids
|
35
105
|
end
|
36
|
-
|
37
|
-
|
106
|
+
|
107
|
+
|
38
108
|
def response
|
39
109
|
raise "Invalid response" unless valid?
|
40
|
-
|
110
|
+
|
41
111
|
openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
|
42
112
|
openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
|
43
113
|
openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
|
44
|
-
|
114
|
+
|
45
115
|
openssl_ocsp_request.certid.each do |cert_id|
|
46
116
|
certificate = self.certificates[cert_id.serial.to_s]
|
47
|
-
|
48
|
-
openssl_ocsp_response.add_status(cert_id,
|
49
|
-
OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
|
117
|
+
|
118
|
+
openssl_ocsp_response.add_status(cert_id,
|
119
|
+
OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
|
50
120
|
0, 0, 30, nil)
|
51
121
|
end
|
52
|
-
|
53
|
-
|
122
|
+
|
123
|
+
|
54
124
|
openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
|
55
125
|
final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
|
56
126
|
self.ocsp_response_body = final_response
|
57
127
|
self.ocsp_response_body
|
58
128
|
end
|
59
|
-
|
129
|
+
|
60
130
|
def to_der
|
61
131
|
raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
|
62
132
|
self.ocsp_response_body.to_der
|
63
133
|
end
|
64
|
-
|
134
|
+
|
65
135
|
private
|
66
|
-
|
136
|
+
|
67
137
|
def all_certificates_available
|
68
138
|
openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
|
69
|
-
|
139
|
+
|
70
140
|
openssl_ocsp_request.certid.each do |cert_id|
|
71
141
|
certificate = self.certificates[cert_id.serial.to_s]
|
72
142
|
errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
|
73
143
|
end
|
74
144
|
end
|
75
|
-
|
145
|
+
|
76
146
|
end
|
77
|
-
end
|
147
|
+
end
|
@@ -1,45 +1,43 @@
|
|
1
1
|
module CertificateAuthority
|
2
2
|
class Pkcs11KeyMaterial
|
3
3
|
include KeyMaterial
|
4
|
-
|
5
|
-
include ActiveModel::Serialization
|
6
|
-
|
4
|
+
|
7
5
|
attr_accessor :engine
|
8
6
|
attr_accessor :token_id
|
9
7
|
attr_accessor :pkcs11_lib
|
10
8
|
attr_accessor :openssl_pkcs11_engine_lib
|
11
9
|
attr_accessor :pin
|
12
|
-
|
10
|
+
|
13
11
|
def initialize(attributes = {})
|
14
12
|
@attributes = attributes
|
15
13
|
initialize_engine
|
16
14
|
end
|
17
|
-
|
15
|
+
|
18
16
|
def is_in_hardware?
|
19
17
|
true
|
20
18
|
end
|
21
|
-
|
19
|
+
|
22
20
|
def is_in_memory?
|
23
21
|
false
|
24
22
|
end
|
25
|
-
|
23
|
+
|
26
24
|
def generate_key(modulus_bits=1024)
|
27
25
|
puts "Key generation is not currently supported in hardware"
|
28
26
|
nil
|
29
27
|
end
|
30
|
-
|
28
|
+
|
31
29
|
def private_key
|
32
30
|
initialize_engine
|
33
31
|
self.engine.load_private_key(self.token_id)
|
34
32
|
end
|
35
|
-
|
33
|
+
|
36
34
|
def public_key
|
37
35
|
initialize_engine
|
38
36
|
self.engine.load_public_key(self.token_id)
|
39
37
|
end
|
40
|
-
|
38
|
+
|
41
39
|
private
|
42
|
-
|
40
|
+
|
43
41
|
def initialize_engine
|
44
42
|
## We're going to return early and try again later if params weren't passed in
|
45
43
|
## at initialization. Any attempt at getting a public/private key will try
|
@@ -47,7 +45,7 @@ module CertificateAuthority
|
|
47
45
|
return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
|
48
46
|
return self.engine unless self.engine.nil?
|
49
47
|
OpenSSL::Engine.load
|
50
|
-
|
48
|
+
|
51
49
|
pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
|
52
50
|
e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
|
53
51
|
e.ctrl_cmd("ID","pkcs11")
|
@@ -56,10 +54,10 @@ module CertificateAuthority
|
|
56
54
|
e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
|
57
55
|
e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
|
58
56
|
end
|
59
|
-
|
57
|
+
|
60
58
|
self.engine = pkcs11
|
61
59
|
pkcs11
|
62
60
|
end
|
63
|
-
|
61
|
+
|
64
62
|
end
|
65
|
-
end
|
63
|
+
end
|
@@ -1,9 +1,22 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
1
3
|
module CertificateAuthority
|
2
4
|
class SerialNumber
|
3
|
-
include
|
4
|
-
|
5
|
+
include Validations
|
6
|
+
include Revocable
|
7
|
+
|
5
8
|
attr_accessor :number
|
6
|
-
|
7
|
-
|
9
|
+
|
10
|
+
def validate
|
11
|
+
if self.number.nil?
|
12
|
+
errors.add :number, "must not be empty"
|
13
|
+
elsif self.number.to_i <= 0
|
14
|
+
errors.add :number, "must be greater than zero"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
self.number = SecureRandom.random_number(2**128-1)
|
20
|
+
end
|
8
21
|
end
|
9
|
-
end
|
22
|
+
end
|
@@ -1,18 +1,16 @@
|
|
1
1
|
module CertificateAuthority
|
2
2
|
module SigningEntity
|
3
|
-
|
3
|
+
|
4
4
|
def self.included(mod)
|
5
5
|
mod.class_eval do
|
6
|
-
attr_accessor :signing_entity
|
6
|
+
attr_accessor :signing_entity
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def signing_entity=(val)
|
11
11
|
raise "invalid param" unless [true,false].include?(val)
|
12
12
|
@signing_entity = val
|
13
13
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
|
17
15
|
end
|
18
|
-
end
|
16
|
+
end
|