certificate_authority 0.1.2 → 1.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 +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
|