r509 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CONTRIBUTING.mdown +21 -0
  5. data/LICENSE +13 -0
  6. data/README.mdown +548 -0
  7. data/Rakefile +5 -0
  8. data/bin/r509 +16 -17
  9. data/doc/R509.html +42 -26
  10. data/doc/R509/ASN1.html +22 -16
  11. data/doc/R509/ASN1/GeneralName.html +180 -173
  12. data/doc/R509/ASN1/GeneralNames.html +390 -62
  13. data/doc/R509/CRL.html +9 -7
  14. data/doc/R509/CRL/Administrator.html +208 -623
  15. data/doc/R509/CRL/FileReaderWriter.html +856 -0
  16. data/doc/R509/CRL/ReaderWriter.html +524 -0
  17. data/doc/R509/CRL/SignedList.html +29 -42
  18. data/doc/R509/CSR.html +248 -333
  19. data/doc/R509/Cert.html +364 -491
  20. data/doc/R509/Cert/Extensions.html +134 -43
  21. data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +335 -65
  22. data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +201 -102
  23. data/doc/R509/Cert/Extensions/BasicConstraints.html +297 -68
  24. data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +690 -77
  25. data/doc/R509/Cert/Extensions/CertificatePolicies.html +293 -43
  26. data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +321 -173
  27. data/doc/R509/Cert/Extensions/GeneralNamesMixin.html +656 -0
  28. data/doc/R509/Cert/Extensions/InhibitAnyPolicy.html +270 -42
  29. data/doc/R509/Cert/Extensions/KeyUsage.html +334 -184
  30. data/doc/R509/Cert/Extensions/NameConstraints.html +363 -93
  31. data/doc/R509/{ASN1 → Cert/Extensions}/NoticeReference.html +209 -48
  32. data/doc/R509/Cert/Extensions/OCSPNoCheck.html +244 -17
  33. data/doc/R509/Cert/Extensions/PolicyConstraints.html +322 -71
  34. data/doc/R509/{ASN1 → Cert/Extensions}/PolicyInformation.html +204 -43
  35. data/doc/R509/{ASN1 → Cert/Extensions}/PolicyQualifiers.html +205 -48
  36. data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +348 -143
  37. data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +165 -13
  38. data/doc/R509/{ASN1 → Cert/Extensions}/UserNotice.html +204 -43
  39. data/doc/R509/Cert/Extensions/ValidationMixin.html +120 -0
  40. data/doc/R509/CertificateAuthority.html +9 -7
  41. data/doc/R509/CertificateAuthority/OptionsBuilder.html +475 -0
  42. data/doc/R509/CertificateAuthority/Signer.html +149 -198
  43. data/doc/R509/Config.html +10 -8
  44. data/doc/R509/Config/CAConfig.html +708 -625
  45. data/doc/R509/Config/CAConfigPool.html +179 -31
  46. data/doc/R509/Config/CertProfile.html +1544 -0
  47. data/doc/R509/Config/SubjectItemPolicy.html +437 -99
  48. data/doc/R509/Engine.html +14 -28
  49. data/doc/R509/Helpers.html +1014 -0
  50. data/doc/R509/MessageDigest.html +73 -25
  51. data/doc/R509/NameSanitizer.html +39 -39
  52. data/doc/R509/OCSP.html +5 -5
  53. data/doc/R509/OCSP/Request.html +5 -5
  54. data/doc/R509/OCSP/Request/Nonce.html +5 -5
  55. data/doc/R509/OCSP/Response.html +7 -7
  56. data/doc/R509/OIDMapper.html +121 -6
  57. data/doc/R509/PrivateKey.html +226 -227
  58. data/doc/R509/R509Error.html +5 -5
  59. data/doc/R509/SPKI.html +244 -342
  60. data/doc/R509/Subject.html +241 -70
  61. data/doc/R509/Validity.html +5 -5
  62. data/doc/R509/Validity/Checker.html +5 -5
  63. data/doc/R509/Validity/DefaultChecker.html +5 -9
  64. data/doc/R509/Validity/DefaultWriter.html +5 -9
  65. data/doc/R509/Validity/Status.html +5 -5
  66. data/doc/R509/Validity/Writer.html +5 -5
  67. data/doc/_index.html +92 -30
  68. data/doc/class_list.html +2 -2
  69. data/doc/file.CONTRIBUTING.html +96 -0
  70. data/doc/file.LICENSE.html +87 -0
  71. data/doc/file.README.html +279 -389
  72. data/doc/file.YAML.html +243 -0
  73. data/doc/file.r509.html +298 -105
  74. data/doc/file_list.html +11 -2
  75. data/doc/frames.html +1 -1
  76. data/doc/index.html +279 -389
  77. data/doc/js/full_list.js +6 -1
  78. data/doc/method_list.html +869 -1139
  79. data/doc/top-level-namespace.html +103 -5
  80. data/lib/r509.rb +7 -2
  81. data/lib/r509/asn1.rb +97 -135
  82. data/lib/r509/cert.rb +17 -106
  83. data/lib/r509/cert/extensions.rb +13 -676
  84. data/lib/r509/cert/extensions/authority_info_access.rb +128 -0
  85. data/lib/r509/cert/extensions/authority_key_identifier.rb +100 -0
  86. data/lib/r509/cert/extensions/base.rb +142 -0
  87. data/lib/r509/cert/extensions/basic_constraints.rb +119 -0
  88. data/lib/r509/cert/extensions/certificate_policies.rb +262 -0
  89. data/lib/r509/cert/extensions/crl_distribution_points.rb +98 -0
  90. data/lib/r509/cert/extensions/extended_key_usage.rb +189 -0
  91. data/lib/r509/cert/extensions/inhibit_any_policy.rb +70 -0
  92. data/lib/r509/cert/extensions/key_usage.rb +209 -0
  93. data/lib/r509/cert/extensions/name_constraints.rb +179 -0
  94. data/lib/r509/cert/extensions/ocsp_no_check.rb +56 -0
  95. data/lib/r509/cert/extensions/policy_constraints.rb +122 -0
  96. data/lib/r509/cert/extensions/subject_alternative_name.rb +88 -0
  97. data/lib/r509/cert/extensions/subject_key_identifier.rb +56 -0
  98. data/lib/r509/cert/extensions/validation_mixin.rb +42 -0
  99. data/lib/r509/certificate_authority/options_builder.rb +142 -0
  100. data/lib/r509/certificate_authority/signer.rb +189 -0
  101. data/lib/r509/config.rb +3 -600
  102. data/lib/r509/config/ca_config.rb +414 -0
  103. data/lib/r509/config/cert_profile.rb +110 -0
  104. data/lib/r509/config/subject_item_policy.rb +118 -0
  105. data/lib/r509/crl/administrator.rb +169 -0
  106. data/lib/r509/crl/reader_writer.rb +109 -0
  107. data/lib/r509/crl/signed_list.rb +135 -0
  108. data/lib/r509/csr.rb +35 -116
  109. data/lib/r509/engine.rb +21 -11
  110. data/lib/r509/helpers.rb +110 -0
  111. data/lib/r509/io_helpers.rb +18 -13
  112. data/lib/r509/message_digest.rb +13 -3
  113. data/lib/r509/oid_mapper.rb +14 -0
  114. data/lib/r509/private_key.rb +74 -50
  115. data/lib/r509/spki.rb +50 -113
  116. data/lib/r509/subject.rb +24 -2
  117. data/lib/r509/trollop.rb +788 -0
  118. data/lib/r509/version.rb +1 -1
  119. data/r509.yaml +289 -96
  120. data/spec/asn1_spec.rb +171 -98
  121. data/spec/cert/extensions/authority_info_access_spec.rb +247 -0
  122. data/spec/cert/extensions/authority_key_identifier_spec.rb +85 -0
  123. data/spec/cert/extensions/base_spec.rb +172 -0
  124. data/spec/cert/extensions/basic_constraints_spec.rb +185 -0
  125. data/spec/cert/extensions/certificate_policies_spec.rb +288 -0
  126. data/spec/cert/extensions/crl_distribution_points_spec.rb +149 -0
  127. data/spec/cert/extensions/extended_key_usage_spec.rb +174 -0
  128. data/spec/cert/extensions/inhibit_any_policy_spec.rb +92 -0
  129. data/spec/cert/extensions/key_usage_spec.rb +172 -0
  130. data/spec/cert/extensions/name_constraints_spec.rb +335 -0
  131. data/spec/cert/extensions/ocsp_no_check_spec.rb +76 -0
  132. data/spec/cert/extensions/policy_constraints_spec.rb +155 -0
  133. data/spec/cert/extensions/subject_alternative_name_spec.rb +354 -0
  134. data/spec/cert/extensions/subject_key_identifier_spec.rb +64 -0
  135. data/spec/cert_spec.rb +11 -9
  136. data/spec/certificate_authority/options_builder_spec.rb +307 -0
  137. data/spec/certificate_authority/signer_spec.rb +278 -0
  138. data/spec/config/ca_config_spec.rb +405 -0
  139. data/spec/config/cert_profile_spec.rb +88 -0
  140. data/spec/config/subject_item_policy_spec.rb +81 -0
  141. data/spec/crl/administrator_spec.rb +199 -0
  142. data/spec/crl/reader_writer_spec.rb +97 -0
  143. data/spec/crl/signed_list_spec.rb +84 -0
  144. data/spec/csr_spec.rb +43 -36
  145. data/spec/engine_spec.rb +51 -0
  146. data/spec/fixtures.rb +40 -40
  147. data/spec/fixtures/cert1.pem +1 -1
  148. data/spec/fixtures/config_pool_test_minimal.yaml +11 -15
  149. data/spec/fixtures/config_test.yaml +96 -59
  150. data/spec/fixtures/config_test_dsa.yaml +29 -35
  151. data/spec/fixtures/config_test_ec.yaml +29 -35
  152. data/spec/fixtures/config_test_engine_key.yaml +7 -7
  153. data/spec/fixtures/config_test_engine_no_key_name.yaml +6 -6
  154. data/spec/fixtures/config_test_minimal.yaml +3 -5
  155. data/spec/fixtures/config_test_password.yaml +4 -6
  156. data/spec/fixtures/config_test_various.yaml +147 -137
  157. data/spec/fixtures/crl_list_file.txt +1 -1
  158. data/spec/fixtures/test_ca_crl.cer +20 -0
  159. data/spec/fixtures/test_ca_crl.key +28 -0
  160. data/spec/fixtures/test_ca_crl.p12 +0 -0
  161. data/spec/message_digest_spec.rb +6 -0
  162. data/spec/oid_mapper_spec.rb +11 -0
  163. data/spec/private_key_spec.rb +19 -18
  164. data/spec/spec_helper.rb +10 -6
  165. data/spec/spki_spec.rb +38 -19
  166. data/spec/subject_spec.rb +16 -0
  167. metadata +108 -59
  168. metadata.gz.sig +0 -0
  169. data/README.md +0 -638
  170. data/doc/R509/Config/CAProfile.html +0 -1015
  171. data/doc/R509/IOHelpers.html +0 -564
  172. data/lib/r509/certificate_authority.rb +0 -407
  173. data/lib/r509/crl.rb +0 -351
  174. data/spec/cert/extensions_spec.rb +0 -1095
  175. data/spec/certificate_authority_spec.rb +0 -681
  176. data/spec/config_spec.rb +0 -562
  177. 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