r509-ocsp-responder 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,6 @@
1
- require 'rubygems' if RUBY_VERSION < "1.9"
2
1
  require 'sinatra/base'
3
2
  require 'r509'
4
3
  require 'r509/ocsp/signer'
5
- require 'r509/validity/redis'
6
4
  require 'base64'
7
5
  require 'dependo'
8
6
  require 'logger'
@@ -13,157 +11,157 @@ require File.dirname(__FILE__)+'/ocsp-config.rb'
13
11
  # I'd rather use HUP, but daemons like thin already capture that
14
12
  # so we can't use it.
15
13
  Signal.trap("USR2") do
16
- R509::Ocsp::Responder::OcspConfig.load_config
17
- R509::Ocsp::Responder::OcspConfig.print_config
14
+ R509::OCSP::Responder::OCSPConfig.load_config
15
+ R509::OCSP::Responder::OCSPConfig.print_config
18
16
  end
19
17
 
20
18
 
21
- module R509::Ocsp::Responder
22
- #error for status checking
23
- class StatusError < StandardError
24
- end
25
-
26
- class Server < Sinatra::Base
27
- include Dependo::Mixin
19
+ module R509::OCSP::Responder
20
+ #error for status checking
21
+ class StatusError < StandardError
22
+ end
28
23
 
29
- configure do
30
- mime_type :ocsp, 'application/ocsp-response'
31
- disable :protection #disable Rack::Protection (for speed)
32
- disable :logging
33
- set :environment, :production
34
- end
24
+ class Server < Sinatra::Base
25
+ include Dependo::Mixin
35
26
 
36
- error do
37
- log.error env["sinatra.error"].inspect
38
- log.error env["sinatra.error"].backtrace.join("\n")
39
- "Something is amiss with our OCSP responder. You should ... wait?"
40
- end
27
+ configure do
28
+ mime_type :ocsp, 'application/ocsp-response'
29
+ disable :protection #disable Rack::Protection (for speed)
30
+ disable :logging
31
+ set :environment, :production
32
+ end
41
33
 
42
- error OpenSSL::OCSP::OCSPError do
43
- "Invalid request"
44
- end
34
+ error do
35
+ log.error env["sinatra.error"].inspect
36
+ log.error env["sinatra.error"].backtrace.join("\n")
37
+ "Something is amiss with our OCSP responder. You should ... wait?"
38
+ end
45
39
 
46
- error R509::Ocsp::Responder::StatusError do
47
- "Down"
48
- end
40
+ error OpenSSL::OCSP::OCSPError do
41
+ "Invalid request"
42
+ end
49
43
 
50
- get '/favicon.ico' do
51
- log.debug "go away. no children."
52
- "go away. no children"
53
- end
44
+ error R509::OCSP::Responder::StatusError do
45
+ "Down"
46
+ end
54
47
 
55
- get '/status/?' do
56
- begin
57
- if Dependo::Registry[:ocsp_signer].validity_checker.is_available?
58
- "OK"
59
- else
60
- raise R509::Ocsp::Responder::StatusError
61
- end
62
- rescue
63
- raise R509::Ocsp::Responder::StatusError
64
- end
65
- end
48
+ get '/favicon.ico' do
49
+ log.debug "go away. no children."
50
+ "go away. no children"
51
+ end
66
52
 
67
- get '/*' do
68
- raw_request = params[:splat].join("/")
69
- #remove any leading slashes (looking at you MS Crypto API)
70
- raw_request.sub!(/^\/+/,"")
71
- log.info { "GET Request: "+raw_request }
72
- der = Base64.decode64(raw_request)
73
- request_response = handle_ocsp_request(der, "GET")
74
- build_headers(request_response)
75
- request_response[:response].to_der
53
+ get '/status/?' do
54
+ begin
55
+ if Dependo::Registry[:ocsp_signer].validity_checker.is_available?
56
+ "OK"
57
+ else
58
+ raise R509::OCSP::Responder::StatusError
76
59
  end
60
+ rescue
61
+ raise R509::OCSP::Responder::StatusError
62
+ end
63
+ end
77
64
 
78
- post '/' do
79
- if request.media_type == 'application/ocsp-request'
80
- der = request.env["rack.input"].read
81
- log.info { "POST Request: "+Base64.encode64(der).gsub!(/\n/,"") }
82
- request_response = handle_ocsp_request(der, "POST")
83
- request_response[:response].to_der
84
- end
85
- end
65
+ get '/*' do
66
+ raw_request = params[:splat].join("/")
67
+ #remove any leading slashes (looking at you MS Crypto API)
68
+ raw_request.sub!(/^\/+/,"")
69
+ log.info { "GET Request: "+raw_request }
70
+ der = Base64.decode64(raw_request)
71
+ request_response = handle_ocsp_request(der, "GET")
72
+ build_headers(request_response)
73
+ request_response[:response].to_der
74
+ end
86
75
 
87
- private
76
+ post '/' do
77
+ if request.media_type == 'application/ocsp-request'
78
+ der = request.env["rack.input"].read
79
+ log.info { "POST Request: "+Base64.encode64(der).gsub!(/\n/,"") }
80
+ request_response = handle_ocsp_request(der, "POST")
81
+ request_response[:response].to_der
82
+ end
83
+ end
88
84
 
89
- def handle_ocsp_request(der, method)
90
- begin
91
- request_response = ocsp_signer.handle_request(der)
85
+ private
92
86
 
93
- log_ocsp_response(request_response[:response],method)
87
+ def handle_ocsp_request(der, method)
88
+ begin
89
+ request_response = ocsp_signer.handle_request(der)
94
90
 
95
- content_type :ocsp
96
- request_response
97
- rescue StandardError => e
98
- log.error "unexpected error #{e}"
99
- raise e
100
- end
101
- end
91
+ log_ocsp_response(request_response[:response],method)
102
92
 
103
- def log_ocsp_response(ocsp_response, method="?")
104
- if response.nil?
105
- log.error "Something went horribly wrong"
106
- return
107
- end
93
+ content_type :ocsp
94
+ request_response
95
+ rescue StandardError => e
96
+ log.error "unexpected error #{e}"
97
+ raise e
98
+ end
99
+ end
108
100
 
109
- case ocsp_response.status
110
- when OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
111
- serial_data = ocsp_response.basic.status.map do |status|
112
- friendly_status = case status[1]
113
- when 0
114
- "VALID"
115
- when 1
116
- "REVOKED"
117
- when 2
118
- "UNKNOWN"
119
- end
120
- if ocsp_response.basic.status[0][0].respond_to?(:issuer_key_hash)
121
- config_used = ocsp_signer.request_checker.configs_hash[ocsp_response.basic.status[0][0].issuer_key_hash]
122
- else
123
- config_used = ocsp_signer.request_checker.configs.find do |config|
124
- #we need to create an OCSP::CertificateId object that has the right
125
- #issuer so we can pass it to #cmp_issuer. This is annoying because
126
- #CertificateId wants a cert and its issuer, but we don't want to
127
- #force users to provide an end entity cert just to make this comparison
128
- #work. So, we create a fake new cert and pass it in.
129
- ee_cert = OpenSSL::X509::Certificate.new
130
- ee_cert.issuer = config.ca_cert.cert.subject
131
- issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert)
132
- ocsp_response.basic.status[0][0].cmp_issuer(issuer_certid)
133
- end
134
- end
135
- stats.record(config_used.ca_cert.subject.to_s, status[0].serial.to_s, friendly_status) if Dependo::Registry.has_key?(:stats)
136
- status[0].serial.to_s+" Status: #{friendly_status}"
137
- end
138
- log.info { "#{method} Request For Serial(s): #{serial_data.join(",")} UserAgent: #{env["HTTP_USER_AGENT"]}" }
139
- when OpenSSL::OCSP::RESPONSE_STATUS_UNAUTHORIZED
140
- log.info { "#{method} Request For Unauthorized CA. UserAgent: #{env["HTTP_USER_AGENT"]}" }
141
- when OpenSSL::OCSP::RESPONSE_STATUS_MALFORMEDREQUEST
142
- log.info { "#{method} Malformed Request. UserAgent: #{env["HTTP_USER_AGENT"]}" }
101
+ def log_ocsp_response(ocsp_response, method="?")
102
+ if response.nil?
103
+ log.error "Something went horribly wrong"
104
+ return
105
+ end
106
+
107
+ case ocsp_response.status
108
+ when OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
109
+ serial_data = ocsp_response.basic.status.map do |status|
110
+ friendly_status = case status[1]
111
+ when 0
112
+ "VALID"
113
+ when 1
114
+ "REVOKED"
115
+ when 2
116
+ "UNKNOWN"
117
+ end
118
+ if ocsp_response.basic.status[0][0].respond_to?(:issuer_key_hash)
119
+ config_used = ocsp_signer.request_checker.configs_hash[ocsp_response.basic.status[0][0].issuer_key_hash]
120
+ else
121
+ config_used = ocsp_signer.request_checker.configs.find do |config|
122
+ #we need to create an OCSP::CertificateId object that has the right
123
+ #issuer so we can pass it to #cmp_issuer. This is annoying because
124
+ #CertificateId wants a cert and its issuer, but we don't want to
125
+ #force users to provide an end entity cert just to make this comparison
126
+ #work. So, we create a fake new cert and pass it in.
127
+ ee_cert = OpenSSL::X509::Certificate.new
128
+ ee_cert.issuer = config.ca_cert.cert.subject
129
+ issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert)
130
+ ocsp_response.basic.status[0][0].cmp_issuer(issuer_certid)
143
131
  end
132
+ end
133
+ stats.record(config_used.ca_cert.subject.to_s, status[0].serial.to_s, friendly_status) if Dependo::Registry.has_key?(:stats)
134
+ status[0].serial.to_s+" Status: #{friendly_status}"
144
135
  end
136
+ log.info { "#{method} Request For Serial(s): #{serial_data.join(",")} UserAgent: #{env["HTTP_USER_AGENT"]}" }
137
+ when OpenSSL::OCSP::RESPONSE_STATUS_UNAUTHORIZED
138
+ log.info { "#{method} Request For Unauthorized CA. UserAgent: #{env["HTTP_USER_AGENT"]}" }
139
+ when OpenSSL::OCSP::RESPONSE_STATUS_MALFORMEDREQUEST
140
+ log.info { "#{method} Malformed Request. UserAgent: #{env["HTTP_USER_AGENT"]}" }
141
+ end
142
+ end
145
143
 
146
- def build_headers(request_response)
147
- ocsp_response = request_response[:response]
148
- ocsp_request = request_response[:request]
149
-
150
- # cache_headers is injected via config.ru
151
- # we only cache if it's a RESPONSE_STATUS_SUCCESSFUL response and there's no nonce.
152
- if cache_headers and not ocsp_response.basic.nil? and ocsp_response.check_nonce(ocsp_request) == R509::Ocsp::Request::Nonce::BOTH_ABSENT
153
- calculated_max_age = ocsp_response.basic.status[0][5] - Time.now
154
- #same with max_cache_age
155
- if not max_cache_age or ( max_cache_age > calculated_max_age )
156
- max_age = calculated_max_age
157
- else
158
- max_age = max_cache_age
159
- end
160
-
161
- response["Last-Modified"] = Time.now.httpdate
162
- response["ETag"] = OpenSSL::Digest::SHA1.new(ocsp_response.to_der).to_s
163
- response["Expires"] = ocsp_response.basic.status[0][5].httpdate
164
- response["Cache-Control"] = "max-age=#{max_age.to_i}, public, no-transform, must-revalidate"
165
- end
144
+ def build_headers(request_response)
145
+ ocsp_response = request_response[:response]
146
+ ocsp_request = request_response[:request]
147
+
148
+ # cache_headers is injected via config.ru
149
+ # we only cache if it's a RESPONSE_STATUS_SUCCESSFUL response and there's no nonce.
150
+ if cache_headers and not ocsp_response.basic.nil? and ocsp_response.check_nonce(ocsp_request) == R509::OCSP::Request::Nonce::BOTH_ABSENT
151
+ calculated_max_age = ocsp_response.basic.status[0][5] - Time.now
152
+ #same with max_cache_age
153
+ if not max_cache_age or ( max_cache_age > calculated_max_age )
154
+ max_age = calculated_max_age
155
+ else
156
+ max_age = max_cache_age
166
157
  end
167
158
 
159
+ response["Last-Modified"] = Time.now.httpdate
160
+ response["ETag"] = OpenSSL::Digest::SHA1.new(ocsp_response.to_der).to_s
161
+ response["Expires"] = ocsp_response.basic.status[0][5].httpdate
162
+ response["Cache-Control"] = "max-age=#{max_age.to_i}, public, no-transform, must-revalidate"
163
+ end
168
164
  end
165
+
166
+ end
169
167
  end
@@ -1,7 +1,7 @@
1
1
  module R509
2
- module Ocsp
3
- module Responder
4
- VERSION="0.3.1"
5
- end
2
+ module OCSP
3
+ module Responder
4
+ VERSION="0.3.2"
6
5
  end
6
+ end
7
7
  end
@@ -4,241 +4,241 @@ require 'r509/config'
4
4
  require 'dependo'
5
5
 
6
6
  # OCSP related classes (signing, response, request)
7
- module R509::Ocsp
8
- # A class for signing OCSP responses
9
- class Signer
10
- attr_reader :validity_checker,:request_checker
11
-
12
- # @option options [Boolean] :copy_nonce copy nonce from request to response?
13
- # @option options [R509::Config::CaConfigPool] :configs CaConfigPool object
14
- # possible OCSP issuance roots that we want to issue OCSP responses for
15
- def initialize(options)
16
- if options.has_key?(:validity_checker)
17
- @validity_checker = options[:validity_checker]
18
- else
19
- @validity_checker = R509::Validity::DefaultChecker.new
20
- end
21
- @request_checker = Helper::RequestChecker.new(options[:configs], @validity_checker)
22
- @response_signer = Helper::ResponseSigner.new(options)
23
- end
24
-
7
+ module R509::OCSP
8
+ # A class for signing OCSP responses
9
+ class Signer
10
+ attr_reader :validity_checker,:request_checker
11
+
12
+ # @option options [Boolean] :copy_nonce copy nonce from request to response?
13
+ # @option options [R509::Config::CAConfigPool] :configs CAConfigPool object
14
+ # possible OCSP issuance roots that we want to issue OCSP responses for
15
+ def initialize(options)
16
+ if options.has_key?(:validity_checker)
17
+ @validity_checker = options[:validity_checker]
18
+ else
19
+ @validity_checker = R509::Validity::DefaultChecker.new
20
+ end
21
+ @request_checker = Helper::RequestChecker.new(options[:configs], @validity_checker)
22
+ @response_signer = Helper::ResponseSigner.new(options)
23
+ end
25
24
 
26
- # @param request [String,OpenSSL::OCSP::Request] OCSP request (string or parsed object)
27
- # @return [Hash]
28
- # * :request [OpenSSL::OCSP::Request] parsed request object
29
- # * :response [OpenSSL::OCSP::Response] full response object
30
- def handle_request(request)
31
- begin
32
- parsed_request = OpenSSL::OCSP::Request.new request
33
- rescue
34
- return {:response => @response_signer.create_response(OpenSSL::OCSP::RESPONSE_STATUS_MALFORMEDREQUEST), :request => nil}
35
- end
36
-
37
- statuses = @request_checker.check_statuses(parsed_request)
38
- if not @request_checker.validate_statuses(statuses)
39
- return {:response => @response_signer.create_response(OpenSSL::OCSP::RESPONSE_STATUS_UNAUTHORIZED), :request => nil}
40
- end
41
-
42
- basic_response = @response_signer.create_basic_response(parsed_request,statuses)
43
-
44
- {:response => @response_signer.create_response(
45
- OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL,
46
- basic_response
47
- ), :request => parsed_request}
48
- end
49
25
 
26
+ # @param request [String,OpenSSL::OCSP::Request] OCSP request (string or parsed object)
27
+ # @return [Hash]
28
+ # * :request [OpenSSL::OCSP::Request] parsed request object
29
+ # * :response [OpenSSL::OCSP::Response] full response object
30
+ def handle_request(request)
31
+ begin
32
+ parsed_request = OpenSSL::OCSP::Request.new request
33
+ rescue
34
+ return {:response => @response_signer.create_response(OpenSSL::OCSP::RESPONSE_STATUS_MALFORMEDREQUEST), :request => nil}
35
+ end
36
+
37
+ statuses = @request_checker.check_statuses(parsed_request)
38
+ if not @request_checker.validate_statuses(statuses)
39
+ return {:response => @response_signer.create_response(OpenSSL::OCSP::RESPONSE_STATUS_UNAUTHORIZED), :request => nil}
40
+ end
41
+
42
+ basic_response = @response_signer.create_basic_response(parsed_request,statuses)
43
+
44
+ {:response => @response_signer.create_response(
45
+ OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL,
46
+ basic_response
47
+ ), :request => parsed_request}
50
48
  end
49
+
50
+ end
51
51
  end
52
52
 
53
53
  #Helper module for OCSP handling
54
- module R509::Ocsp::Helper
55
- # checks requests for validity against a set of configs
56
- class RequestChecker
57
- include Dependo::Mixin
58
- attr_reader :configs,:configs_hash
59
-
60
- # @param [R509::Config::CaConfigPool] configs CaConfigPool object
61
- # @param [R509::Validity::Checker] validity_checker an implementation of the R509::Validity::Checker class
62
- def initialize(configs, validity_checker)
63
- unless configs.kind_of?(R509::Config::CaConfigPool)
64
- raise R509::R509Error, "Must pass R509::Config::CaConfigPool object"
65
- end
66
- if configs.all.empty?
67
- raise R509::R509Error, "Must be at least one R509::Config object"
68
- end
69
- @configs = configs.all
70
- test_cid = OpenSSL::OCSP::CertificateId.new(OpenSSL::X509::Certificate.new,OpenSSL::X509::Certificate.new)
71
- if test_cid.respond_to?(:issuer_key_hash)
72
- @configs_hash = {}
73
- @configs.each do |config|
74
- ee_cert = OpenSSL::X509::Certificate.new
75
- ee_cert.issuer = config.ca_cert.cert.subject
76
- # per RFC 5019
77
- # Clients MUST use SHA1 as the hashing algorithm for the
78
- # CertID.issuerNameHash and the CertID.issuerKeyHash values.
79
- # so we can safely assume that our inbound hashes will be SHA1
80
- issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert,OpenSSL::Digest::SHA1.new)
81
- @configs_hash[issuer_certid.issuer_key_hash] = config
82
- end
83
- end
84
- @validity_checker = validity_checker
85
- if @validity_checker.nil?
86
- raise R509::R509Error, "Must supply a R509::Validity::Checker"
87
- end
88
- if not @validity_checker.respond_to?(:check)
89
- raise R509::R509Error, "The validity checker must have a check method"
90
- end
54
+ module R509::OCSP::Helper
55
+ # checks requests for validity against a set of configs
56
+ class RequestChecker
57
+ include Dependo::Mixin
58
+ attr_reader :configs,:configs_hash
59
+
60
+ # @param [R509::Config::CAConfigPool] configs CAConfigPool object
61
+ # @param [R509::Validity::Checker] validity_checker an implementation of the R509::Validity::Checker class
62
+ def initialize(configs, validity_checker)
63
+ unless configs.kind_of?(R509::Config::CAConfigPool)
64
+ raise R509::R509Error, "Must pass R509::Config::CAConfigPool object"
65
+ end
66
+ if configs.all.empty?
67
+ raise R509::R509Error, "Must be at least one R509::Config object"
68
+ end
69
+ @configs = configs.all
70
+ test_cid = OpenSSL::OCSP::CertificateId.new(OpenSSL::X509::Certificate.new,OpenSSL::X509::Certificate.new)
71
+ if test_cid.respond_to?(:issuer_key_hash)
72
+ @configs_hash = {}
73
+ @configs.each do |config|
74
+ ee_cert = OpenSSL::X509::Certificate.new
75
+ ee_cert.issuer = config.ca_cert.cert.subject.name
76
+ # per RFC 5019
77
+ # Clients MUST use SHA1 as the hashing algorithm for the
78
+ # CertID.issuerNameHash and the CertID.issuerKeyHash values.
79
+ # so we can safely assume that our inbound hashes will be SHA1
80
+ issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert,OpenSSL::Digest::SHA1.new)
81
+ @configs_hash[issuer_certid.issuer_key_hash] = config
91
82
  end
83
+ end
84
+ @validity_checker = validity_checker
85
+ if @validity_checker.nil?
86
+ raise R509::R509Error, "Must supply a R509::Validity::Checker"
87
+ end
88
+ if not @validity_checker.respond_to?(:check)
89
+ raise R509::R509Error, "The validity checker must have a check method"
90
+ end
91
+ end
92
92
 
93
- # Loads and checks a raw OCSP request
94
- #
95
- # @param request [OpenSSL::OCSP::Request] OpenSSL OCSP Request object
96
- # @return [Hash] hash from the check_status method
97
- def check_statuses(request)
98
- request.certid.map { |certid|
99
- if certid.respond_to?(:issuer_key_hash)
100
- validated_config = @configs_hash[certid.issuer_key_hash]
101
- else
102
- validated_config = @configs.find do |config|
103
- #we need to create an OCSP::CertificateId object that has the right
104
- #issuer so we can pass it to #cmp_issuer. This is annoying because
105
- #CertificateId wants a cert and its issuer, but we don't want to
106
- #force users to provide an end entity cert just to make this comparison
107
- #work. So, we create a fake new cert and pass it in.
108
- ee_cert = OpenSSL::X509::Certificate.new
109
- ee_cert.issuer = config.ca_cert.cert.subject
110
- issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert)
111
- certid.cmp_issuer(issuer_certid)
112
- end
113
- end
114
-
115
- log.info "#{validated_config.ca_cert.subject.to_s} found for issuer" if validated_config
116
- check_status(certid, validated_config)
117
- }
93
+ # Loads and checks a raw OCSP request
94
+ #
95
+ # @param request [OpenSSL::OCSP::Request] OpenSSL OCSP Request object
96
+ # @return [Hash] hash from the check_status method
97
+ def check_statuses(request)
98
+ request.certid.map { |certid|
99
+ if certid.respond_to?(:issuer_key_hash)
100
+ validated_config = @configs_hash[certid.issuer_key_hash]
101
+ else
102
+ validated_config = @configs.find do |config|
103
+ #we need to create an OCSP::CertificateId object that has the right
104
+ #issuer so we can pass it to #cmp_issuer. This is annoying because
105
+ #CertificateId wants a cert and its issuer, but we don't want to
106
+ #force users to provide an end entity cert just to make this comparison
107
+ #work. So, we create a fake new cert and pass it in.
108
+ ee_cert = OpenSSL::X509::Certificate.new
109
+ ee_cert.issuer = config.ca_cert.cert.subject
110
+ issuer_certid = OpenSSL::OCSP::CertificateId.new(ee_cert,config.ca_cert.cert)
111
+ certid.cmp_issuer(issuer_certid)
112
+ end
118
113
  end
119
114
 
120
- # Determines whether the statuses constitute a request that is compliant.
121
- # No config means we don't know the CA, different configs means there are
122
- # requests from two different CAs in there. Both are invalid.
123
- #
124
- # @param statuses [Array<Hash>] array of hashes from check_statuses
125
- # @return [Boolean]
126
- def validate_statuses(statuses)
127
- validity = true
128
- config = nil
129
-
130
- statuses.each do |status|
131
- if status[:config].nil?
132
- validity = false
133
- end
134
- if config.nil?
135
- config = status[:config]
136
- end
137
- if config != status[:config]
138
- validity = false
139
- end
140
- end
141
-
142
- validity
143
- end
115
+ log.info "#{validated_config.ca_cert.subject.to_s} found for issuer" if validated_config
116
+ check_status(certid, validated_config)
117
+ }
118
+ end
144
119
 
145
- private
146
-
147
- # Checks the status of a certificate with the corresponding CA
148
- # @param certid [OpenSSL::OCSP::CertificateId] The certId object from check_statuses
149
- # @param validated_config [R509::Config]
150
- def check_status(certid, validated_config)
151
- if(validated_config == nil) then
152
- return {
153
- :certid => certid,
154
- :config => nil
155
- }
156
- else
157
- validity_status = @validity_checker.check(validated_config.ca_cert.subject.to_s,certid.serial)
158
- return {
159
- :certid => certid,
160
- :status => validity_status.ocsp_status,
161
- :revocation_reason => validity_status.revocation_reason,
162
- :revocation_time => validity_status.revocation_time,
163
- :config => validated_config
164
- }
165
- end
120
+ # Determines whether the statuses constitute a request that is compliant.
121
+ # No config means we don't know the CA, different configs means there are
122
+ # requests from two different CAs in there. Both are invalid.
123
+ #
124
+ # @param statuses [Array<Hash>] array of hashes from check_statuses
125
+ # @return [Boolean]
126
+ def validate_statuses(statuses)
127
+ validity = true
128
+ config = nil
129
+
130
+ statuses.each do |status|
131
+ if status[:config].nil?
132
+ validity = false
166
133
  end
134
+ if config.nil?
135
+ config = status[:config]
136
+ end
137
+ if config != status[:config]
138
+ validity = false
139
+ end
140
+ end
141
+
142
+ validity
167
143
  end
168
144
 
169
- #signs OCSP responses
170
- class ResponseSigner
171
- # @option options [Boolean] :copy_nonce
172
- def initialize(options)
173
- if options.has_key?(:copy_nonce)
174
- @copy_nonce = options[:copy_nonce]
175
- else
176
- @copy_nonce = false
177
- end
178
- end
145
+ private
146
+
147
+ # Checks the status of a certificate with the corresponding CA
148
+ # @param certid [OpenSSL::OCSP::CertificateId] The certId object from check_statuses
149
+ # @param validated_config [R509::Config]
150
+ def check_status(certid, validated_config)
151
+ if(validated_config == nil) then
152
+ return {
153
+ :certid => certid,
154
+ :config => nil
155
+ }
156
+ else
157
+ validity_status = @validity_checker.check(validated_config.ca_cert.subject.to_s,certid.serial)
158
+ return {
159
+ :certid => certid,
160
+ :status => validity_status.ocsp_status,
161
+ :revocation_reason => validity_status.revocation_reason,
162
+ :revocation_time => validity_status.revocation_time,
163
+ :config => validated_config
164
+ }
165
+ end
166
+ end
167
+ end
168
+
169
+ #signs OCSP responses
170
+ class ResponseSigner
171
+ # @option options [Boolean] :copy_nonce
172
+ def initialize(options)
173
+ if options.has_key?(:copy_nonce)
174
+ @copy_nonce = options[:copy_nonce]
175
+ else
176
+ @copy_nonce = false
177
+ end
178
+ end
179
179
 
180
- # It is UNWISE to call this method directly because it assumes that the request is
181
- # validated. You probably want to take a look at R509::Ocsp::Signer#handle_request
182
- #
183
- # @param request [OpenSSL::OCSP::Request]
184
- # @param statuses [Hash] hash from R509::Ocsp::Helper::RequestChecker#check_statuses
185
- # @return [OpenSSL::OCSP::BasicResponse]
186
- def create_basic_response(request,statuses)
187
- basic_response = OpenSSL::OCSP::BasicResponse.new
188
-
189
- basic_response.copy_nonce(request) if @copy_nonce
190
-
191
- statuses.each do |status|
192
- #revocation time is retarded and is relative to now, so
193
- #let's figure out what that is.
194
- if status[:status] == OpenSSL::OCSP::V_CERTSTATUS_REVOKED
195
- revocation_time = status[:revocation_time].to_i - Time.now.to_i
196
- end
197
- basic_response.add_status(status[:certid],
198
- status[:status],
199
- status[:revocation_reason],
200
- revocation_time,
201
- -1*status[:config].ocsp_start_skew_seconds,
202
- status[:config].ocsp_validity_hours*3600,
203
- [] #array of OpenSSL::X509::Extensions
204
- )
205
- end
206
-
207
- #this method assumes the request data is validated by validate_request so all configs will be the same and
208
- #we can choose to use the first one safely
209
- config = statuses[0][:config]
210
-
211
- #confusing, but R509::Cert contains R509::PrivateKey under #key. PrivateKey#key gives the OpenSSL object
212
- #turns out BasicResponse#sign can take up to 4 params
213
- #cert, key, array of OpenSSL::X509::Certificates, flags (not sure what the enumeration of those are)
214
- basic_response.sign(config.ocsp_cert.cert,config.ocsp_cert.key.key,config.ocsp_chain)
180
+ # It is UNWISE to call this method directly because it assumes that the request is
181
+ # validated. You probably want to take a look at R509::OCSP::Signer#handle_request
182
+ #
183
+ # @param request [OpenSSL::OCSP::Request]
184
+ # @param statuses [Hash] hash from R509::OCSP::Helper::RequestChecker#check_statuses
185
+ # @return [OpenSSL::OCSP::BasicResponse]
186
+ def create_basic_response(request,statuses)
187
+ basic_response = OpenSSL::OCSP::BasicResponse.new
188
+
189
+ basic_response.copy_nonce(request) if @copy_nonce
190
+
191
+ statuses.each do |status|
192
+ #revocation time is retarded and is relative to now, so
193
+ #let's figure out what that is.
194
+ if status[:status] == OpenSSL::OCSP::V_CERTSTATUS_REVOKED
195
+ revocation_time = status[:revocation_time].to_i - Time.now.to_i
215
196
  end
197
+ basic_response.add_status(status[:certid],
198
+ status[:status],
199
+ status[:revocation_reason],
200
+ revocation_time,
201
+ -1*status[:config].ocsp_start_skew_seconds,
202
+ status[:config].ocsp_validity_hours*3600,
203
+ [] #array of OpenSSL::X509::Extensions
204
+ )
205
+ end
206
+
207
+ #this method assumes the request data is validated by validate_request so all configs will be the same and
208
+ #we can choose to use the first one safely
209
+ config = statuses[0][:config]
210
+
211
+ #confusing, but R509::Cert contains R509::PrivateKey under #key. PrivateKey#key gives the OpenSSL object
212
+ #turns out BasicResponse#sign can take up to 4 params
213
+ #cert, key, array of OpenSSL::X509::Certificates, flags (not sure what the enumeration of those are)
214
+ basic_response.sign(config.ocsp_cert.cert,config.ocsp_cert.key.key,config.ocsp_chain)
215
+ end
216
216
 
217
- # Builds final response.
218
- #
219
- # @param response_status [OpenSSL::OCSP::RESPONSE_STATUS_*] the primary response status
220
- # @param basic_response [OpenSSL::OCSP::BasicResponse] an optional basic response object
221
- # generated by create_basic_response
222
- # @return [OpenSSL::OCSP::OCSPResponse]
223
- def create_response(response_status,basic_response=nil)
224
-
225
- # first arg is the response status code, comes from this list
226
- # these can also be enumerated via OpenSSL::OCSP::RESPONSE_STATUS_*
227
- #OCSPResponseStatus ::= ENUMERATED {
228
- # successful (0), --Response has valid confirmations
229
- # malformedRequest (1), --Illegal confirmation request
230
- # internalError (2), --Internal error in issuer
231
- # tryLater (3), --Try again later
232
- # --(4) is not used
233
- # sigRequired (5), --Must sign the request
234
- # unauthorized (6) --Request unauthorized
235
- #}
236
- #
237
- R509::Ocsp::Response.new(
238
- OpenSSL::OCSP::Response.create(
239
- response_status, basic_response
240
- )
241
- )
242
- end
217
+ # Builds final response.
218
+ #
219
+ # @param response_status [OpenSSL::OCSP::RESPONSE_STATUS_*] the primary response status
220
+ # @param basic_response [OpenSSL::OCSP::BasicResponse] an optional basic response object
221
+ # generated by create_basic_response
222
+ # @return [OpenSSL::OCSP::OCSPResponse]
223
+ def create_response(response_status,basic_response=nil)
224
+
225
+ # first arg is the response status code, comes from this list
226
+ # these can also be enumerated via OpenSSL::OCSP::RESPONSE_STATUS_*
227
+ #OCSPResponseStatus ::= ENUMERATED {
228
+ # successful (0), --Response has valid confirmations
229
+ # malformedRequest (1), --Illegal confirmation request
230
+ # internalError (2), --Internal error in issuer
231
+ # tryLater (3), --Try again later
232
+ # --(4) is not used
233
+ # sigRequired (5), --Must sign the request
234
+ # unauthorized (6) --Request unauthorized
235
+ #}
236
+ #
237
+ R509::OCSP::Response.new(
238
+ OpenSSL::OCSP::Response.create(
239
+ response_status, basic_response
240
+ )
241
+ )
243
242
  end
243
+ end
244
244
  end