certificate_authority 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +92 -86
- data/VERSION.yml +2 -2
- data/certificate_authority.gemspec +9 -7
- data/lib/certificate_authority.rb +1 -1
- data/lib/certificate_authority/certificate.rb +60 -38
- data/lib/certificate_authority/certificate_revocation_list.rb +12 -12
- data/lib/certificate_authority/distinguished_name.rb +33 -14
- data/lib/certificate_authority/extensions.rb +77 -63
- data/lib/certificate_authority/key_material.rb +48 -15
- data/lib/certificate_authority/ocsp_handler.rb +24 -24
- data/lib/certificate_authority/pkcs11_key_material.rb +13 -13
- data/lib/certificate_authority/serial_number.rb +3 -3
- data/lib/certificate_authority/signing_entity.rb +5 -7
- data/spec/units/certificate_revocation_list_spec.rb +16 -16
- data/spec/units/certificate_spec.rb +149 -84
- data/spec/units/distinguished_name_spec.rb +26 -5
- data/spec/units/extensions_spec.rb +72 -10
- data/spec/units/key_material_spec.rb +69 -65
- data/spec/units/ocsp_handler_spec.rb +20 -20
- data/spec/units/pkcs11_key_material_spec.rb +41 -0
- data/spec/units/serial_number_spec.rb +4 -4
- metadata +50 -53
data/README.rdoc
CHANGED
@@ -24,14 +24,15 @@ Let's look at a complete example for generating a new root certificate. Assuming
|
|
24
24
|
|
25
25
|
Generating a self-signed root certificate is fairly easy:
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
require 'certificate_authority'
|
28
|
+
root = CertificateAuthority::Certificate.new
|
29
|
+
root.subject.common_name= "http://mydomain.com"
|
30
|
+
root.serial_number.number=1
|
31
|
+
root.key_material.generate_key
|
32
|
+
root.signing_entity = true
|
33
|
+
signing_profile = {"extensions" => {"keyUsage" => {"usage" => ["critical", "keyCertSign"] }} }
|
34
|
+
root.sign!(signing_profile)
|
35
|
+
|
35
36
|
The required elements for the gem at this time are a common name for the subject and a serial number for the certificate. Since this is our self-signed root we're going to give it the first serial available of 1. Because certificate_authority is not designed to manage the issuance lifecycle you'll be expected to store serial numbers yourself.
|
36
37
|
|
37
38
|
Next, after taking care of required fields, we will require key material for the new certificate. There's a convenience method made available on the key_material object for generating new keys. The private key will be available in:
|
@@ -48,29 +49,30 @@ Lastly, we declare that the certificate we're about to sign is itself a signing
|
|
48
49
|
|
49
50
|
== Creating a new intermediate
|
50
51
|
|
51
|
-
Maybe you don't want to actually sign certificates with your super-secret root certificate. This is actually how a good number of most public certificate authorities do it. Rather than sign with the primary root, they generate an intermediate root that is then responsible for signing the final certificates. If you wanted to create
|
52
|
+
Maybe you don't want to actually sign certificates with your super-secret root certificate. This is actually how a good number of most public certificate authorities do it. Rather than sign with the primary root, they generate an intermediate root that is then responsible for signing the final certificates. If you wanted to create an intermediate root certificate you would do something like the following:
|
53
|
+
|
54
|
+
intermediate = CertificateAuthority::Certificate.new
|
55
|
+
intermediate.subject.common_name= "My snazzy intermediate!"
|
56
|
+
intermediate.serial_number.number=2
|
57
|
+
intermediate.key_material.generate_key
|
58
|
+
intermediate.signing_entity = true
|
59
|
+
intermediate.parent = root
|
60
|
+
signing_profile = {"extensions" => {"keyUsage" => {"usage" => ["critical", "keyCertSign"] }} }
|
61
|
+
intermediate.sign!(signing_profile)
|
52
62
|
|
53
|
-
intermediate = CertificateAuthority::Certificate.new
|
54
|
-
intermediate.subject.common_name= "My snazzy intermediate!"
|
55
|
-
intermediate.serial_number.number=2
|
56
|
-
intermediate.key_material.generate_key
|
57
|
-
intermediate.signing_entity = true
|
58
|
-
intermediate.parent = root
|
59
|
-
intermediate.sign!
|
60
|
-
|
61
63
|
All we have to do is create another certificate like we did with the root. In this example we gave it the next available serial number which for us, was 2. We then generate (and save!) key material for this new entity. Even the +signing_entity+ is set to true so this certificate can sign other certificates. The difference here is that the +parent+ field is set to the root. Going forward, whatever entity you want to sign a certificate, you set that entity to be the parent. In this case, our root will be responsible for signing this intermediate when we call +sign!+.
|
62
64
|
|
63
65
|
= Creating new certificates (in general)
|
64
66
|
|
65
67
|
Now that we have a root certificate (and possibly an intermediate) we can sign end-user certificates. It is, perhaps unsurprisingly, similar to all the others:
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
plain_cert = CertificateAuthority::Certificate.new
|
70
|
+
plain_cert.subject.common_name= "http://mydomain.com"
|
71
|
+
plain_cert.serial_number.number=4
|
72
|
+
plain_cert.key_material.generate_key
|
73
|
+
plain_cert.parent = root # or intermediate
|
74
|
+
plain_cert.sign!
|
75
|
+
|
74
76
|
That's all there is to it! In this example we generate the key material ourselves, but it's possible for the end-user to generate certificate signing request (CSR) that we can then parse and consume automatically (coming soon). To get the PEM formatted certificate for the user you would need to call:
|
75
77
|
|
76
78
|
plain_cert.to_pem
|
@@ -83,27 +85,27 @@ Creating basic certificates is all well and good, but maybe you want _more_ sign
|
|
83
85
|
|
84
86
|
Here's an example of a full signing profile for most of the common V3 extensions:
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
88
|
+
signing_profile = {
|
89
|
+
"extensions" => {
|
90
|
+
"basicConstraints" => {"ca" => false},
|
91
|
+
"crlDistributionPoints" => {"uri" => "http://notme.com/other.crl" },
|
92
|
+
"subjectKeyIdentifier" => {},
|
93
|
+
"authorityKeyIdentifier" => {},
|
94
|
+
"authorityInfoAccess" => {"ocsp" => ["http://youFillThisOut/ocsp/"] },
|
95
|
+
"keyUsage" => {"usage" => ["digitalSignature","nonRepudiation"] },
|
96
|
+
"extendedKeyUsage" => {"usage" => [ "serverAuth","clientAuth"]},
|
97
|
+
"subjectAltName" => {"uris" => ["http://subdomains.youFillThisOut/"]},
|
98
|
+
"certificatePolicies" => {
|
99
|
+
"policy_identifier" => "1.3.5.8", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"],
|
100
|
+
"user_notice" => {
|
101
|
+
"explicit_text" => "Explicit Text Here",
|
102
|
+
"organization" => "Organization name",
|
103
|
+
"notice_numbers" => "1,2,3,4"
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
107
109
|
Using a signing profile is done this way:
|
108
110
|
|
109
111
|
certificate.sign!(signing_profile)
|
@@ -128,33 +130,33 @@ This extension controls where a conformant client can go to obtain a list of cer
|
|
128
130
|
|
129
131
|
This extension is required to be present, but doesn't offer any configurable parameters. Directly from the RFC:
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
133
|
+
The subject key identifier extension provides a means of identifying
|
134
|
+
certificates that contain a particular public key.
|
135
|
+
|
136
|
+
To facilitate certification path construction, this extension MUST
|
137
|
+
appear in all conforming CA certificates, that is, all certificates
|
138
|
+
including the basic constraints extension (section 4.2.1.10) where
|
139
|
+
the value of cA is TRUE. The value of the subject key identifier
|
140
|
+
MUST be the value placed in the key identifier field of the Authority
|
141
|
+
Key Identifier extension (section 4.2.1.1) of certificates issued by
|
142
|
+
the subject of this certificate.
|
143
|
+
|
142
144
|
== Authority Key Identifier
|
143
145
|
|
144
146
|
Just like the subject key identifier, this is required under most circumstances and doesn't contain any meaningful configuration options. From the RFC:
|
145
147
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
148
|
+
The keyIdentifier field of the authorityKeyIdentifier extension MUST
|
149
|
+
be included in all certificates generated by conforming CAs to
|
150
|
+
facilitate certification path construction. There is one exception;
|
151
|
+
where a CA distributes its public key in the form of a "self-signed"
|
152
|
+
certificate, the authority key identifier MAY be omitted. The
|
153
|
+
signature on a self-signed certificate is generated with the private
|
154
|
+
key associated with the certificate's subject public key. (This
|
155
|
+
proves that the issuer possesses both the public and private keys.)
|
156
|
+
In this case, the subject and authority key identifiers would be
|
157
|
+
identical, but only the subject key identifier is needed for
|
158
|
+
certification path building.
|
159
|
+
|
158
160
|
== Authority Info Access
|
159
161
|
|
160
162
|
The authority info access extension allows a CA to sign a certificate with information a client can use to get up-to-the-minute status information on a signed certificate. This takes the form of an OCSP[link:http://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol] (Online Certificate Status Protocol) endpoints.
|
@@ -197,20 +199,20 @@ If you happen to have a PKCS#11 compliant hardware token you can use +certificat
|
|
197
199
|
|
198
200
|
To configure a certificate to utilize PKCS#11 instead of in memory keys all you need to do is:
|
199
201
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
202
|
+
root = CertificateAuthority::Certificate.new
|
203
|
+
root.subject.common_name= "http://mydomain.com"
|
204
|
+
root.serial_number.number=1
|
205
|
+
root.signing_entity = true
|
206
|
+
|
207
|
+
key_material_in_hardware = CertificateAuthority::Pkcs11KeyMaterial.new
|
208
|
+
key_material_in_hardware.token_id = "46"
|
209
|
+
key_material_in_hardware.pkcs11_lib = "/usr/lib/libeTPkcs11.so"
|
210
|
+
key_material_in_hardware.openssl_pkcs11_engine_lib = "/usr/lib/engines/engine_pkcs11.so"
|
211
|
+
key_material_in_hardware.pin = "11111111"
|
212
|
+
|
213
|
+
root.key_material = key_material_in_hardware
|
214
|
+
root.sign!
|
215
|
+
|
214
216
|
Your current version of OpenSSL _must_ include dynamic engine support and you will need to have OpenSSL PKCS#11 engine support. You will also require the actual PKCS#11 driver from the hardware manufacturer. As of today the only tokens I've gotten to work are:
|
215
217
|
|
216
218
|
[eTokenPro] Released by Aladdin (now SafeNet Inc.). I have only had success with the version 4 and 5 (32 bit only) copy of the driver. The newer authentication client released by SafeNet appears to be completely broken for interacting with the tokens outside of SafeNet's own tools. If anyone has a different experience I'd like to hear from you.
|
@@ -224,14 +226,18 @@ Also of note, I have gotten these to work with 32-bit copies of Ubuntu 10.10 and
|
|
224
226
|
= Coming Soon
|
225
227
|
|
226
228
|
* More PKCS#11 hardware (I need driver support from the manufacturers)
|
227
|
-
*
|
229
|
+
* Support for working with CSRs to request & issue certificates
|
230
|
+
|
231
|
+
= Misc notes
|
232
|
+
|
233
|
+
* Firefox will complain about root/intermediate certificates unless both digitalSignature and keyEncipherment are specified as keyUsage attributes. Thanks diogomonica
|
228
234
|
|
229
235
|
== Meta
|
230
236
|
|
231
|
-
Written by Chris Chandler(http://chrischandler.name)
|
237
|
+
Written by Chris Chandler(http://chrischandler.name)
|
232
238
|
|
233
239
|
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
234
240
|
|
235
|
-
Main page: http://github.com/cchandler/
|
241
|
+
Main page: http://github.com/cchandler/certificate_authority
|
236
242
|
|
237
|
-
Issue tracking: https://github.com/cchandler/
|
243
|
+
Issue tracking: https://github.com/cchandler/certificate_authority/issues
|
data/VERSION.yml
CHANGED
@@ -4,13 +4,13 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.1.
|
7
|
+
s.name = "certificate_authority"
|
8
|
+
s.version = "0.1.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Chris Chandler"]
|
12
|
-
s.date =
|
13
|
-
s.email =
|
12
|
+
s.date = "2012-08-12"
|
13
|
+
s.email = "chris@flatterline.com"
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.rdoc"
|
16
16
|
]
|
@@ -40,15 +40,16 @@ Gem::Specification.new do |s|
|
|
40
40
|
"spec/units/extensions_spec.rb",
|
41
41
|
"spec/units/key_material_spec.rb",
|
42
42
|
"spec/units/ocsp_handler_spec.rb",
|
43
|
+
"spec/units/pkcs11_key_material_spec.rb",
|
43
44
|
"spec/units/serial_number_spec.rb",
|
44
45
|
"spec/units/signing_entity_spec.rb",
|
45
46
|
"spec/units/units_helper.rb"
|
46
47
|
]
|
47
|
-
s.homepage =
|
48
|
+
s.homepage = "http://github.com/cchandler/certificate_authority"
|
48
49
|
s.licenses = ["MIT"]
|
49
50
|
s.require_paths = ["lib"]
|
50
|
-
s.rubygems_version =
|
51
|
-
s.summary =
|
51
|
+
s.rubygems_version = "1.8.15"
|
52
|
+
s.summary = "Ruby gem for managing the core functions outlined in RFC-3280 for PKI"
|
52
53
|
s.test_files = [
|
53
54
|
"spec/spec_helper.rb",
|
54
55
|
"spec/units/certificate_authority_spec.rb",
|
@@ -58,6 +59,7 @@ Gem::Specification.new do |s|
|
|
58
59
|
"spec/units/extensions_spec.rb",
|
59
60
|
"spec/units/key_material_spec.rb",
|
60
61
|
"spec/units/ocsp_handler_spec.rb",
|
62
|
+
"spec/units/pkcs11_key_material_spec.rb",
|
61
63
|
"spec/units/serial_number_spec.rb",
|
62
64
|
"spec/units/signing_entity_spec.rb",
|
63
65
|
"spec/units/units_helper.rb"
|
@@ -2,7 +2,7 @@ module CertificateAuthority
|
|
2
2
|
class Certificate
|
3
3
|
# include SigningEntity
|
4
4
|
include ActiveModel::Validations
|
5
|
-
|
5
|
+
|
6
6
|
attr_accessor :distinguished_name
|
7
7
|
attr_accessor :serial_number
|
8
8
|
attr_accessor :key_material
|
@@ -11,24 +11,24 @@ module CertificateAuthority
|
|
11
11
|
attr_accessor :revoked_at
|
12
12
|
attr_accessor :extensions
|
13
13
|
attr_accessor :openssl_body
|
14
|
-
|
14
|
+
|
15
15
|
alias :subject :distinguished_name #Same thing as the DN
|
16
|
-
|
16
|
+
|
17
17
|
attr_accessor :parent
|
18
|
-
|
18
|
+
|
19
19
|
validate do |certificate|
|
20
20
|
errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
|
21
|
-
errors.add :base, "Key material
|
21
|
+
errors.add :base, "Key material must be valid" unless key_material.valid?
|
22
22
|
errors.add :base, "Serial number must be valid" unless serial_number.valid?
|
23
23
|
errors.add :base, "Extensions must be valid" unless extensions.each do |item|
|
24
24
|
unless item.respond_to?(:valid?)
|
25
|
-
true
|
25
|
+
true
|
26
26
|
else
|
27
27
|
item.valid?
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def initialize
|
33
33
|
self.distinguished_name = DistinguishedName.new
|
34
34
|
self.serial_number = SerialNumber.new
|
@@ -37,35 +37,35 @@ module CertificateAuthority
|
|
37
37
|
self.not_after = Time.now + 60 * 60 * 24 * 365 #One year
|
38
38
|
self.parent = self
|
39
39
|
self.extensions = load_extensions()
|
40
|
-
|
40
|
+
|
41
41
|
self.signing_entity = false
|
42
|
-
|
42
|
+
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def sign!(signing_profile={})
|
46
46
|
raise "Invalid certificate #{self.errors.full_messages}" unless valid?
|
47
47
|
merge_profile_with_extensions(signing_profile)
|
48
|
-
|
48
|
+
|
49
49
|
openssl_cert = OpenSSL::X509::Certificate.new
|
50
50
|
openssl_cert.version = 2
|
51
51
|
openssl_cert.not_before = self.not_before
|
52
52
|
openssl_cert.not_after = self.not_after
|
53
53
|
openssl_cert.public_key = self.key_material.public_key
|
54
|
-
|
54
|
+
|
55
55
|
openssl_cert.serial = self.serial_number.number
|
56
|
-
|
56
|
+
|
57
57
|
openssl_cert.subject = self.distinguished_name.to_x509_name
|
58
58
|
openssl_cert.issuer = parent.distinguished_name.to_x509_name
|
59
|
-
|
59
|
+
|
60
60
|
require 'tempfile'
|
61
61
|
t = Tempfile.new("bullshit_conf")
|
62
62
|
# t = File.new("/tmp/openssl.cnf")
|
63
63
|
## The config requires a file even though we won't use it
|
64
64
|
openssl_config = OpenSSL::Config.new(t.path)
|
65
|
-
|
65
|
+
|
66
66
|
factory = OpenSSL::X509::ExtensionFactory.new
|
67
67
|
factory.subject_certificate = openssl_cert
|
68
|
-
|
68
|
+
|
69
69
|
#NB: If the parent doesn't have an SSL body we're making this a self-signed cert
|
70
70
|
if parent.openssl_body.nil?
|
71
71
|
factory.issuer_certificate = openssl_cert
|
@@ -77,51 +77,56 @@ module CertificateAuthority
|
|
77
77
|
config_extensions = extensions[k].config_extensions
|
78
78
|
openssl_config = merge_options(openssl_config,config_extensions)
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
# p openssl_config.sections
|
82
|
-
|
82
|
+
|
83
83
|
factory.config = openssl_config
|
84
|
-
|
85
|
-
|
84
|
+
|
85
|
+
# Order matters: e.g. for self-signed, subjectKeyIdentifier must come before authorityKeyIdentifier
|
86
|
+
self.extensions.keys.sort{|a,b| b<=>a}.each do |k|
|
86
87
|
e = extensions[k]
|
87
88
|
next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it
|
88
89
|
ext = factory.create_ext(e.openssl_identifier, e.to_s)
|
89
90
|
openssl_cert.add_extension(ext)
|
90
91
|
end
|
91
|
-
|
92
|
-
|
92
|
+
|
93
|
+
if signing_profile["digest"].nil?
|
94
|
+
digest = OpenSSL::Digest::Digest.new("SHA512")
|
95
|
+
else
|
96
|
+
digest = OpenSSL::Digest::Digest.new(signing_profile["digest"])
|
97
|
+
end
|
93
98
|
self.openssl_body = openssl_cert.sign(parent.key_material.private_key,digest)
|
94
99
|
t.close! if t.is_a?(Tempfile)# We can get rid of the ridiculous temp file
|
95
100
|
self.openssl_body
|
96
101
|
end
|
97
|
-
|
102
|
+
|
98
103
|
def is_signing_entity?
|
99
104
|
self.extensions["basicConstraints"].ca
|
100
105
|
end
|
101
|
-
|
106
|
+
|
102
107
|
def signing_entity=(signing)
|
103
108
|
self.extensions["basicConstraints"].ca = signing
|
104
109
|
end
|
105
|
-
|
110
|
+
|
106
111
|
def revoked?
|
107
112
|
!self.revoked_at.nil?
|
108
113
|
end
|
109
|
-
|
114
|
+
|
110
115
|
def to_pem
|
111
116
|
raise "Certificate has no signed body" if self.openssl_body.nil?
|
112
117
|
self.openssl_body.to_pem
|
113
118
|
end
|
114
|
-
|
119
|
+
|
115
120
|
def is_root_entity?
|
116
121
|
self.parent == self && is_signing_entity?
|
117
122
|
end
|
118
|
-
|
123
|
+
|
119
124
|
def is_intermediate_entity?
|
120
125
|
(self.parent != self) && is_signing_entity?
|
121
126
|
end
|
122
|
-
|
127
|
+
|
123
128
|
private
|
124
|
-
|
129
|
+
|
125
130
|
def merge_profile_with_extensions(signing_profile={})
|
126
131
|
return self.extensions if signing_profile["extensions"].nil?
|
127
132
|
signing_config = signing_profile["extensions"]
|
@@ -130,17 +135,17 @@ module CertificateAuthority
|
|
130
135
|
items = signing_config[k]
|
131
136
|
items.keys.each do |profile_item_key|
|
132
137
|
if extension.respond_to?("#{profile_item_key}=".to_sym)
|
133
|
-
extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
|
138
|
+
extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
|
134
139
|
else
|
135
140
|
p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!"
|
136
141
|
end
|
137
142
|
end
|
138
143
|
end
|
139
144
|
end
|
140
|
-
|
145
|
+
|
141
146
|
def load_extensions
|
142
147
|
extension_hash = {}
|
143
|
-
|
148
|
+
|
144
149
|
temp_extensions = []
|
145
150
|
basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
|
146
151
|
temp_extensions << basic_constraints
|
@@ -160,20 +165,37 @@ module CertificateAuthority
|
|
160
165
|
temp_extensions << subject_alternative_name
|
161
166
|
certificate_policies = CertificateAuthority::Extensions::CertificatePolicies.new
|
162
167
|
temp_extensions << certificate_policies
|
163
|
-
|
168
|
+
|
164
169
|
temp_extensions.each do |extension|
|
165
170
|
extension_hash[extension.openssl_identifier] = extension
|
166
171
|
end
|
167
|
-
|
172
|
+
|
168
173
|
extension_hash
|
169
174
|
end
|
170
|
-
|
175
|
+
|
171
176
|
def merge_options(config,hash)
|
172
177
|
hash.keys.each do |k|
|
173
178
|
config[k] = hash[k]
|
174
179
|
end
|
175
180
|
config
|
176
181
|
end
|
177
|
-
|
182
|
+
|
183
|
+
def self.from_openssl openssl_cert
|
184
|
+
unless openssl_cert.is_a? OpenSSL::X509::Certificate
|
185
|
+
raise "Can only construct from an OpenSSL::X509::Certificate"
|
186
|
+
end
|
187
|
+
|
188
|
+
certificate = Certificate.new
|
189
|
+
# Only subject, key_material, and body are used for signing
|
190
|
+
certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject
|
191
|
+
certificate.key_material.public_key = openssl_cert.public_key
|
192
|
+
certificate.openssl_body = openssl_cert
|
193
|
+
certificate.serial_number.number = openssl_cert.serial.to_i
|
194
|
+
certificate.not_before = openssl_cert.not_before
|
195
|
+
certificate.not_after = openssl_cert.not_after
|
196
|
+
# TODO extensions
|
197
|
+
certificate
|
198
|
+
end
|
199
|
+
|
178
200
|
end
|
179
|
-
end
|
201
|
+
end
|