certificate_authority 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ module CertificateAuthority
2
+ class DistinguishedName
3
+ include ActiveModel::Validations
4
+
5
+ validates_presence_of :common_name
6
+
7
+ attr_accessor :common_name
8
+ alias :cn :common_name
9
+
10
+ attr_accessor :locality
11
+ alias :l :locality
12
+
13
+ attr_accessor :state
14
+ alias :s :state
15
+
16
+ attr_accessor :country
17
+ alias :c :country
18
+
19
+ attr_accessor :organization
20
+ alias :o :organization
21
+
22
+ attr_accessor :organizational_unit
23
+ alias :ou :organizational_unit
24
+
25
+ def to_x509_name
26
+ raise "Invalid Distinguished Name" unless valid?
27
+
28
+ # NB: the capitalization in the strings counts
29
+ name = OpenSSL::X509::Name.new
30
+ name.add_entry("CN", common_name)
31
+ name.add_entry("O", organization) unless organization.blank?
32
+ name.add_entry("OU", common_name) unless organizational_unit.blank?
33
+ name.add_entry("S", common_name) unless state.blank?
34
+ name.add_entry("L", common_name) unless locality.blank?
35
+
36
+ name
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,251 @@
1
+ module CertificateAuthority
2
+ module Extensions
3
+ module ExtensionAPI
4
+ def to_s
5
+ raise "Implementation required"
6
+ end
7
+
8
+ def config_extensions
9
+ {}
10
+ end
11
+
12
+ def openssl_identifier
13
+ raise "Implementation required"
14
+ end
15
+ end
16
+
17
+ class BasicContraints
18
+ include ExtensionAPI
19
+ include ActiveModel::Validations
20
+ attr_accessor :ca
21
+ attr_accessor :path_len
22
+ validates :ca, :inclusion => [true,false]
23
+
24
+ def initialize
25
+ self.ca = false
26
+ end
27
+
28
+ def is_ca?
29
+ self.ca
30
+ end
31
+
32
+ def path_len=(value)
33
+ raise "path_len must be a non-negative integer" if value < 0 or !value.is_a?(Fixnum)
34
+ @path_len = value
35
+ end
36
+
37
+ def openssl_identifier
38
+ "basicConstraints"
39
+ end
40
+
41
+ def to_s
42
+ result = ""
43
+ result += "CA:#{self.ca}"
44
+ result += ",pathlen:#{self.path_len}" unless self.path_len.nil?
45
+ result
46
+ end
47
+ end
48
+
49
+ class CrlDistributionPoints
50
+ include ExtensionAPI
51
+
52
+ attr_accessor :uri
53
+
54
+ def initialize
55
+ self.uri = "http://moo.crlendPoint.example.com/something.crl"
56
+ end
57
+
58
+ def openssl_identifier
59
+ "crlDistributionPoints"
60
+ end
61
+
62
+ ## NB: At this time it seems OpenSSL's extension handlers don't support
63
+ ## any of the config options the docs claim to support... everything comes back
64
+ ## "missing value" on GENERAL NAME. Even if copied verbatim
65
+ def config_extensions
66
+ {
67
+ # "custom_crl_fields" => {"fullname" => "URI:#{fullname}"},
68
+ # "issuer_sect" => {"CN" => "crlissuer.com", "C" => "US", "O" => "shudder"}
69
+ }
70
+ end
71
+
72
+ def to_s
73
+ "URI:#{self.uri}"
74
+ end
75
+ end
76
+
77
+ class SubjectKeyIdentifier
78
+ include ExtensionAPI
79
+ def openssl_identifier
80
+ "subjectKeyIdentifier"
81
+ end
82
+
83
+ def to_s
84
+ "hash"
85
+ end
86
+ end
87
+
88
+ class AuthorityKeyIdentifier
89
+ include ExtensionAPI
90
+
91
+ def openssl_identifier
92
+ "authorityKeyIdentifier"
93
+ end
94
+
95
+ def to_s
96
+ "keyid,issuer"
97
+ end
98
+ end
99
+
100
+ class AuthorityInfoAccess
101
+ include ExtensionAPI
102
+
103
+ attr_accessor :ocsp
104
+
105
+ def initialize
106
+ self.ocsp = []
107
+ end
108
+
109
+ def openssl_identifier
110
+ "authorityInfoAccess"
111
+ end
112
+
113
+ def to_s
114
+ "OCSP;URI:#{self.ocsp}"
115
+ end
116
+ end
117
+
118
+ class KeyUsage
119
+ include ExtensionAPI
120
+
121
+ attr_accessor :usage
122
+
123
+ def initialize
124
+ self.usage = ["digitalSignature", "nonRepudiation"]
125
+ end
126
+
127
+ def openssl_identifier
128
+ "keyUsage"
129
+ end
130
+
131
+ def to_s
132
+ "#{self.usage.join(',')}"
133
+ end
134
+ end
135
+
136
+ class ExtendedKeyUsage
137
+ include ExtensionAPI
138
+
139
+ attr_accessor :usage
140
+
141
+ def initialize
142
+ self.usage = ["serverAuth","clientAuth"]
143
+ end
144
+
145
+ def openssl_identifier
146
+ "extendedKeyUsage"
147
+ end
148
+
149
+ def to_s
150
+ "#{self.usage.join(',')}"
151
+ end
152
+ end
153
+
154
+ class SubjectAlternativeName
155
+ include ExtensionAPI
156
+
157
+ attr_accessor :uris
158
+
159
+ def initialize
160
+ self.uris = []
161
+ end
162
+
163
+ def uris=(value)
164
+ raise "URIs must be an array" unless value.is_a?(Array)
165
+ @uris = value
166
+ end
167
+
168
+ def openssl_identifier
169
+ "subjectAltName"
170
+ end
171
+
172
+ def to_s
173
+ if self.uris.empty?
174
+ return ""
175
+ end
176
+ "URI:#{self.uris.join(',URI:')}"
177
+ end
178
+ end
179
+
180
+ class CertificatePolicies
181
+ include ExtensionAPI
182
+
183
+ attr_accessor :policy_identifier
184
+ attr_accessor :cps_uris
185
+ ##User notice
186
+ attr_accessor :explicit_text
187
+ attr_accessor :organization
188
+ attr_accessor :notice_numbers
189
+
190
+ def initialize
191
+ @contains_data = false
192
+ end
193
+
194
+
195
+ def openssl_identifier
196
+ "certificatePolicies"
197
+ end
198
+
199
+ def user_notice=(value={})
200
+ value.keys.each do |key|
201
+ self.send("#{key}=".to_sym, value[key])
202
+ end
203
+ end
204
+
205
+ def config_extensions
206
+ config_extension = {}
207
+ custom_policies = {}
208
+ notice = {}
209
+ unless self.policy_identifier.nil?
210
+ custom_policies["policyIdentifier"] = self.policy_identifier
211
+ end
212
+
213
+ if !self.cps_uris.nil? and self.cps_uris.is_a?(Array)
214
+ self.cps_uris.each_with_index do |cps_uri,i|
215
+ custom_policies["CPS.#{i}"] = cps_uri
216
+ end
217
+ end
218
+
219
+ unless self.explicit_text.nil?
220
+ notice["explicitText"] = self.explicit_text
221
+ end
222
+
223
+ unless self.organization.nil?
224
+ notice["organization"] = self.organization
225
+ end
226
+
227
+ unless self.notice_numbers.nil?
228
+ notice["noticeNumbers"] = self.notice_numbers
229
+ end
230
+
231
+ if notice.keys.size > 0
232
+ custom_policies["userNotice.1"] = "@notice"
233
+ config_extension["notice"] = notice
234
+ end
235
+
236
+ if custom_policies.keys.size > 0
237
+ config_extension["custom_policies"] = custom_policies
238
+ @contains_data = true
239
+ end
240
+
241
+ config_extension
242
+ end
243
+
244
+ def to_s
245
+ return "" unless @contains_data
246
+ "ia5org,@custom_policies"
247
+ end
248
+ end
249
+
250
+ end
251
+ end
@@ -0,0 +1,62 @@
1
+ module CertificateAuthority
2
+ module KeyMaterial
3
+ def public_key
4
+ raise "Required implementation"
5
+ end
6
+
7
+ def private_key
8
+ raise "Required implementation"
9
+ end
10
+
11
+ def is_in_hardware?
12
+ raise "Required implementation"
13
+ end
14
+
15
+ def is_in_memory?
16
+ raise "Required implementation"
17
+ end
18
+ end
19
+
20
+ class MemoryKeyMaterial
21
+ include KeyMaterial
22
+ include ActiveModel::Validations
23
+
24
+ attr_accessor :keypair
25
+ attr_accessor :private_key
26
+ attr_accessor :public_key
27
+
28
+ def initialize
29
+ end
30
+
31
+ validates_each :private_key do |record, attr, value|
32
+ record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
33
+ end
34
+ validates_each :public_key do |record, attr, value|
35
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
36
+ end
37
+
38
+ def is_in_hardware?
39
+ false
40
+ end
41
+
42
+ def is_in_memory?
43
+ true
44
+ end
45
+
46
+ def generate_key(modulus_bits=1024)
47
+ self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
48
+ self.private_key = keypair
49
+ self.public_key = keypair.public_key
50
+ self.keypair
51
+ end
52
+
53
+ def private_key
54
+ @private_key
55
+ end
56
+
57
+ def public_key
58
+ @public_key
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,77 @@
1
+ module CertificateAuthority
2
+ class OCSPHandler
3
+ include ActiveModel::Validations
4
+
5
+ attr_accessor :ocsp_request
6
+ attr_accessor :certificate_ids
7
+
8
+ attr_accessor :certificates
9
+ attr_accessor :parent
10
+
11
+ attr_accessor :ocsp_response_body
12
+
13
+ validate do |crl|
14
+ errors.add :parent, "A parent entity must be set" if parent.nil?
15
+ end
16
+ validate :all_certificates_available
17
+
18
+ def initialize
19
+ self.certificates = {}
20
+ end
21
+
22
+ def <<(cert)
23
+ self.certificates[cert.serial_number.number.to_s] = cert
24
+ end
25
+
26
+ def extract_certificate_serials
27
+ raise "No valid OCSP request was supplied" if self.ocsp_request.nil?
28
+ openssl_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
29
+
30
+ self.certificate_ids = openssl_request.certid.collect do |cert_id|
31
+ cert_id.serial
32
+ end
33
+
34
+ self.certificate_ids
35
+ end
36
+
37
+
38
+ def response
39
+ raise "Invalid response" unless valid?
40
+
41
+ openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
42
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
43
+ openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
44
+
45
+ openssl_ocsp_request.certid.each do |cert_id|
46
+ certificate = self.certificates[cert_id.serial.to_s]
47
+
48
+ openssl_ocsp_response.add_status(cert_id,
49
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
50
+ 0, 0, 30, nil)
51
+ end
52
+
53
+
54
+ openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
55
+ final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
56
+ self.ocsp_response_body = final_response
57
+ self.ocsp_response_body
58
+ end
59
+
60
+ def to_der
61
+ raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
62
+ self.ocsp_response_body.to_der
63
+ end
64
+
65
+ private
66
+
67
+ def all_certificates_available
68
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
69
+
70
+ openssl_ocsp_request.certid.each do |cert_id|
71
+ certificate = self.certificates[cert_id.serial.to_s]
72
+ errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
73
+ end
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ module CertificateAuthority
2
+ class Pkcs11KeyMaterial
3
+ include KeyMaterial
4
+ include ActiveModel::Validations
5
+ include ActiveModel::Serialization
6
+
7
+ attr_accessor :engine
8
+ attr_accessor :token_id
9
+ attr_accessor :pkcs11_lib
10
+ attr_accessor :openssl_pkcs11_engine_lib
11
+ attr_accessor :pin
12
+
13
+ def initialize(attributes = {})
14
+ @attributes = attributes
15
+ initialize_engine
16
+ end
17
+
18
+ def is_in_hardware?
19
+ true
20
+ end
21
+
22
+ def is_in_memory?
23
+ false
24
+ end
25
+
26
+ def generate_key(modulus_bits=1024)
27
+ puts "Key generation is not currently supported in hardware"
28
+ nil
29
+ end
30
+
31
+ def private_key
32
+ initialize_engine
33
+ self.engine.load_private_key(self.token_id)
34
+ end
35
+
36
+ def public_key
37
+ initialize_engine
38
+ self.engine.load_public_key(self.token_id)
39
+ end
40
+
41
+ private
42
+
43
+ def initialize_engine
44
+ ## We're going to return early and try again later if params weren't passed in
45
+ ## at initialization. Any attempt at getting a public/private key will try
46
+ ## again.
47
+ return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
48
+ return self.engine unless self.engine.nil?
49
+ OpenSSL::Engine.load
50
+
51
+ pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
52
+ e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
53
+ e.ctrl_cmd("ID","pkcs11")
54
+ e.ctrl_cmd("LIST_ADD","1")
55
+ e.ctrl_cmd("LOAD")
56
+ e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
57
+ e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
58
+ end
59
+
60
+ self.engine = pkcs11
61
+ pkcs11
62
+ end
63
+
64
+ end
65
+ end