r509-ocsp-responder 0.3.1
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.
- data/README.md +77 -0
- data/Rakefile +38 -0
- data/doc/R509.html +115 -0
- data/doc/R509/Ocsp.html +130 -0
- data/doc/R509/Ocsp/Helper.html +126 -0
- data/doc/R509/Ocsp/Helper/RequestChecker.html +739 -0
- data/doc/R509/Ocsp/Helper/ResponseSigner.html +583 -0
- data/doc/R509/Ocsp/Responder.html +129 -0
- data/doc/R509/Ocsp/Responder/OcspConfig.html +289 -0
- data/doc/R509/Ocsp/Responder/Server.html +128 -0
- data/doc/R509/Ocsp/Responder/StatusError.html +134 -0
- data/doc/R509/Ocsp/Signer.html +584 -0
- data/doc/_index.html +197 -0
- data/doc/class_list.html +53 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +328 -0
- data/doc/file.README.html +156 -0
- data/doc/file_list.html +55 -0
- data/doc/frames.html +28 -0
- data/doc/index.html +156 -0
- data/doc/js/app.js +214 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +164 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/r509/ocsp/responder/ocsp-config.rb +35 -0
- data/lib/r509/ocsp/responder/server.rb +169 -0
- data/lib/r509/ocsp/responder/version.rb +7 -0
- data/lib/r509/ocsp/signer.rb +244 -0
- data/spec/fixtures.rb +196 -0
- data/spec/fixtures/cert1.pem +24 -0
- data/spec/fixtures/config_test_various.yaml +46 -0
- data/spec/fixtures/ocsptest.r509.local.pem +27 -0
- data/spec/fixtures/second_ca.cer +26 -0
- data/spec/fixtures/second_ca.key +27 -0
- data/spec/fixtures/stca.pem +22 -0
- data/spec/fixtures/stca_ocsp_request.der +0 -0
- data/spec/fixtures/stca_ocsp_response.der +0 -0
- data/spec/fixtures/test_ca.cer +22 -0
- data/spec/fixtures/test_ca.key +28 -0
- data/spec/fixtures/test_ca_ocsp.cer +26 -0
- data/spec/fixtures/test_ca_ocsp.key +27 -0
- data/spec/fixtures/test_ca_ocsp_chain.txt +48 -0
- data/spec/fixtures/test_ca_request.der +0 -0
- data/spec/fixtures/test_ca_response.der +0 -0
- data/spec/fixtures/test_ca_subroot.cer +25 -0
- data/spec/fixtures/test_ca_subroot.key +27 -0
- data/spec/fixtures/test_ca_subroot_ocsp.cer +25 -0
- data/spec/fixtures/test_ca_subroot_ocsp.key +27 -0
- data/spec/fixtures/test_config.yaml +17 -0
- data/spec/server_spec.rb +400 -0
- data/spec/signer_spec.rb +275 -0
- data/spec/spec_helper.rb +18 -0
- metadata +259 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module R509::Ocsp::Responder
|
2
|
+
class OcspConfig
|
3
|
+
def self.load_config
|
4
|
+
config_data = File.read("config.yaml")
|
5
|
+
|
6
|
+
Dependo::Registry[:config_pool] = R509::Config::CaConfigPool.from_yaml("certificate_authorities", config_data)
|
7
|
+
|
8
|
+
Dependo::Registry[:copy_nonce] = YAML.load(config_data)["copy_nonce"] || false
|
9
|
+
|
10
|
+
Dependo::Registry[:cache_headers] = YAML.load(config_data)["cache_headers"] || false
|
11
|
+
|
12
|
+
Dependo::Registry[:max_cache_age] = YAML.load(config_data)["max_cache_age"]
|
13
|
+
|
14
|
+
Dependo::Registry[:ocsp_signer] = R509::Ocsp::Signer.new(
|
15
|
+
:configs => Dependo::Registry[:config_pool],
|
16
|
+
:validity_checker => R509::Validity::Redis::Checker.new(Dependo::Registry[:redis]),
|
17
|
+
:copy_nonce => Dependo::Registry[:copy_nonce]
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.print_config
|
22
|
+
Dependo::Registry[:log].warn "Config loaded"
|
23
|
+
Dependo::Registry[:log].warn "Copy Nonce: "+Dependo::Registry[:copy_nonce].to_s
|
24
|
+
Dependo::Registry[:log].warn "Cache Headers: "+Dependo::Registry[:cache_headers].to_s
|
25
|
+
Dependo::Registry[:log].warn "Max Cache Age: "+Dependo::Registry[:max_cache_age].to_s
|
26
|
+
Dependo::Registry[:config_pool].all.each do |config|
|
27
|
+
Dependo::Registry[:log].warn "Config: "
|
28
|
+
Dependo::Registry[:log].warn "CA Cert:"+config.ca_cert.subject.to_s
|
29
|
+
Dependo::Registry[:log].warn "OCSP Cert (may be the same as above):"+config.ocsp_cert.subject.to_s
|
30
|
+
Dependo::Registry[:log].warn "OCSP Validity Hours: "+config.ocsp_validity_hours.to_s
|
31
|
+
Dependo::Registry[:log].warn "\n"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'rubygems' if RUBY_VERSION < "1.9"
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'r509'
|
4
|
+
require 'r509/ocsp/signer'
|
5
|
+
require 'r509/validity/redis'
|
6
|
+
require 'base64'
|
7
|
+
require 'dependo'
|
8
|
+
require 'logger'
|
9
|
+
require 'time'
|
10
|
+
require File.dirname(__FILE__)+'/ocsp-config.rb'
|
11
|
+
|
12
|
+
# Capture USR2 calls so we can reload and print the config
|
13
|
+
# I'd rather use HUP, but daemons like thin already capture that
|
14
|
+
# so we can't use it.
|
15
|
+
Signal.trap("USR2") do
|
16
|
+
R509::Ocsp::Responder::OcspConfig.load_config
|
17
|
+
R509::Ocsp::Responder::OcspConfig.print_config
|
18
|
+
end
|
19
|
+
|
20
|
+
|
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
|
28
|
+
|
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
|
35
|
+
|
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
|
41
|
+
|
42
|
+
error OpenSSL::OCSP::OCSPError do
|
43
|
+
"Invalid request"
|
44
|
+
end
|
45
|
+
|
46
|
+
error R509::Ocsp::Responder::StatusError do
|
47
|
+
"Down"
|
48
|
+
end
|
49
|
+
|
50
|
+
get '/favicon.ico' do
|
51
|
+
log.debug "go away. no children."
|
52
|
+
"go away. no children"
|
53
|
+
end
|
54
|
+
|
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
|
66
|
+
|
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
|
76
|
+
end
|
77
|
+
|
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
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def handle_ocsp_request(der, method)
|
90
|
+
begin
|
91
|
+
request_response = ocsp_signer.handle_request(der)
|
92
|
+
|
93
|
+
log_ocsp_response(request_response[:response],method)
|
94
|
+
|
95
|
+
content_type :ocsp
|
96
|
+
request_response
|
97
|
+
rescue StandardError => e
|
98
|
+
log.error "unexpected error #{e}"
|
99
|
+
raise e
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def log_ocsp_response(ocsp_response, method="?")
|
104
|
+
if response.nil?
|
105
|
+
log.error "Something went horribly wrong"
|
106
|
+
return
|
107
|
+
end
|
108
|
+
|
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"]}" }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
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
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'r509/exceptions'
|
3
|
+
require 'r509/config'
|
4
|
+
require 'dependo'
|
5
|
+
|
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
|
+
|
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}
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
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
|
91
|
+
end
|
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
|
+
}
|
118
|
+
end
|
119
|
+
|
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
|
144
|
+
|
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
|
+
|
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)
|
215
|
+
end
|
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
|
243
|
+
end
|
244
|
+
end
|