r509-ocsp-responder 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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