r509 0.9.2 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/CONTRIBUTING.mdown +21 -0
- data/LICENSE +13 -0
- data/README.mdown +548 -0
- data/Rakefile +5 -0
- data/bin/r509 +16 -17
- data/doc/R509.html +42 -26
- data/doc/R509/ASN1.html +22 -16
- data/doc/R509/ASN1/GeneralName.html +180 -173
- data/doc/R509/ASN1/GeneralNames.html +390 -62
- data/doc/R509/CRL.html +9 -7
- data/doc/R509/CRL/Administrator.html +208 -623
- data/doc/R509/CRL/FileReaderWriter.html +856 -0
- data/doc/R509/CRL/ReaderWriter.html +524 -0
- data/doc/R509/CRL/SignedList.html +29 -42
- data/doc/R509/CSR.html +248 -333
- data/doc/R509/Cert.html +364 -491
- data/doc/R509/Cert/Extensions.html +134 -43
- data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +335 -65
- data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +201 -102
- data/doc/R509/Cert/Extensions/BasicConstraints.html +297 -68
- data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +690 -77
- data/doc/R509/Cert/Extensions/CertificatePolicies.html +293 -43
- data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +321 -173
- data/doc/R509/Cert/Extensions/GeneralNamesMixin.html +656 -0
- data/doc/R509/Cert/Extensions/InhibitAnyPolicy.html +270 -42
- data/doc/R509/Cert/Extensions/KeyUsage.html +334 -184
- data/doc/R509/Cert/Extensions/NameConstraints.html +363 -93
- data/doc/R509/{ASN1 → Cert/Extensions}/NoticeReference.html +209 -48
- data/doc/R509/Cert/Extensions/OCSPNoCheck.html +244 -17
- data/doc/R509/Cert/Extensions/PolicyConstraints.html +322 -71
- data/doc/R509/{ASN1 → Cert/Extensions}/PolicyInformation.html +204 -43
- data/doc/R509/{ASN1 → Cert/Extensions}/PolicyQualifiers.html +205 -48
- data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +348 -143
- data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +165 -13
- data/doc/R509/{ASN1 → Cert/Extensions}/UserNotice.html +204 -43
- data/doc/R509/Cert/Extensions/ValidationMixin.html +120 -0
- data/doc/R509/CertificateAuthority.html +9 -7
- data/doc/R509/CertificateAuthority/OptionsBuilder.html +475 -0
- data/doc/R509/CertificateAuthority/Signer.html +149 -198
- data/doc/R509/Config.html +10 -8
- data/doc/R509/Config/CAConfig.html +708 -625
- data/doc/R509/Config/CAConfigPool.html +179 -31
- data/doc/R509/Config/CertProfile.html +1544 -0
- data/doc/R509/Config/SubjectItemPolicy.html +437 -99
- data/doc/R509/Engine.html +14 -28
- data/doc/R509/Helpers.html +1014 -0
- data/doc/R509/MessageDigest.html +73 -25
- data/doc/R509/NameSanitizer.html +39 -39
- data/doc/R509/OCSP.html +5 -5
- data/doc/R509/OCSP/Request.html +5 -5
- data/doc/R509/OCSP/Request/Nonce.html +5 -5
- data/doc/R509/OCSP/Response.html +7 -7
- data/doc/R509/OIDMapper.html +121 -6
- data/doc/R509/PrivateKey.html +226 -227
- data/doc/R509/R509Error.html +5 -5
- data/doc/R509/SPKI.html +244 -342
- data/doc/R509/Subject.html +241 -70
- data/doc/R509/Validity.html +5 -5
- data/doc/R509/Validity/Checker.html +5 -5
- data/doc/R509/Validity/DefaultChecker.html +5 -9
- data/doc/R509/Validity/DefaultWriter.html +5 -9
- data/doc/R509/Validity/Status.html +5 -5
- data/doc/R509/Validity/Writer.html +5 -5
- data/doc/_index.html +92 -30
- data/doc/class_list.html +2 -2
- data/doc/file.CONTRIBUTING.html +96 -0
- data/doc/file.LICENSE.html +87 -0
- data/doc/file.README.html +279 -389
- data/doc/file.YAML.html +243 -0
- data/doc/file.r509.html +298 -105
- data/doc/file_list.html +11 -2
- data/doc/frames.html +1 -1
- data/doc/index.html +279 -389
- data/doc/js/full_list.js +6 -1
- data/doc/method_list.html +869 -1139
- data/doc/top-level-namespace.html +103 -5
- data/lib/r509.rb +7 -2
- data/lib/r509/asn1.rb +97 -135
- data/lib/r509/cert.rb +17 -106
- data/lib/r509/cert/extensions.rb +13 -676
- data/lib/r509/cert/extensions/authority_info_access.rb +128 -0
- data/lib/r509/cert/extensions/authority_key_identifier.rb +100 -0
- data/lib/r509/cert/extensions/base.rb +142 -0
- data/lib/r509/cert/extensions/basic_constraints.rb +119 -0
- data/lib/r509/cert/extensions/certificate_policies.rb +262 -0
- data/lib/r509/cert/extensions/crl_distribution_points.rb +98 -0
- data/lib/r509/cert/extensions/extended_key_usage.rb +189 -0
- data/lib/r509/cert/extensions/inhibit_any_policy.rb +70 -0
- data/lib/r509/cert/extensions/key_usage.rb +209 -0
- data/lib/r509/cert/extensions/name_constraints.rb +179 -0
- data/lib/r509/cert/extensions/ocsp_no_check.rb +56 -0
- data/lib/r509/cert/extensions/policy_constraints.rb +122 -0
- data/lib/r509/cert/extensions/subject_alternative_name.rb +88 -0
- data/lib/r509/cert/extensions/subject_key_identifier.rb +56 -0
- data/lib/r509/cert/extensions/validation_mixin.rb +42 -0
- data/lib/r509/certificate_authority/options_builder.rb +142 -0
- data/lib/r509/certificate_authority/signer.rb +189 -0
- data/lib/r509/config.rb +3 -600
- data/lib/r509/config/ca_config.rb +414 -0
- data/lib/r509/config/cert_profile.rb +110 -0
- data/lib/r509/config/subject_item_policy.rb +118 -0
- data/lib/r509/crl/administrator.rb +169 -0
- data/lib/r509/crl/reader_writer.rb +109 -0
- data/lib/r509/crl/signed_list.rb +135 -0
- data/lib/r509/csr.rb +35 -116
- data/lib/r509/engine.rb +21 -11
- data/lib/r509/helpers.rb +110 -0
- data/lib/r509/io_helpers.rb +18 -13
- data/lib/r509/message_digest.rb +13 -3
- data/lib/r509/oid_mapper.rb +14 -0
- data/lib/r509/private_key.rb +74 -50
- data/lib/r509/spki.rb +50 -113
- data/lib/r509/subject.rb +24 -2
- data/lib/r509/trollop.rb +788 -0
- data/lib/r509/version.rb +1 -1
- data/r509.yaml +289 -96
- data/spec/asn1_spec.rb +171 -98
- data/spec/cert/extensions/authority_info_access_spec.rb +247 -0
- data/spec/cert/extensions/authority_key_identifier_spec.rb +85 -0
- data/spec/cert/extensions/base_spec.rb +172 -0
- data/spec/cert/extensions/basic_constraints_spec.rb +185 -0
- data/spec/cert/extensions/certificate_policies_spec.rb +288 -0
- data/spec/cert/extensions/crl_distribution_points_spec.rb +149 -0
- data/spec/cert/extensions/extended_key_usage_spec.rb +174 -0
- data/spec/cert/extensions/inhibit_any_policy_spec.rb +92 -0
- data/spec/cert/extensions/key_usage_spec.rb +172 -0
- data/spec/cert/extensions/name_constraints_spec.rb +335 -0
- data/spec/cert/extensions/ocsp_no_check_spec.rb +76 -0
- data/spec/cert/extensions/policy_constraints_spec.rb +155 -0
- data/spec/cert/extensions/subject_alternative_name_spec.rb +354 -0
- data/spec/cert/extensions/subject_key_identifier_spec.rb +64 -0
- data/spec/cert_spec.rb +11 -9
- data/spec/certificate_authority/options_builder_spec.rb +307 -0
- data/spec/certificate_authority/signer_spec.rb +278 -0
- data/spec/config/ca_config_spec.rb +405 -0
- data/spec/config/cert_profile_spec.rb +88 -0
- data/spec/config/subject_item_policy_spec.rb +81 -0
- data/spec/crl/administrator_spec.rb +199 -0
- data/spec/crl/reader_writer_spec.rb +97 -0
- data/spec/crl/signed_list_spec.rb +84 -0
- data/spec/csr_spec.rb +43 -36
- data/spec/engine_spec.rb +51 -0
- data/spec/fixtures.rb +40 -40
- data/spec/fixtures/cert1.pem +1 -1
- data/spec/fixtures/config_pool_test_minimal.yaml +11 -15
- data/spec/fixtures/config_test.yaml +96 -59
- data/spec/fixtures/config_test_dsa.yaml +29 -35
- data/spec/fixtures/config_test_ec.yaml +29 -35
- data/spec/fixtures/config_test_engine_key.yaml +7 -7
- data/spec/fixtures/config_test_engine_no_key_name.yaml +6 -6
- data/spec/fixtures/config_test_minimal.yaml +3 -5
- data/spec/fixtures/config_test_password.yaml +4 -6
- data/spec/fixtures/config_test_various.yaml +147 -137
- data/spec/fixtures/crl_list_file.txt +1 -1
- data/spec/fixtures/test_ca_crl.cer +20 -0
- data/spec/fixtures/test_ca_crl.key +28 -0
- data/spec/fixtures/test_ca_crl.p12 +0 -0
- data/spec/message_digest_spec.rb +6 -0
- data/spec/oid_mapper_spec.rb +11 -0
- data/spec/private_key_spec.rb +19 -18
- data/spec/spec_helper.rb +10 -6
- data/spec/spki_spec.rb +38 -19
- data/spec/subject_spec.rb +16 -0
- metadata +108 -59
- metadata.gz.sig +0 -0
- data/README.md +0 -638
- data/doc/R509/Config/CAProfile.html +0 -1015
- data/doc/R509/IOHelpers.html +0 -564
- data/lib/r509/certificate_authority.rb +0 -407
- data/lib/r509/crl.rb +0 -351
- data/spec/cert/extensions_spec.rb +0 -1095
- data/spec/certificate_authority_spec.rb +0 -681
- data/spec/config_spec.rb +0 -562
- data/spec/crl_spec.rb +0 -226
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'r509/config'
|
3
|
+
require 'r509/cert'
|
4
|
+
require 'r509/exceptions'
|
5
|
+
require 'r509/ec-hack'
|
6
|
+
|
7
|
+
# CertificateAuthority related classes
|
8
|
+
module R509::CertificateAuthority
|
9
|
+
# Contains the certification authority signing operation methods
|
10
|
+
class Signer
|
11
|
+
# @param [R509::Config] config
|
12
|
+
def initialize(config)
|
13
|
+
@config = config
|
14
|
+
|
15
|
+
if not @config.nil? and not @config.kind_of?(R509::Config::CAConfig)
|
16
|
+
raise R509::R509Error, "config must be a kind of R509::Config::CAConfig"
|
17
|
+
end
|
18
|
+
if not @config.nil? and not @config.ca_cert.has_private_key?
|
19
|
+
raise R509::R509Error, "You must have a private key associated with your CA certificate to issue"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Signs a CSR
|
24
|
+
# @option options :csr [R509::CSR]
|
25
|
+
# @option options :spki [R509::SPKI]
|
26
|
+
# @option options :subject [R509::Subject,OpenSSL::X509::Subject,Array] This is optional when passing a :csr but required for :spki
|
27
|
+
# @option options :message_digest [String] the message digest to use for this certificate instead of the default (see R509::MessageDigest::DEFAULT_MD).
|
28
|
+
# @option options :serial [String] (random serial) the serial number you want to issue the certificate with
|
29
|
+
# @option options :extensions [Array] An array of R509::Cert::Extensions::* objects that represent the extensions you want to embed in the final certificate
|
30
|
+
# @option options :not_before [Time] (Time.now - 6 hours) the notBefore for the certificate
|
31
|
+
# @option options :not_after [Time] (Time.now + 365 days) the notAfter for the certificate
|
32
|
+
# @return [R509::Cert] the signed cert object
|
33
|
+
def sign(options)
|
34
|
+
R509::CertificateAuthority::Signer.check_options(options)
|
35
|
+
|
36
|
+
message_digest = R509::MessageDigest.new(options[:message_digest])
|
37
|
+
|
38
|
+
subject, public_key = R509::CertificateAuthority::Signer.extract_public_key_subject(options)
|
39
|
+
|
40
|
+
cert = R509::CertificateAuthority::Signer.build_cert(
|
41
|
+
:subject => subject.name,
|
42
|
+
:issuer => @config.ca_cert.subject.name,
|
43
|
+
:not_before => options[:not_before],
|
44
|
+
:not_after => options[:not_after],
|
45
|
+
:public_key => public_key,
|
46
|
+
:serial => options[:serial]
|
47
|
+
)
|
48
|
+
|
49
|
+
cert.extensions = options[:extensions] || [
|
50
|
+
R509::Cert::Extensions::SubjectKeyIdentifier.new(:public_key => public_key),
|
51
|
+
R509::Cert::Extensions::AuthorityKeyIdentifier.new(:public_key => @config.ca_cert.public_key)
|
52
|
+
]
|
53
|
+
|
54
|
+
#@config.ca_cert.key.key ... ugly. ca_cert returns R509::Cert
|
55
|
+
# #key returns R509::PrivateKey and #key on that returns OpenSSL object we need
|
56
|
+
cert.sign( @config.ca_cert.key.key, message_digest.digest )
|
57
|
+
cert_opts = { :cert => cert }
|
58
|
+
cert_opts[:key] = options[:csr].key if not options[:csr].nil? and not options[:csr].key.nil?
|
59
|
+
R509::Cert.new(cert_opts)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Self-signs a CSR
|
63
|
+
# @option options :csr [R509::CSR]
|
64
|
+
# @option options :message_digest [String] the message digest to use for this certificate (defaults to R509::MessageDigest::DEFAULT_MD)
|
65
|
+
# @option options :serial [String] (random serial) the serial number you want to issue the certificate with
|
66
|
+
# @option options :extensions [Array] An array of R509::Cert::Extensions::* objects that represent the extensions you want to embed in the final certificate
|
67
|
+
# @option options :not_before [Time] (Time.now - 6 hours) the notBefore for the certificate
|
68
|
+
# @option options :not_after [Time] (Time.now + 365 days) the notAfter for the certificate
|
69
|
+
# @return [R509::Cert] the signed cert object
|
70
|
+
def self.selfsign(options)
|
71
|
+
if not options.kind_of?(Hash)
|
72
|
+
raise ArgumentError, "You must pass a hash of options consisting of at minimum :csr"
|
73
|
+
end
|
74
|
+
csr = options[:csr]
|
75
|
+
if csr.key.nil?
|
76
|
+
raise ArgumentError, 'CSR must also have a private key to self sign'
|
77
|
+
end
|
78
|
+
|
79
|
+
subject, public_key = R509::CertificateAuthority::Signer.extract_public_key_subject(options)
|
80
|
+
|
81
|
+
cert = self.build_cert(
|
82
|
+
:subject => subject.name,
|
83
|
+
:issuer => subject.name,
|
84
|
+
:not_before => options[:not_before],
|
85
|
+
:not_after => options[:not_after],
|
86
|
+
:public_key => public_key,
|
87
|
+
:serial => options[:serial]
|
88
|
+
)
|
89
|
+
|
90
|
+
cert.extensions = options[:extensions] || [
|
91
|
+
R509::Cert::Extensions::BasicConstraints.new(:ca => true),
|
92
|
+
R509::Cert::Extensions::SubjectKeyIdentifier.new(:public_key => public_key),
|
93
|
+
R509::Cert::Extensions::AuthorityKeyIdentifier.new(:public_key => public_key)
|
94
|
+
]
|
95
|
+
|
96
|
+
if options.has_key?(:message_digest)
|
97
|
+
message_digest = R509::MessageDigest.new(options[:message_digest])
|
98
|
+
else
|
99
|
+
message_digest = R509::MessageDigest.new(R509::MessageDigest::DEFAULT_MD)
|
100
|
+
end
|
101
|
+
|
102
|
+
cert.sign( csr.key.key, message_digest.digest )
|
103
|
+
|
104
|
+
R509::Cert.new(:cert => cert, :key => csr.key)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def self.check_options(options)
|
110
|
+
if options.has_key?(:csr) and options.has_key?(:spki)
|
111
|
+
raise ArgumentError, "You can't pass both :csr and :spki"
|
112
|
+
elsif not options.has_key?(:csr) and not options.has_key?(:spki)
|
113
|
+
raise ArgumentError, "You must supply either :csr or :spki"
|
114
|
+
elsif options.has_key?(:csr) and not options[:csr].kind_of?(R509::CSR)
|
115
|
+
raise ArgumentError, "You must pass an R509::CSR object for :csr"
|
116
|
+
elsif options.has_key?(:spki) and not options[:spki].kind_of?(R509::SPKI)
|
117
|
+
raise ArgumentError, "You must pass an R509::SPKI object for :spki"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.build_cert(options)
|
122
|
+
|
123
|
+
cert = OpenSSL::X509::Certificate.new
|
124
|
+
|
125
|
+
cert.subject = options[:subject]
|
126
|
+
cert.issuer = options[:issuer]
|
127
|
+
cert.not_before = calculate_not_before(options[:not_before])
|
128
|
+
cert.not_after = calculate_not_after(options[:not_after],cert.not_before)
|
129
|
+
cert.public_key = options[:public_key]
|
130
|
+
cert.serial = create_serial(options[:serial])
|
131
|
+
cert.version = 2 #2 means v3
|
132
|
+
cert
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.create_serial(serial)
|
136
|
+
if not serial.nil?
|
137
|
+
serial = OpenSSL::BN.new(serial.to_s)
|
138
|
+
else
|
139
|
+
# generate random serial in accordance with best practices
|
140
|
+
#
|
141
|
+
# guidelines state 20-bits of entropy, but we can cram more in!
|
142
|
+
# per rfc5280 conforming CAs can make the serial field up to 20 octets
|
143
|
+
# to prevent even the incredibly remote possibility of collision we'll
|
144
|
+
# concatenate current time (to the microsecond) with a random num
|
145
|
+
rand = OpenSSL::BN.rand(96,0) # 96 bits is 12 bytes (octets).
|
146
|
+
serial = OpenSSL::BN.new((Time.now.to_f*1000000).to_i.to_s + rand.to_s)
|
147
|
+
# since second param is 0 the most significant bit must always be 1
|
148
|
+
# this theoretically gives us 95 bits of entropy
|
149
|
+
# (see: http://www.openssl.org/docs/crypto/BN_rand.html) + microtime,
|
150
|
+
# which adds a non-zero quantity of entropy. depending upon how predictable
|
151
|
+
# your issuance is, this could range from a reasonably large quantity
|
152
|
+
# of entropy to very little
|
153
|
+
end
|
154
|
+
serial
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.calculate_not_before(not_before)
|
158
|
+
if not_before.nil?
|
159
|
+
#not_before will be set to 6 hours before now to prevent issues with bad system clocks (clients don't sync)
|
160
|
+
not_before = Time.now - 6 * 60 * 60
|
161
|
+
end
|
162
|
+
not_before
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.calculate_not_after(not_after,not_before)
|
166
|
+
if not_after.nil?
|
167
|
+
not_after = not_before + 365 * 24 * 60 * 60
|
168
|
+
end
|
169
|
+
not_after
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.extract_public_key_subject(options)
|
173
|
+
if options.has_key?(:csr)
|
174
|
+
subject = (options.has_key?(:subject))? R509::Subject.new(options[:subject]) : options[:csr].subject
|
175
|
+
public_key = options[:csr].public_key
|
176
|
+
else
|
177
|
+
# spki
|
178
|
+
if not options.has_key?(:subject)
|
179
|
+
raise ArgumentError, "You must supply :subject when passing :spki"
|
180
|
+
end
|
181
|
+
public_key = options[:spki].public_key
|
182
|
+
subject = R509::Subject.new(options[:subject])
|
183
|
+
end
|
184
|
+
|
185
|
+
[subject,public_key]
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
data/lib/r509/config.rb
CHANGED
@@ -1,604 +1,7 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'openssl'
|
3
|
-
require 'r509/exceptions'
|
4
|
-
require 'r509/io_helpers'
|
5
|
-
require 'r509/subject'
|
6
|
-
require 'r509/private_key'
|
7
|
-
require 'r509/engine'
|
8
|
-
require 'fileutils'
|
9
|
-
require 'pathname'
|
10
|
-
|
11
1
|
module R509
|
12
|
-
# Module to contain all configuration related classes (e.g. CAConfig, CAProfile, SubjectItemPolicy)
|
13
2
|
module Config
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
:certificate_policies, :subject_item_policy, :ocsp_no_check,
|
18
|
-
:inhibit_any_policy, :policy_constraints, :name_constraints
|
19
|
-
|
20
|
-
# All hash options for CAProfile are optional.
|
21
|
-
# @option opts [String] :basic_constraints
|
22
|
-
# @option opts [Array] :key_usage
|
23
|
-
# @option opts [Array] :extended_key_usage
|
24
|
-
# @option opts [Array] :certificate_policies
|
25
|
-
# @option opts [Boolean] :ocsp_no_check Sets OCSP No Check extension in the certificate if true
|
26
|
-
# @option opts [Integer] :inhibit_any_policy Sets the value of the inhibitAnyPolicy extension
|
27
|
-
# @option opts [Hash] :policy_constraints Sets the value of the policyConstriants extension
|
28
|
-
# @option opts [Hash] :name_constraints Sets the value of the nameConstraints extension
|
29
|
-
# @option opts [R509::Config::SubjectItemPolicy] :subject_item_policy
|
30
|
-
def initialize(opts = {})
|
31
|
-
validate_basic_constraints opts[:basic_constraints]
|
32
|
-
validate_key_usage opts[:key_usage]
|
33
|
-
validate_extended_key_usage opts[:extended_key_usage]
|
34
|
-
validate_certificate_policies opts[:certificate_policies]
|
35
|
-
validate_inhibit_any_policy opts[:inhibit_any_policy]
|
36
|
-
validate_policy_constraints opts[:policy_constraints]
|
37
|
-
validate_name_constraints opts[:name_constraints]
|
38
|
-
@ocsp_no_check = (opts[:ocsp_no_check] == true or opts[:ocsp_no_check] == "true")?true:false
|
39
|
-
validate_subject_item_policy opts[:subject_item_policy]
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
# @private
|
44
|
-
# validates subject item policy
|
45
|
-
def validate_subject_item_policy(sip)
|
46
|
-
if not sip.nil? and not sip.kind_of?(R509::Config::SubjectItemPolicy)
|
47
|
-
raise ArgumentError, "subject_item_policy must be of type R509::Config::SubjectItemPolicy"
|
48
|
-
end
|
49
|
-
@subject_item_policy = sip
|
50
|
-
end
|
51
|
-
|
52
|
-
# @private
|
53
|
-
# validates key usage array
|
54
|
-
def validate_key_usage(ku)
|
55
|
-
if not ku.nil? and not ku.kind_of?(Array)
|
56
|
-
raise ArgumentError, "key_usage must be an array of strings (see README)"
|
57
|
-
end
|
58
|
-
@key_usage = ku
|
59
|
-
end
|
60
|
-
|
61
|
-
# @private
|
62
|
-
# validates inhibit any policy
|
63
|
-
def validate_inhibit_any_policy(iap)
|
64
|
-
if not iap.nil?
|
65
|
-
validate_non_negative_integer("Inhibit any policy",iap)
|
66
|
-
end
|
67
|
-
@inhibit_any_policy = iap
|
68
|
-
end
|
69
|
-
|
70
|
-
# @private
|
71
|
-
def validate_policy_constraints(pc)
|
72
|
-
if not pc.nil?
|
73
|
-
if not pc.kind_of?(Hash)
|
74
|
-
raise ArgumentError, 'Policy constraints must be provided as a hash with at least one of the two allowed keys: "inhibit_policy_mapping" and "require_explicit_policy"'
|
75
|
-
end
|
76
|
-
if not pc["inhibit_policy_mapping"].nil?
|
77
|
-
ipm = validate_non_negative_integer("inhibit_policy_mapping",pc["inhibit_policy_mapping"])
|
78
|
-
end
|
79
|
-
if not pc["require_explicit_policy"].nil?
|
80
|
-
rep = validate_non_negative_integer("require_explicit_policy",pc["require_explicit_policy"])
|
81
|
-
end
|
82
|
-
if not ipm and not rep
|
83
|
-
raise ArgumentError, 'Policy constraints must have at least one of two keys: "inhibit_policy_mapping" and "require_explicit_policy" and the value must be non-negative'
|
84
|
-
end
|
85
|
-
end
|
86
|
-
@policy_constraints = pc
|
87
|
-
end
|
88
|
-
|
89
|
-
# @private
|
90
|
-
# used by iap and pc validation methods
|
91
|
-
def validate_non_negative_integer(source,value)
|
92
|
-
if not value.kind_of?(Integer) or value < 0
|
93
|
-
raise ArgumentError, "#{source} must be a non-negative integer"
|
94
|
-
end
|
95
|
-
value
|
96
|
-
end
|
97
|
-
|
98
|
-
# @private
|
99
|
-
# validates extended key usage array
|
100
|
-
def validate_extended_key_usage(eku)
|
101
|
-
if not eku.nil? and not eku.kind_of?(Array)
|
102
|
-
raise ArgumentError, "extended_key_usage must be an array of strings (see README)"
|
103
|
-
end
|
104
|
-
@extended_key_usage = eku
|
105
|
-
end
|
106
|
-
|
107
|
-
|
108
|
-
# @private
|
109
|
-
# validates the structure of the certificate policies array
|
110
|
-
def validate_certificate_policies(policies)
|
111
|
-
if not policies.nil?
|
112
|
-
if not policies.respond_to?(:each)
|
113
|
-
raise ArgumentError, "Not a valid certificate policy structure. Must be an array of hashes"
|
114
|
-
else
|
115
|
-
policies.each do |policy|
|
116
|
-
if policy["policy_identifier"].nil?
|
117
|
-
raise ArgumentError, "Each policy requires a policy identifier"
|
118
|
-
end
|
119
|
-
if not policy["cps_uris"].nil?
|
120
|
-
if not policy["cps_uris"].respond_to?(:each)
|
121
|
-
raise ArgumentError, "CPS URIs must be an array of strings"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
if not policy["user_notices"].nil?
|
125
|
-
if not policy["user_notices"].respond_to?(:each)
|
126
|
-
raise ArgumentError, "User notices must be an array of hashes"
|
127
|
-
else
|
128
|
-
policy["user_notices"].each do |un|
|
129
|
-
if not un["organization"].nil? and un["notice_numbers"].nil?
|
130
|
-
raise ArgumentError, "If you provide an organization you must provide notice numbers"
|
131
|
-
end
|
132
|
-
if not un["notice_numbers"].nil? and un["organization"].nil?
|
133
|
-
raise ArgumentError, "If you provide notice numbers you must provide an organization"
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
@certificate_policies = policies
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# @private
|
145
|
-
def validate_name_constraints(nc)
|
146
|
-
if not nc.nil?
|
147
|
-
if not nc.kind_of?(Hash)
|
148
|
-
raise ArgumentError, "name_constraints must be provided as a hash"
|
149
|
-
end
|
150
|
-
["permitted","excluded"].each do |key|
|
151
|
-
if not nc[key].nil?
|
152
|
-
validate_name_constraints_elements(key,nc[key])
|
153
|
-
end
|
154
|
-
end
|
155
|
-
if (nc["permitted"].nil? or nc["permitted"].empty?) and (nc["excluded"].nil? or nc["excluded"].empty?)
|
156
|
-
raise ArgumentError, "If name_constraints are supplied you must have at least one valid permitted or excluded element"
|
157
|
-
end
|
158
|
-
end
|
159
|
-
@name_constraints = nc
|
160
|
-
end
|
161
|
-
|
162
|
-
# @private
|
163
|
-
def validate_name_constraints_elements(type,arr)
|
164
|
-
if not arr.kind_of?(Array)
|
165
|
-
raise ArgumentError, "#{type} must be an array"
|
166
|
-
end
|
167
|
-
arr.each do |el|
|
168
|
-
if not el.kind_of?(Hash) or not el.has_key?("type") or not el.has_key?("value")
|
169
|
-
raise ArgumentError, "Elements within the #{type} array must be hashes with both type and value"
|
170
|
-
end
|
171
|
-
if R509::ASN1::GeneralName.map_type_to_tag(el["type"]) == nil
|
172
|
-
raise ArgumentError, "#{el["type"]} is not an allowed type. Check R509::ASN1::GeneralName.map_type_to_tag to see a list of types"
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
# @private
|
178
|
-
# validates the structure of the certificate policies array
|
179
|
-
def validate_basic_constraints(constraints)
|
180
|
-
if not constraints.nil?
|
181
|
-
if not constraints.respond_to?(:has_key?) or not constraints.has_key?("ca")
|
182
|
-
raise ArgumentError, "You must supply a hash with a key named \"ca\" with a boolean value"
|
183
|
-
end
|
184
|
-
if constraints["ca"].nil? or (not constraints["ca"].kind_of?(TrueClass) and not constraints["ca"].kind_of?(FalseClass))
|
185
|
-
raise ArgumentError, "You must supply true/false for the ca key when specifying basic constraints"
|
186
|
-
end
|
187
|
-
if constraints["ca"] == false and not constraints["path_length"].nil?
|
188
|
-
raise ArgumentError, "path_length is not allowed when ca is false"
|
189
|
-
end
|
190
|
-
if constraints["ca"] == true and not constraints["path_length"].nil? and (constraints["path_length"] < 0 or not constraints["path_length"].kind_of?(Integer))
|
191
|
-
raise ArgumentError, "Path length must be a non-negative integer (>= 0)"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
@basic_constraints = constraints
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# returns information about the subject item policy for a profile
|
199
|
-
class SubjectItemPolicy
|
200
|
-
attr_reader :required, :optional
|
201
|
-
|
202
|
-
# @param [Hash] hash of required/optional subject items. These must be in OpenSSL shortname format.
|
203
|
-
# @example sample hash
|
204
|
-
# {"CN" => "required",
|
205
|
-
# "O" => "required",
|
206
|
-
# "OU" => "optional",
|
207
|
-
# "ST" => "required",
|
208
|
-
# "C" => "required",
|
209
|
-
# "L" => "required",
|
210
|
-
# "emailAddress" => "optional"}
|
211
|
-
def initialize(hash={})
|
212
|
-
if not hash.kind_of?(Hash)
|
213
|
-
raise ArgumentError, "Must supply a hash in form 'shortname'=>'required/optional'"
|
214
|
-
end
|
215
|
-
@required = []
|
216
|
-
@optional = []
|
217
|
-
if not hash.empty?
|
218
|
-
hash.each_pair do |key,value|
|
219
|
-
if value == "required"
|
220
|
-
@required.push(key)
|
221
|
-
elsif value == "optional"
|
222
|
-
@optional.push(key)
|
223
|
-
else
|
224
|
-
raise ArgumentError, "Unknown subject item policy value. Allowed values are required and optional"
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
# @param [R509::Subject] subject
|
231
|
-
# @return [R509::Subject] validated version of the subject or error
|
232
|
-
def validate_subject(subject)
|
233
|
-
# convert the subject components into an array of component names that match
|
234
|
-
# those that are on the required list
|
235
|
-
supplied = subject.to_a.each do |item|
|
236
|
-
@required.include?(item[0])
|
237
|
-
end.map do |item|
|
238
|
-
item[0]
|
239
|
-
end
|
240
|
-
# so we can make sure they gave us everything that's required
|
241
|
-
diff = @required - supplied
|
242
|
-
if not diff.empty?
|
243
|
-
raise R509::R509Error, "This profile requires you supply "+@required.join(", ")
|
244
|
-
end
|
245
|
-
|
246
|
-
# the validated subject contains only those subject components that are either
|
247
|
-
# required or optional
|
248
|
-
R509::Subject.new(subject.to_a.select do |item|
|
249
|
-
@required.include?(item[0]) or @optional.include?(item[0])
|
250
|
-
end)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
# pool of configs, so we can support multiple CAs from a single config file
|
255
|
-
class CAConfigPool
|
256
|
-
# @option configs [Hash<String, R509::Config::CAConfig>] the configs to add to the pool
|
257
|
-
def initialize(configs)
|
258
|
-
@configs = configs
|
259
|
-
end
|
260
|
-
|
261
|
-
# get all the config names
|
262
|
-
def names
|
263
|
-
@configs.keys
|
264
|
-
end
|
265
|
-
|
266
|
-
# retrieve a particular config by its name
|
267
|
-
def [](name)
|
268
|
-
@configs[name]
|
269
|
-
end
|
270
|
-
|
271
|
-
# @return a list of all the configs in this pool
|
272
|
-
def all
|
273
|
-
@configs.values
|
274
|
-
end
|
275
|
-
|
276
|
-
# Loads the named configuration config from a yaml string.
|
277
|
-
# @param [String] name The name of the config within the file. Note
|
278
|
-
# that a single yaml file can contain more than one configuration.
|
279
|
-
# @param [String] yaml_data The filename to load yaml config data from.
|
280
|
-
def self.from_yaml(name, yaml_data, opts = {})
|
281
|
-
conf = YAML.load(yaml_data)
|
282
|
-
configs = {}
|
283
|
-
conf[name].each_pair do |ca_name, data|
|
284
|
-
configs[ca_name] = R509::Config::CAConfig.load_from_hash(data, opts)
|
285
|
-
end
|
286
|
-
R509::Config::CAConfigPool.new(configs)
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
# Stores a configuration for our CA.
|
291
|
-
class CAConfig
|
292
|
-
include R509::IOHelpers
|
293
|
-
extend R509::IOHelpers
|
294
|
-
attr_accessor :ca_cert, :crl_validity_hours, :message_digest,
|
295
|
-
:cdp_location, :crl_start_skew_seconds, :ocsp_location, :ocsp_chain,
|
296
|
-
:ocsp_start_skew_seconds, :ocsp_validity_hours, :crl_number_file, :crl_list_file,
|
297
|
-
:ca_issuers_location
|
298
|
-
|
299
|
-
# @option opts [R509::Cert] :ca_cert Cert+Key pair
|
300
|
-
# @option opts [Integer] :crl_validity_hours (168) The number of hours that
|
301
|
-
# a CRL will be valid. Defaults to 7 days.
|
302
|
-
# @option opts [Hash<String, R509::Config::CAProfile>] :profiles
|
303
|
-
# @option opts [String] :message_digest (SHA1) The hashing algorithm to use.
|
304
|
-
# @option opts [Array] :cdp_location array of strings (URLs)
|
305
|
-
# @option opts [Array] :ocsp_location array of strings (URLs)
|
306
|
-
# @option opts [Array] :ca_issuers_location array of strings (URLs)
|
307
|
-
# @option opts [String] :crl_number_file The file that we will save
|
308
|
-
# the CRL numbers to. defaults to a StringIO object if not provided
|
309
|
-
# @option opts [String] :crl_list_file The file that we will save
|
310
|
-
# the CRL list data to. defaults to a StringIO object if not provided
|
311
|
-
# @option opts [R509::Cert] :ocsp_cert An optional cert+key pair
|
312
|
-
# OCSP signing delegate
|
313
|
-
# @option opts [Array<OpenSSL::X509::Certificate>] :ocsp_chain An optional array
|
314
|
-
# that constitutes the chain to attach to an OCSP response
|
315
|
-
#
|
316
|
-
def initialize(opts = {} )
|
317
|
-
if not opts.has_key?(:ca_cert) then
|
318
|
-
raise ArgumentError, 'Config object requires that you pass :ca_cert'
|
319
|
-
end
|
320
|
-
|
321
|
-
@ca_cert = opts[:ca_cert]
|
322
|
-
|
323
|
-
if not @ca_cert.kind_of?(R509::Cert) then
|
324
|
-
raise ArgumentError, ':ca_cert must be of type R509::Cert'
|
325
|
-
end
|
326
|
-
|
327
|
-
#ocsp data
|
328
|
-
if opts.has_key?(:ocsp_cert) and not opts[:ocsp_cert].kind_of?(R509::Cert) and not opts[:ocsp_cert].nil?
|
329
|
-
raise ArgumentError, ':ocsp_cert, if provided, must be of type R509::Cert'
|
330
|
-
end
|
331
|
-
if opts.has_key?(:ocsp_cert) and not opts[:ocsp_cert].nil? and not opts[:ocsp_cert].has_private_key?
|
332
|
-
raise ArgumentError, ':ocsp_cert must contain a private key, not just a certificate'
|
333
|
-
end
|
334
|
-
@ocsp_cert = opts[:ocsp_cert] unless opts[:ocsp_cert].nil?
|
335
|
-
validate_ocsp_location opts[:ocsp_location]
|
336
|
-
validate_ca_issuers_location opts[:ca_issuers_location]
|
337
|
-
@ocsp_chain = opts[:ocsp_chain] if opts[:ocsp_chain].kind_of?(Array)
|
338
|
-
@ocsp_validity_hours = opts[:ocsp_validity_hours] || 168
|
339
|
-
@ocsp_start_skew_seconds = opts[:ocsp_start_skew_seconds] || 3600
|
340
|
-
|
341
|
-
@crl_validity_hours = opts[:crl_validity_hours] || 168
|
342
|
-
@crl_start_skew_seconds = opts[:crl_start_skew_seconds] || 3600
|
343
|
-
@crl_number_file = opts[:crl_number_file] || nil
|
344
|
-
@crl_list_file = opts[:crl_list_file] || nil
|
345
|
-
validate_cdp_location opts[:cdp_location]
|
346
|
-
@message_digest = opts[:message_digest] || "SHA1"
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
@profiles = {}
|
351
|
-
if opts[:profiles]
|
352
|
-
opts[:profiles].each_pair do |name, prof|
|
353
|
-
set_profile(name, prof)
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
end
|
358
|
-
|
359
|
-
# @return [R509::Cert] either a custom OCSP cert or the ca_cert
|
360
|
-
def ocsp_cert
|
361
|
-
if @ocsp_cert.nil? then @ca_cert else @ocsp_cert end
|
362
|
-
end
|
363
|
-
|
364
|
-
# @param [String] name The name of the profile
|
365
|
-
# @param [R509::Config::CAProfile] prof The profile configuration
|
366
|
-
def set_profile(name, prof)
|
367
|
-
unless prof.is_a?(R509::Config::CAProfile)
|
368
|
-
raise TypeError, "profile is supposed to be a R509::Config::CAProfile"
|
369
|
-
end
|
370
|
-
@profiles[name] = prof
|
371
|
-
end
|
372
|
-
|
373
|
-
# @param [String] prof
|
374
|
-
# @return [R509::Config::CAProfile] The config profile.
|
375
|
-
def profile(prof)
|
376
|
-
if !@profiles.has_key?(prof)
|
377
|
-
raise R509::R509Error, "unknown profile '#{prof}'"
|
378
|
-
end
|
379
|
-
@profiles[prof]
|
380
|
-
end
|
381
|
-
|
382
|
-
# @return [Integer] The number of profiles
|
383
|
-
def num_profiles
|
384
|
-
@profiles.count
|
385
|
-
end
|
386
|
-
|
387
|
-
|
388
|
-
######### Class Methods ##########
|
389
|
-
|
390
|
-
# Load the configuration from a data hash. The same type that might be
|
391
|
-
# used when loading from a YAML file.
|
392
|
-
# @param [Hash] conf A hash containing all the configuration options
|
393
|
-
# @option opts [String] :ca_root_path The root path for the CA. Defaults to
|
394
|
-
# the current working directory.
|
395
|
-
def self.load_from_hash(conf, opts = {})
|
396
|
-
if conf.nil?
|
397
|
-
raise ArgumentError, "conf not found"
|
398
|
-
end
|
399
|
-
unless conf.kind_of?(Hash)
|
400
|
-
raise ArgumentError, "conf must be a Hash"
|
401
|
-
end
|
402
|
-
|
403
|
-
ca_root_path = Pathname.new(opts[:ca_root_path] || FileUtils.getwd)
|
404
|
-
|
405
|
-
unless File.directory?(ca_root_path)
|
406
|
-
raise R509Error, "ca_root_path is not a directory: #{ca_root_path}"
|
407
|
-
end
|
408
|
-
|
409
|
-
ca_cert_hash = conf['ca_cert']
|
410
|
-
|
411
|
-
if ca_cert_hash.has_key?('engine')
|
412
|
-
ca_cert = self.load_with_engine(ca_cert_hash,ca_root_path)
|
413
|
-
end
|
414
|
-
|
415
|
-
if ca_cert.nil? and ca_cert_hash.has_key?('pkcs12')
|
416
|
-
ca_cert = self.load_with_pkcs12(ca_cert_hash,ca_root_path)
|
417
|
-
end
|
418
|
-
|
419
|
-
if ca_cert.nil? and ca_cert_hash.has_key?('cert')
|
420
|
-
ca_cert = self.load_with_key(ca_cert_hash,ca_root_path)
|
421
|
-
end
|
422
|
-
|
423
|
-
if conf.has_key?("ocsp_cert")
|
424
|
-
if conf["ocsp_cert"].has_key?('engine')
|
425
|
-
ocsp_cert = self.load_with_engine(conf["ocsp_cert"],ca_root_path)
|
426
|
-
end
|
427
|
-
|
428
|
-
if ocsp_cert.nil? and conf["ocsp_cert"].has_key?('pkcs12')
|
429
|
-
ocsp_cert = self.load_with_pkcs12(conf["ocsp_cert"],ca_root_path)
|
430
|
-
end
|
431
|
-
|
432
|
-
if ocsp_cert.nil? and conf["ocsp_cert"].has_key?('cert')
|
433
|
-
ocsp_cert = self.load_with_key(conf["ocsp_cert"],ca_root_path)
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
ocsp_chain = []
|
438
|
-
if conf.has_key?("ocsp_chain")
|
439
|
-
ocsp_chain_data = read_data(ca_root_path+conf["ocsp_chain"])
|
440
|
-
cert_regex = /-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m
|
441
|
-
ocsp_chain_data.scan(cert_regex) do |cert|
|
442
|
-
ocsp_chain.push(OpenSSL::X509::Certificate.new(cert))
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
opts = {
|
447
|
-
:ca_cert => ca_cert,
|
448
|
-
:ocsp_cert => ocsp_cert,
|
449
|
-
:ocsp_chain => ocsp_chain,
|
450
|
-
:crl_validity_hours => conf['crl_validity_hours'],
|
451
|
-
:ocsp_validity_hours => conf['ocsp_validity_hours'],
|
452
|
-
:ocsp_start_skew_seconds => conf['ocsp_start_skew_seconds'],
|
453
|
-
:ocsp_location => conf['ocsp_location'],
|
454
|
-
:ca_issuers_location => conf['ca_issuers_location'],
|
455
|
-
:cdp_location => conf['cdp_location'],
|
456
|
-
:message_digest => conf['message_digest'],
|
457
|
-
}
|
458
|
-
|
459
|
-
if conf.has_key?("crl_list")
|
460
|
-
opts[:crl_list_file] = (ca_root_path + conf['crl_list']).to_s
|
461
|
-
end
|
462
|
-
|
463
|
-
if conf.has_key?("crl_number")
|
464
|
-
opts[:crl_number_file] = (ca_root_path + conf['crl_number']).to_s
|
465
|
-
end
|
466
|
-
|
467
|
-
|
468
|
-
profs = {}
|
469
|
-
conf['profiles'].keys.each do |profile|
|
470
|
-
data = conf['profiles'][profile]
|
471
|
-
if not data["subject_item_policy"].nil?
|
472
|
-
subject_item_policy = R509::Config::SubjectItemPolicy.new(data["subject_item_policy"])
|
473
|
-
end
|
474
|
-
profs[profile] = R509::Config::CAProfile.new(:key_usage => data["key_usage"],
|
475
|
-
:extended_key_usage => data["extended_key_usage"],
|
476
|
-
:basic_constraints => data["basic_constraints"],
|
477
|
-
:certificate_policies => data["certificate_policies"],
|
478
|
-
:ocsp_no_check => data["ocsp_no_check"],
|
479
|
-
:inhibit_any_policy => data["inhibit_any_policy"],
|
480
|
-
:policy_constraints => data["policy_constraints"],
|
481
|
-
:name_constraints => data["name_constraints"],
|
482
|
-
:subject_item_policy => subject_item_policy)
|
483
|
-
end unless conf['profiles'].nil?
|
484
|
-
opts[:profiles] = profs
|
485
|
-
|
486
|
-
# Create the instance.
|
487
|
-
self.new(opts)
|
488
|
-
end
|
489
|
-
|
490
|
-
# Loads the named configuration config from a yaml file.
|
491
|
-
# @param [String] conf_name The name of the config within the file. Note
|
492
|
-
# that a single yaml file can contain more than one configuration.
|
493
|
-
# @param [String] yaml_file The filename to load yaml config data from.
|
494
|
-
def self.load_yaml(conf_name, yaml_file, opts = {})
|
495
|
-
conf = YAML.load_file(yaml_file)
|
496
|
-
self.load_from_hash(conf[conf_name], opts)
|
497
|
-
end
|
498
|
-
|
499
|
-
# Loads the named configuration config from a yaml string.
|
500
|
-
# @param [String] conf_name The name of the config within the file. Note
|
501
|
-
# that a single yaml file can contain more than one configuration.
|
502
|
-
# @param [String] yaml_data The filename to load yaml config data from.
|
503
|
-
def self.from_yaml(conf_name, yaml_data, opts = {})
|
504
|
-
conf = YAML.load(yaml_data)
|
505
|
-
self.load_from_hash(conf[conf_name], opts)
|
506
|
-
end
|
507
|
-
|
508
|
-
private
|
509
|
-
|
510
|
-
def self.load_with_engine(ca_cert_hash,ca_root_path)
|
511
|
-
if ca_cert_hash.has_key?('key')
|
512
|
-
raise ArgumentError, "You can't specify both key and engine"
|
513
|
-
end
|
514
|
-
if ca_cert_hash.has_key?('pkcs12')
|
515
|
-
raise ArgumentError, "You can't specify both engine and pkcs12"
|
516
|
-
end
|
517
|
-
if not ca_cert_hash.has_key?('key_name')
|
518
|
-
raise ArgumentError, "You must supply a key_name with an engine"
|
519
|
-
end
|
520
|
-
|
521
|
-
if ca_cert_hash['engine'].respond_to?(:load_private_key)
|
522
|
-
#this path is only for testing...ugh
|
523
|
-
engine = ca_cert_hash['engine']
|
524
|
-
else
|
525
|
-
#this path can't be tested by unit tests. bah!
|
526
|
-
engine = R509::Engine.instance.load(ca_cert_hash['engine'])
|
527
|
-
end
|
528
|
-
ca_key = R509::PrivateKey.new(
|
529
|
-
:engine => engine,
|
530
|
-
:key_name => ca_cert_hash['key_name']
|
531
|
-
)
|
532
|
-
ca_cert_file = ca_root_path + ca_cert_hash['cert']
|
533
|
-
ca_cert = R509::Cert.new(
|
534
|
-
:cert => read_data(ca_cert_file),
|
535
|
-
:key => ca_key
|
536
|
-
)
|
537
|
-
ca_cert
|
538
|
-
end
|
539
|
-
|
540
|
-
def self.load_with_pkcs12(ca_cert_hash,ca_root_path)
|
541
|
-
if ca_cert_hash.has_key?('cert')
|
542
|
-
raise ArgumentError, "You can't specify both pkcs12 and cert"
|
543
|
-
end
|
544
|
-
if ca_cert_hash.has_key?('key')
|
545
|
-
raise ArgumentError, "You can't specify both pkcs12 and key"
|
546
|
-
end
|
547
|
-
|
548
|
-
pkcs12_file = ca_root_path + ca_cert_hash['pkcs12']
|
549
|
-
ca_cert = R509::Cert.new(
|
550
|
-
:pkcs12 => read_data(pkcs12_file),
|
551
|
-
:password => ca_cert_hash['password']
|
552
|
-
)
|
553
|
-
ca_cert
|
554
|
-
end
|
555
|
-
|
556
|
-
def self.load_with_key(ca_cert_hash,ca_root_path)
|
557
|
-
ca_cert_file = ca_root_path + ca_cert_hash['cert']
|
558
|
-
|
559
|
-
if ca_cert_hash.has_key?('key')
|
560
|
-
ca_key_file = ca_root_path + ca_cert_hash['key']
|
561
|
-
ca_key = R509::PrivateKey.new(
|
562
|
-
:key => read_data(ca_key_file),
|
563
|
-
:password => ca_cert_hash['password']
|
564
|
-
)
|
565
|
-
ca_cert = R509::Cert.new(
|
566
|
-
:cert => read_data(ca_cert_file),
|
567
|
-
:key => ca_key
|
568
|
-
)
|
569
|
-
else
|
570
|
-
# in certain cases (OCSP responders for example) we may want
|
571
|
-
# to load a ca_cert with no private key
|
572
|
-
ca_cert = R509::Cert.new(:cert => read_data(ca_cert_file))
|
573
|
-
end
|
574
|
-
ca_cert
|
575
|
-
end
|
576
|
-
|
577
|
-
private
|
578
|
-
|
579
|
-
# @private
|
580
|
-
def validate_cdp_location(location)
|
581
|
-
if not location.nil? and not location.kind_of?(Array)
|
582
|
-
raise ArgumentError, "cdp_location must be an array if provided"
|
583
|
-
end
|
584
|
-
@cdp_location = location
|
585
|
-
end
|
586
|
-
|
587
|
-
# @private
|
588
|
-
def validate_ocsp_location(location)
|
589
|
-
if not location.nil? and not location.kind_of?(Array)
|
590
|
-
raise ArgumentError, "ocsp_location must be an array if provided"
|
591
|
-
end
|
592
|
-
@ocsp_location = location
|
593
|
-
end
|
594
|
-
|
595
|
-
# @private
|
596
|
-
def validate_ca_issuers_location(location)
|
597
|
-
if not location.nil? and not location.kind_of?(Array)
|
598
|
-
raise ArgumentError, "ca_issuers_location must be an array if provided"
|
599
|
-
end
|
600
|
-
@ca_issuers_location = location
|
601
|
-
end
|
602
|
-
end
|
3
|
+
require('r509/config/ca_config.rb')
|
4
|
+
require('r509/config/cert_profile.rb')
|
5
|
+
require('r509/config/subject_item_policy.rb')
|
603
6
|
end
|
604
7
|
end
|