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,414 @@
|
|
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
|
+
require 'r509/config/subject_item_policy'
|
11
|
+
require 'r509/config/cert_profile'
|
12
|
+
|
13
|
+
module R509
|
14
|
+
# Module to contain all configuration related classes (e.g. CAConfig, CertProfile, SubjectItemPolicy)
|
15
|
+
module Config
|
16
|
+
# pool of configs, so we can support multiple CAs from a single config file
|
17
|
+
class CAConfigPool
|
18
|
+
# @option configs [Hash<String, R509::Config::CAConfig>] the configs to add to the pool
|
19
|
+
def initialize(configs)
|
20
|
+
@configs = configs
|
21
|
+
end
|
22
|
+
|
23
|
+
# get all the config names
|
24
|
+
def names
|
25
|
+
@configs.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
# retrieve a particular config by its name
|
29
|
+
def [](name)
|
30
|
+
@configs[name]
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return a list of all the configs in this pool
|
34
|
+
def all
|
35
|
+
@configs.values
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Hash]
|
39
|
+
def to_h
|
40
|
+
@configs.merge(@configs) { |k,v| v.to_h }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [YAML]
|
44
|
+
def to_yaml
|
45
|
+
self.to_h.to_yaml
|
46
|
+
end
|
47
|
+
|
48
|
+
# Loads the named configuration config from a yaml string.
|
49
|
+
# @param [String] name The name of the config within the file. Note
|
50
|
+
# that a single yaml file can contain more than one configuration.
|
51
|
+
# @param [String] yaml_data The filename to load yaml config data from.
|
52
|
+
def self.from_yaml(name, yaml_data, opts = {})
|
53
|
+
conf = YAML.load(yaml_data)
|
54
|
+
configs = {}
|
55
|
+
conf[name].each_pair do |ca_name, data|
|
56
|
+
configs[ca_name] = R509::Config::CAConfig.load_from_hash(data, opts)
|
57
|
+
end
|
58
|
+
R509::Config::CAConfigPool.new(configs)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Stores a configuration for our CA.
|
63
|
+
class CAConfig
|
64
|
+
include R509::IOHelpers
|
65
|
+
extend R509::IOHelpers
|
66
|
+
attr_reader :ca_cert, :crl_validity_hours, :crl_start_skew_seconds,
|
67
|
+
:crl_number_file, :crl_list_file, :crl_md,
|
68
|
+
:ocsp_chain, :ocsp_start_skew_seconds, :ocsp_validity_hours
|
69
|
+
|
70
|
+
# Default number of seconds to subtract from now when calculating the signing time of an OCSP response
|
71
|
+
DEFAULT_OCSP_START_SKEW_SECONDS = 3600
|
72
|
+
# Default number of hours an OCSP response should be valid for
|
73
|
+
DEFAULT_OCSP_VALIDITY_HOURS = 168
|
74
|
+
# Default number of hours a CRL should be valid for
|
75
|
+
DEFAULT_CRL_VALIDITY_HOURS = 168
|
76
|
+
# Default number of seconds to subtract from now when calculating the signing time of a CRL
|
77
|
+
DEFAULT_CRL_START_SKEW_SECONDS = 3600
|
78
|
+
|
79
|
+
# @option opts [R509::Cert] :ca_cert Cert+Key pair
|
80
|
+
# @option opts [Integer] :crl_validity_hours (168) The number of hours that
|
81
|
+
# a CRL will be valid. Defaults to 7 days.
|
82
|
+
# @option opts [Hash<String, R509::Config::CertProfile>] :profiles
|
83
|
+
# @option opts [String] :crl_number_file A file to save the CRL number
|
84
|
+
# into. This is only used if you use the default FileReaderWriter in CRL::Administrator
|
85
|
+
# @option opts [String] :crl_md Optional digest for signing CRLs. sha1, sha224, sha256, sha384, sha512, md5. Defaults to R509::MessageDigest::DEFAULT_MD
|
86
|
+
# @option opts [String] :crl_list_file A file to serialize revoked certificates into. This
|
87
|
+
# is only used if you use the default FileReaderWriter in CRL::Administrator
|
88
|
+
# @option opts [R509::Cert] :ocsp_cert An optional cert+key pair
|
89
|
+
# OCSP signing delegate
|
90
|
+
# @option opts [R509::Cert] :crl_cert An optional cert+key pair
|
91
|
+
# CRL signing delegate
|
92
|
+
# @option opts [Array<OpenSSL::X509::Certificate>] :ocsp_chain An optional array
|
93
|
+
# that constitutes the chain to attach to an OCSP response
|
94
|
+
# @option opts [Integer] :ocsp_validity_hours Number of hours OCSP responses should be valid for
|
95
|
+
# @option opts [Integer] :ocsp_start_skew_seconds The number of seconds to subtract from Time.now when calculating the signing time of an OCSP response. This is important to handle bad user clocks.
|
96
|
+
# @option opts [Integer] :crl_validity_hours Number of hours CRLs should be valid for
|
97
|
+
# @option opts [Integer] :crl_start_skew_seconds The number of seconds to subtract from Time.now when calculating the signing time of a CRL. This is important to handle bad user clocks.
|
98
|
+
#
|
99
|
+
def initialize(opts = {} )
|
100
|
+
if not opts.has_key?(:ca_cert) then
|
101
|
+
raise ArgumentError, 'Config object requires that you pass :ca_cert'
|
102
|
+
end
|
103
|
+
|
104
|
+
@ca_cert = opts[:ca_cert]
|
105
|
+
|
106
|
+
if not @ca_cert.kind_of?(R509::Cert) then
|
107
|
+
raise ArgumentError, ':ca_cert must be of type R509::Cert'
|
108
|
+
end
|
109
|
+
|
110
|
+
parse_ocsp_data(opts)
|
111
|
+
parse_crl_data(opts)
|
112
|
+
|
113
|
+
@profiles = {}
|
114
|
+
if opts[:profiles]
|
115
|
+
opts[:profiles].each_pair do |name, prof|
|
116
|
+
set_profile(name, prof)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [R509::Cert] either a custom OCSP cert or the ca_cert
|
122
|
+
def ocsp_cert
|
123
|
+
if @ocsp_cert.nil? then @ca_cert else @ocsp_cert end
|
124
|
+
end
|
125
|
+
|
126
|
+
# @return [R509::Cert] either a custom CRL cert or the ca_cert
|
127
|
+
def crl_cert
|
128
|
+
if @crl_cert.nil? then @ca_cert else @crl_cert end
|
129
|
+
end
|
130
|
+
|
131
|
+
# @param [String] name The name of the profile
|
132
|
+
# @param [R509::Config::CertProfile] prof The profile configuration
|
133
|
+
def set_profile(name, prof)
|
134
|
+
unless prof.is_a?(R509::Config::CertProfile)
|
135
|
+
raise TypeError, "profile is supposed to be a R509::Config::CertProfile"
|
136
|
+
end
|
137
|
+
@profiles[name] = prof
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param [String] prof
|
141
|
+
# @return [R509::Config::CertProfile] The config profile.
|
142
|
+
def profile(prof)
|
143
|
+
if !@profiles.has_key?(prof)
|
144
|
+
raise R509::R509Error, "unknown profile '#{prof}'"
|
145
|
+
end
|
146
|
+
@profiles[prof]
|
147
|
+
end
|
148
|
+
|
149
|
+
# @return [Integer] The number of profiles
|
150
|
+
def num_profiles
|
151
|
+
@profiles.count
|
152
|
+
end
|
153
|
+
|
154
|
+
# @return [Hash]
|
155
|
+
def to_h
|
156
|
+
hash = {}
|
157
|
+
hash["ca_cert"] = build_cert_hash(@ca_cert)
|
158
|
+
hash["ocsp_cert"] = build_cert_hash(@ocsp_cert) unless @ocsp_cert.nil?
|
159
|
+
hash["crl_cert"] = build_cert_hash(@crl_cert) unless @crl_cert.nil?
|
160
|
+
hash["ocsp_chain"] = "<add_path>" unless @ocsp_chain.nil?
|
161
|
+
hash["ocsp_start_skew_seconds"] = @ocsp_start_skew_seconds
|
162
|
+
hash["ocsp_validity_hours"] = @ocsp_validity_hours
|
163
|
+
hash["crl_start_skew_seconds"] = @crl_start_skew_seconds
|
164
|
+
hash["crl_validity_hours"] = @crl_validity_hours
|
165
|
+
hash["crl_list_file"] = @crl_list_file unless @crl_list_file.nil?
|
166
|
+
hash["crl_number_file"] = @crl_number_file unless @crl_number_file.nil?
|
167
|
+
hash["crl_md"] = @crl_md
|
168
|
+
hash["profiles"] = @profiles.merge(@profiles) { |k,v| v.to_h } unless @profiles.empty?
|
169
|
+
hash
|
170
|
+
end
|
171
|
+
|
172
|
+
# @return [YAML]
|
173
|
+
def to_yaml
|
174
|
+
self.to_h.to_yaml
|
175
|
+
end
|
176
|
+
|
177
|
+
######### Class Methods ##########
|
178
|
+
|
179
|
+
# Load the configuration from a data hash. The same type that might be
|
180
|
+
# used when loading from a YAML file.
|
181
|
+
# @param [Hash] conf A hash containing all the configuration options
|
182
|
+
# @option opts [String] :ca_root_path The root path for the CA. Defaults to
|
183
|
+
# the current working directory.
|
184
|
+
def self.load_from_hash(conf, opts = {})
|
185
|
+
if conf.nil?
|
186
|
+
raise ArgumentError, "conf not found"
|
187
|
+
end
|
188
|
+
unless conf.kind_of?(Hash)
|
189
|
+
raise ArgumentError, "conf must be a Hash"
|
190
|
+
end
|
191
|
+
|
192
|
+
ca_root_path = Pathname.new(opts[:ca_root_path] || FileUtils.getwd)
|
193
|
+
|
194
|
+
unless File.directory?(ca_root_path)
|
195
|
+
raise R509Error, "ca_root_path is not a directory: #{ca_root_path}"
|
196
|
+
end
|
197
|
+
|
198
|
+
ca_cert = self.load_ca_cert(conf['ca_cert'],ca_root_path)
|
199
|
+
|
200
|
+
ocsp_cert = self.load_ca_cert(conf['ocsp_cert'],ca_root_path)
|
201
|
+
|
202
|
+
crl_cert = self.load_ca_cert(conf['crl_cert'],ca_root_path)
|
203
|
+
|
204
|
+
ocsp_chain = build_ocsp_chain(conf['ocsp_chain'],ca_root_path)
|
205
|
+
|
206
|
+
opts = {
|
207
|
+
:ca_cert => ca_cert,
|
208
|
+
:ocsp_cert => ocsp_cert,
|
209
|
+
:crl_cert => crl_cert,
|
210
|
+
:ocsp_chain => ocsp_chain,
|
211
|
+
:crl_validity_hours => conf['crl_validity_hours'],
|
212
|
+
:ocsp_validity_hours => conf['ocsp_validity_hours'],
|
213
|
+
:ocsp_start_skew_seconds => conf['ocsp_start_skew_seconds'],
|
214
|
+
:crl_md => conf['crl_md'],
|
215
|
+
}
|
216
|
+
|
217
|
+
if conf.has_key?("crl_list_file")
|
218
|
+
opts[:crl_list_file] = (ca_root_path + conf['crl_list_file']).to_s
|
219
|
+
end
|
220
|
+
|
221
|
+
if conf.has_key?("crl_number_file")
|
222
|
+
opts[:crl_number_file] = (ca_root_path + conf['crl_number_file']).to_s
|
223
|
+
end
|
224
|
+
|
225
|
+
opts[:profiles] = self.load_profiles(conf['profiles'])
|
226
|
+
|
227
|
+
# Create the instance.
|
228
|
+
self.new(opts)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Used by load_from_hash
|
232
|
+
#
|
233
|
+
# @param profiles [Hash] Hash of profiles
|
234
|
+
# @return [Hash] hash of parsed profiles
|
235
|
+
def self.load_profiles(profiles)
|
236
|
+
profs = {}
|
237
|
+
profiles.each do |profile,data|
|
238
|
+
if not data["subject_item_policy"].nil?
|
239
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new(data["subject_item_policy"])
|
240
|
+
end
|
241
|
+
profs[profile] = R509::Config::CertProfile.new(:key_usage => data["key_usage"],
|
242
|
+
:extended_key_usage => data["extended_key_usage"],
|
243
|
+
:basic_constraints => data["basic_constraints"],
|
244
|
+
:certificate_policies => data["certificate_policies"],
|
245
|
+
:ocsp_no_check => data["ocsp_no_check"],
|
246
|
+
:inhibit_any_policy => data["inhibit_any_policy"],
|
247
|
+
:policy_constraints => data["policy_constraints"],
|
248
|
+
:name_constraints => data["name_constraints"],
|
249
|
+
:crl_distribution_points => data["crl_distribution_points"],
|
250
|
+
:authority_info_access => data["authority_info_access"],
|
251
|
+
:default_md => data["default_md"],
|
252
|
+
:allowed_mds => data["allowed_mds"],
|
253
|
+
:subject_item_policy => subject_item_policy)
|
254
|
+
end unless profiles.nil?
|
255
|
+
profs
|
256
|
+
end
|
257
|
+
|
258
|
+
# Loads the named configuration config from a yaml file.
|
259
|
+
# @param [String] conf_name The name of the config within the file. Note
|
260
|
+
# that a single yaml file can contain more than one configuration.
|
261
|
+
# @param [String] yaml_file The filename to load yaml config data from.
|
262
|
+
def self.load_yaml(conf_name, yaml_file, opts = {})
|
263
|
+
conf = YAML.load_file(yaml_file)
|
264
|
+
self.load_from_hash(conf[conf_name], opts)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Loads the named configuration config from a yaml string.
|
268
|
+
# @param [String] conf_name The name of the config within the file. Note
|
269
|
+
# that a single yaml file can contain more than one configuration.
|
270
|
+
# @param [String] yaml_data The filename to load yaml config data from.
|
271
|
+
def self.from_yaml(conf_name, yaml_data, opts = {})
|
272
|
+
conf = YAML.load(yaml_data)
|
273
|
+
self.load_from_hash(conf[conf_name], opts)
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def parse_ocsp_data(opts)
|
279
|
+
#ocsp data
|
280
|
+
if opts.has_key?(:ocsp_cert)
|
281
|
+
check_ocsp_crl_delegate(opts[:ocsp_cert],'ocsp_cert')
|
282
|
+
@ocsp_cert = opts[:ocsp_cert]
|
283
|
+
end
|
284
|
+
@ocsp_chain = opts[:ocsp_chain] if opts[:ocsp_chain].kind_of?(Array)
|
285
|
+
@ocsp_validity_hours = opts[:ocsp_validity_hours] || DEFAULT_OCSP_VALIDITY_HOURS
|
286
|
+
@ocsp_start_skew_seconds = opts[:ocsp_start_skew_seconds] || DEFAULT_OCSP_START_SKEW_SECONDS
|
287
|
+
end
|
288
|
+
|
289
|
+
def parse_crl_data(opts)
|
290
|
+
if opts.has_key?(:crl_cert)
|
291
|
+
check_ocsp_crl_delegate(opts[:crl_cert],'crl_cert')
|
292
|
+
@crl_cert = opts[:crl_cert]
|
293
|
+
end
|
294
|
+
@crl_validity_hours = opts[:crl_validity_hours] || DEFAULT_CRL_VALIDITY_HOURS
|
295
|
+
@crl_start_skew_seconds = opts[:crl_start_skew_seconds] || DEFAULT_CRL_START_SKEW_SECONDS
|
296
|
+
@crl_number_file = opts[:crl_number_file] || nil
|
297
|
+
@crl_list_file = opts[:crl_list_file] || nil
|
298
|
+
@crl_md = opts[:crl_md] || R509::MessageDigest::DEFAULT_MD
|
299
|
+
end
|
300
|
+
|
301
|
+
def build_cert_hash(obj)
|
302
|
+
hash = { "cert" => "<add_path>" }
|
303
|
+
if not obj.key.nil? and obj.key.in_hardware?
|
304
|
+
hash["engine"] = { :so_path => "<add_path>", :id => "<add_name>" }
|
305
|
+
return hash
|
306
|
+
elsif not obj.key.nil?
|
307
|
+
hash["key"] = "<add_path>"
|
308
|
+
end
|
309
|
+
hash
|
310
|
+
end
|
311
|
+
|
312
|
+
def self.load_ca_cert(ca_cert_hash,ca_root_path)
|
313
|
+
return nil if ca_cert_hash.nil?
|
314
|
+
if ca_cert_hash.has_key?('engine')
|
315
|
+
ca_cert = self.load_with_engine(ca_cert_hash,ca_root_path)
|
316
|
+
end
|
317
|
+
|
318
|
+
if ca_cert.nil? and ca_cert_hash.has_key?('pkcs12')
|
319
|
+
ca_cert = self.load_with_pkcs12(ca_cert_hash,ca_root_path)
|
320
|
+
end
|
321
|
+
|
322
|
+
if ca_cert.nil? and ca_cert_hash.has_key?('cert')
|
323
|
+
ca_cert = self.load_with_key(ca_cert_hash,ca_root_path)
|
324
|
+
end
|
325
|
+
|
326
|
+
return ca_cert
|
327
|
+
end
|
328
|
+
|
329
|
+
def self.load_with_engine(ca_cert_hash,ca_root_path)
|
330
|
+
if ca_cert_hash.has_key?('key')
|
331
|
+
raise ArgumentError, "You can't specify both key and engine"
|
332
|
+
end
|
333
|
+
if ca_cert_hash.has_key?('pkcs12')
|
334
|
+
raise ArgumentError, "You can't specify both engine and pkcs12"
|
335
|
+
end
|
336
|
+
if not ca_cert_hash.has_key?('key_name')
|
337
|
+
raise ArgumentError, "You must supply a key_name with an engine"
|
338
|
+
end
|
339
|
+
|
340
|
+
engine = R509::Engine.instance.load(ca_cert_hash['engine'])
|
341
|
+
|
342
|
+
ca_key = R509::PrivateKey.new(
|
343
|
+
:engine => engine,
|
344
|
+
:key_name => ca_cert_hash['key_name']
|
345
|
+
)
|
346
|
+
ca_cert_file = ca_root_path + ca_cert_hash['cert']
|
347
|
+
ca_cert = R509::Cert.new(
|
348
|
+
:cert => read_data(ca_cert_file),
|
349
|
+
:key => ca_key
|
350
|
+
)
|
351
|
+
ca_cert
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.load_with_pkcs12(ca_cert_hash,ca_root_path)
|
355
|
+
if ca_cert_hash.has_key?('cert')
|
356
|
+
raise ArgumentError, "You can't specify both pkcs12 and cert"
|
357
|
+
end
|
358
|
+
if ca_cert_hash.has_key?('key')
|
359
|
+
raise ArgumentError, "You can't specify both pkcs12 and key"
|
360
|
+
end
|
361
|
+
|
362
|
+
pkcs12_file = ca_root_path + ca_cert_hash['pkcs12']
|
363
|
+
ca_cert = R509::Cert.new(
|
364
|
+
:pkcs12 => read_data(pkcs12_file),
|
365
|
+
:password => ca_cert_hash['password']
|
366
|
+
)
|
367
|
+
ca_cert
|
368
|
+
end
|
369
|
+
|
370
|
+
def self.load_with_key(ca_cert_hash,ca_root_path)
|
371
|
+
ca_cert_file = ca_root_path + ca_cert_hash['cert']
|
372
|
+
|
373
|
+
if ca_cert_hash.has_key?('key')
|
374
|
+
ca_key_file = ca_root_path + ca_cert_hash['key']
|
375
|
+
ca_key = R509::PrivateKey.new(
|
376
|
+
:key => read_data(ca_key_file),
|
377
|
+
:password => ca_cert_hash['password']
|
378
|
+
)
|
379
|
+
ca_cert = R509::Cert.new(
|
380
|
+
:cert => read_data(ca_cert_file),
|
381
|
+
:key => ca_key
|
382
|
+
)
|
383
|
+
else
|
384
|
+
# in certain cases (OCSP responders for example) we may want
|
385
|
+
# to load a ca_cert with no private key
|
386
|
+
ca_cert = R509::Cert.new(:cert => read_data(ca_cert_file))
|
387
|
+
end
|
388
|
+
ca_cert
|
389
|
+
end
|
390
|
+
|
391
|
+
def check_ocsp_crl_delegate(cert,kind)
|
392
|
+
if not cert.kind_of?(R509::Cert) and not cert.nil?
|
393
|
+
raise ArgumentError, ":#{kind}, if provided, must be of type R509::Cert"
|
394
|
+
end
|
395
|
+
if not cert.nil? and not cert.has_private_key?
|
396
|
+
raise ArgumentError, ":#{kind} must contain a private key, not just a certificate"
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def self.build_ocsp_chain(ocsp_chain_path,ca_root_path)
|
401
|
+
ocsp_chain = []
|
402
|
+
if not ocsp_chain_path.nil?
|
403
|
+
ocsp_chain_data = read_data(ca_root_path+ocsp_chain_path)
|
404
|
+
cert_regex = /-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m
|
405
|
+
ocsp_chain_data.scan(cert_regex) do |cert|
|
406
|
+
ocsp_chain.push(OpenSSL::X509::Certificate.new(cert))
|
407
|
+
end
|
408
|
+
end
|
409
|
+
ocsp_chain
|
410
|
+
end
|
411
|
+
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
@@ -0,0 +1,110 @@
|
|
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
|
+
module R509
|
12
|
+
# Module to contain all configuration related classes (e.g. CAConfig, CertProfile, SubjectItemPolicy)
|
13
|
+
module Config
|
14
|
+
# Provides access to configuration profiles
|
15
|
+
class CertProfile
|
16
|
+
|
17
|
+
attr_reader :basic_constraints, :key_usage, :extended_key_usage,
|
18
|
+
:certificate_policies, :subject_item_policy, :ocsp_no_check,
|
19
|
+
:inhibit_any_policy, :policy_constraints, :name_constraints,
|
20
|
+
:authority_info_access, :crl_distribution_points, :default_md, :allowed_mds
|
21
|
+
|
22
|
+
# All hash options for CertProfile are optional.
|
23
|
+
# @option opts [Hash] :basic_constraints
|
24
|
+
# @option opts [Hash] :key_usage
|
25
|
+
# @option opts [Hash] :extended_key_usage
|
26
|
+
# @option opts [Hash] :certificate_policies
|
27
|
+
# @option opts [Boolean] :ocsp_no_check Sets OCSP No Check extension in the certificate if true
|
28
|
+
# @option opts [Hash] :inhibit_any_policy Sets the value of the inhibitAnyPolicy extension
|
29
|
+
# @option opts [Hash] :policy_constraints Sets the value of the policyConstraints extension
|
30
|
+
# @option opts [Hash] :authority_info_access
|
31
|
+
# @option opts [Hash] :crl_distribution_points
|
32
|
+
# @option opts [Hash] :name_constraints Sets the value of the nameConstraints extension
|
33
|
+
# @option opts [R509::Config::SubjectItemPolicy] :subject_item_policy
|
34
|
+
# @option opts [String] :default_md (SHA1) The hashing algorithm to use.
|
35
|
+
# @option opts [Array] :allowed_mds (nil) Array of allowed hashes.
|
36
|
+
# default_md will be automatically added to this list if it isn't already listed.
|
37
|
+
def initialize(opts = {})
|
38
|
+
@basic_constraints = R509::Cert::Extensions::BasicConstraints.new(opts[:basic_constraints]) unless opts[:basic_constraints].nil?
|
39
|
+
@key_usage = R509::Cert::Extensions::KeyUsage.new(opts[:key_usage]) unless opts[:key_usage].nil?
|
40
|
+
@extended_key_usage = R509::Cert::Extensions::ExtendedKeyUsage.new(opts[:extended_key_usage]) unless opts[:extended_key_usage].nil?
|
41
|
+
@certificate_policies = R509::Cert::Extensions::CertificatePolicies.new(opts[:certificate_policies]) unless opts[:certificate_policies].nil?
|
42
|
+
@inhibit_any_policy = R509::Cert::Extensions::InhibitAnyPolicy.new(opts[:inhibit_any_policy]) unless opts[:inhibit_any_policy].nil?
|
43
|
+
@policy_constraints = R509::Cert::Extensions::PolicyConstraints.new(opts[:policy_constraints]) unless opts[:policy_constraints].nil?
|
44
|
+
@name_constraints = R509::Cert::Extensions::NameConstraints.new(opts[:name_constraints]) unless opts[:name_constraints].nil?
|
45
|
+
@ocsp_no_check = R509::Cert::Extensions::OCSPNoCheck.new(opts[:ocsp_no_check]) unless opts[:ocsp_no_check].nil?
|
46
|
+
@authority_info_access = R509::Cert::Extensions::AuthorityInfoAccess.new(opts[:authority_info_access]) unless opts[:authority_info_access].nil?
|
47
|
+
@crl_distribution_points = R509::Cert::Extensions::CRLDistributionPoints.new(opts[:crl_distribution_points]) unless opts[:crl_distribution_points].nil?
|
48
|
+
@subject_item_policy = validate_subject_item_policy(opts[:subject_item_policy])
|
49
|
+
@default_md = validate_md(opts[:default_md] || R509::MessageDigest::DEFAULT_MD)
|
50
|
+
@allowed_mds = validate_allowed_mds(opts[:allowed_mds])
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Hash]
|
54
|
+
def to_h
|
55
|
+
hash = {}
|
56
|
+
hash["basic_constraints"] = @basic_constraints.to_h unless @basic_constraints.nil?
|
57
|
+
hash["key_usage"] = @key_usage.to_h unless @key_usage.nil?
|
58
|
+
hash["extended_key_usage"] = @extended_key_usage.to_h unless @extended_key_usage.nil?
|
59
|
+
hash["certificate_policies"] = @certificate_policies.to_h unless @certificate_policies.nil?
|
60
|
+
hash["inhibit_any_policy"] = @inhibit_any_policy.to_h unless @inhibit_any_policy.nil?
|
61
|
+
hash["policy_constraints"] = @policy_constraints.to_h unless @policy_constraints.nil?
|
62
|
+
hash["name_constraints"] = @name_constraints.to_h unless @name_constraints.nil?
|
63
|
+
hash["ocsp_no_check"] = @ocsp_no_check.to_h unless @ocsp_no_check.nil?
|
64
|
+
hash["authority_info_access"] = @authority_info_access.to_h unless @authority_info_access.nil?
|
65
|
+
hash["crl_distribution_points"] = @crl_distribution_points.to_h unless @crl_distribution_points.nil?
|
66
|
+
hash["subject_item_policy"] = @subject_item_policy.to_h unless @subject_item_policy.nil?
|
67
|
+
hash["default_md"] = @default_md unless @default_md.nil?
|
68
|
+
hash["allowed_mds"] = @allowed_mds unless @allowed_mds.nil?
|
69
|
+
hash
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [YAML]
|
73
|
+
def to_yaml
|
74
|
+
self.to_h.to_yaml
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
# @private
|
79
|
+
def validate_allowed_mds(allowed_mds)
|
80
|
+
if allowed_mds.respond_to?(:each)
|
81
|
+
allowed_mds = allowed_mds.map { |md| validate_md(md) }
|
82
|
+
# case insensitively check if the default_md is in the allowed_mds
|
83
|
+
# and add it if it's not there.
|
84
|
+
if not allowed_mds.any?{ |s| s.casecmp(@default_md)==0 }
|
85
|
+
allowed_mds.push @default_md
|
86
|
+
end
|
87
|
+
end
|
88
|
+
allowed_mds
|
89
|
+
end
|
90
|
+
|
91
|
+
# @private
|
92
|
+
def validate_md(md)
|
93
|
+
md = md.upcase
|
94
|
+
if not R509::MessageDigest::KNOWN_MDS.include?(md)
|
95
|
+
raise ArgumentError, "An unknown message digest was supplied. Permitted: #{R509::MessageDigest::KNOWN_MDS.join(", ")}"
|
96
|
+
end
|
97
|
+
md
|
98
|
+
end
|
99
|
+
|
100
|
+
# @private
|
101
|
+
# validates subject item policy
|
102
|
+
def validate_subject_item_policy(sip)
|
103
|
+
if not sip.nil? and not sip.kind_of?(R509::Config::SubjectItemPolicy)
|
104
|
+
raise ArgumentError, "subject_item_policy must be of type R509::Config::SubjectItemPolicy"
|
105
|
+
end
|
106
|
+
sip
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|