ocsprf 0.0.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.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +29 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +24 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +74 -0
- data/Rakefile +10 -0
- data/exe/ocsprf +8 -0
- data/lib/ocsp_response_fetch/cli.rb +158 -0
- data/lib/ocsp_response_fetch/error.rb +11 -0
- data/lib/ocsp_response_fetch/fetcher.rb +115 -0
- data/lib/ocsp_response_fetch/utils.rb +58 -0
- data/lib/ocsp_response_fetch/version.rb +5 -0
- data/lib/ocsp_response_fetch.rb +12 -0
- data/ocsprf.gemspec +26 -0
- data/spec/cli_spec.rb +37 -0
- data/spec/fetcher_spec.rb +77 -0
- data/spec/fixtures/rsa_ca.crt +19 -0
- data/spec/fixtures/rsa_ca.key +27 -0
- data/spec/fixtures/rsa_rsassaPss.crt +22 -0
- data/spec/fixtures/rsa_rsassaPss.key +27 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/utils_spec.rb +97 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b574da141094b8dbcbdc3f6a5d50d7c9a378f17432e9391e857b83e5e522f320
|
4
|
+
data.tar.gz: e5b3ee60a8164a1788afc2c74120a7e256445d8eeb84df9c718a18f21fa047e6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e722fab4df04282da7f3a72aa6e19dccb5b32fcbb54fc42aaa1d7176aee31e6090fdbdfd9aaddf3169f5dc9e95b29cddeee73fdde5bc4ad93b7ebfedaa6ce8d8
|
7
|
+
data.tar.gz: 3a8e2c864d036f5b83ed0a4443947cf117bf673c007e7385911e6da0ed15b96f60d3e1ea6ed945ba46679ef6ad09874d80cb351bbb46907d3102b2c97ee3db09
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- '*'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
ci:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
ruby-version: ['2.6.x', '2.7.x']
|
17
|
+
steps:
|
18
|
+
- name: Set up Ruby
|
19
|
+
uses: actions/setup-ruby@v1
|
20
|
+
- uses: actions/checkout@v1
|
21
|
+
- name: Install dependencies
|
22
|
+
run: |
|
23
|
+
gem --version
|
24
|
+
gem install bundler
|
25
|
+
bundle --version
|
26
|
+
bundle install
|
27
|
+
- name: Run test
|
28
|
+
run: |
|
29
|
+
bundle exec rake
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
|
4
|
+
Style/ConditionalAssignment:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Style/Documentation:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/NumericLiterals:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Metrics/AbcSize:
|
14
|
+
Max: 30
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Max: 30
|
18
|
+
|
19
|
+
Naming/MethodParameterName:
|
20
|
+
MinNameLength: 1
|
21
|
+
|
22
|
+
Metrics/BlockLength:
|
23
|
+
Exclude:
|
24
|
+
- 'spec/*.rb'
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Tomoya Kuwayama
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# ocsprf
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/ocsprf)
|
4
|
+
[](https://github.com/thekuwayama/ocsprf/actions?workflow=CI)
|
5
|
+
[](https://codeclimate.com/github/thekuwayama/ocsprf/maintainability)
|
6
|
+
|
7
|
+
OCSP Response Fetch
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
The gem is available at [rubygems.org](https://rubygems.org/gems/ocsprf). You can install it the following.
|
13
|
+
|
14
|
+
```bash
|
15
|
+
$ gem install ocsprf
|
16
|
+
```
|
17
|
+
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ ocsprf --help
|
23
|
+
Usage: ocsprf [options] PATH
|
24
|
+
-i, --issuer PATH issuer certificate path
|
25
|
+
-s, --strict strict mode (default false)
|
26
|
+
-v, --verbose verbose mode (default false)
|
27
|
+
```
|
28
|
+
|
29
|
+
You can run it the following and print the DER-encoded OCSP Response that fetched.
|
30
|
+
|
31
|
+
```bash
|
32
|
+
$ ocsprf /path/to/subject/certificate
|
33
|
+
$DER_BINARY
|
34
|
+
```
|
35
|
+
|
36
|
+
If you need to print OCSP Response text, you can run it the following.
|
37
|
+
|
38
|
+
```bash
|
39
|
+
$ ocsprf /path/to/subject/certificate --verbose > /dev/null
|
40
|
+
OCSP Response Data:
|
41
|
+
OCSP Response Status: (0x0)
|
42
|
+
Responses:
|
43
|
+
Certificate ID:
|
44
|
+
Hash Algorithm: sha1
|
45
|
+
Issuer Name Hash: 0123456789ABCDEF0123456789ABCDEF01234567
|
46
|
+
Issuer Key Hash: 0123456789ABCDEF0123456789ABCDEF01234567
|
47
|
+
Serial Number: 0123456789ABCDEF0123456789ABCDEF01234567
|
48
|
+
Cert Status: good
|
49
|
+
This Update: 2020-01-01 12:00:00 UTC
|
50
|
+
Next Update: 2020-01-08 12:00:00 UTC
|
51
|
+
```
|
52
|
+
|
53
|
+
If you have the issuer certificate corresponding to the subject certificate, you can pass it using `--issuer` option.
|
54
|
+
By default, `ocsprf` tries to get the issuer certificate using AIA extension.
|
55
|
+
|
56
|
+
```bash
|
57
|
+
$ ocsprf /path/to/subject/certificate --issuer /path/to/issuer/certificate --verbose > /dev/null
|
58
|
+
OCSP Response Data:
|
59
|
+
OCSP Response Status: (0x0)
|
60
|
+
Responses:
|
61
|
+
Certificate ID:
|
62
|
+
Hash Algorithm: sha1
|
63
|
+
Issuer Name Hash: 0123456789ABCDEF0123456789ABCDEF01234567
|
64
|
+
Issuer Key Hash: 0123456789ABCDEF0123456789ABCDEF01234567
|
65
|
+
Serial Number: 0123456789ABCDEF0123456789ABCDEF01234567
|
66
|
+
Cert Status: good
|
67
|
+
This Update: 2020-01-01 12:00:00 UTC
|
68
|
+
Next Update: 2020-01-08 12:00:00 UTC
|
69
|
+
```
|
70
|
+
|
71
|
+
|
72
|
+
## License
|
73
|
+
|
74
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/ocsprf
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
using OCSPResponseFetch::Refinements
|
4
|
+
|
5
|
+
module OCSPResponseFetch
|
6
|
+
# rubocop: disable Metrics/ClassLength
|
7
|
+
class CLI
|
8
|
+
class << self
|
9
|
+
def run
|
10
|
+
subject, opts = parse_options
|
11
|
+
issuer = opts[:issuer]
|
12
|
+
subject_cert, issuer_cert = read_certs(subject, issuer)
|
13
|
+
|
14
|
+
fetcher = Fetcher.new(subject_cert, issuer_cert)
|
15
|
+
begin
|
16
|
+
ocsp_response = fetcher.run
|
17
|
+
rescue OCSPResponseFetch::Error::RevokedError
|
18
|
+
warn 'error: end entity certificate is revoked'
|
19
|
+
exit 1
|
20
|
+
rescue OCSPResponseFetch::Error::Error => e
|
21
|
+
warn e
|
22
|
+
exit 1 if opts[:strict]
|
23
|
+
exit 0
|
24
|
+
end
|
25
|
+
|
26
|
+
warn ocsp_response.to_text if opts[:verbose]
|
27
|
+
puts ocsp_response.to_der
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# rubocop: disable Metrics/AbcSize
|
33
|
+
# rubocop: disable Metrics/MethodLength
|
34
|
+
def parse_options(argv = ARGV)
|
35
|
+
op = OptionParser.new
|
36
|
+
|
37
|
+
# default value
|
38
|
+
opts = {
|
39
|
+
issuer: nil,
|
40
|
+
strict: false,
|
41
|
+
verbose: false
|
42
|
+
}
|
43
|
+
|
44
|
+
op.on(
|
45
|
+
'-i PATH',
|
46
|
+
'--issuer PATH',
|
47
|
+
'issuer certificate path'
|
48
|
+
) do |v|
|
49
|
+
opts[:issuer] = v
|
50
|
+
end
|
51
|
+
|
52
|
+
op.on(
|
53
|
+
'-s',
|
54
|
+
'--strict',
|
55
|
+
"strict mode (default #{opts[:strict]})"
|
56
|
+
) do |v|
|
57
|
+
opts[:strict] = v
|
58
|
+
end
|
59
|
+
|
60
|
+
op.on(
|
61
|
+
'-v',
|
62
|
+
'--verbose',
|
63
|
+
"verbose mode (default #{opts[:verbose]})"
|
64
|
+
) do |v|
|
65
|
+
opts[:verbose] = v
|
66
|
+
end
|
67
|
+
|
68
|
+
op.banner += ' PATH'
|
69
|
+
begin
|
70
|
+
args = op.parse(argv)
|
71
|
+
rescue OptionParser::InvalidOption => e
|
72
|
+
warn op.to_s
|
73
|
+
warn "error: #{e.message}"
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
|
77
|
+
if args.size != 1
|
78
|
+
warn op.to_s
|
79
|
+
warn 'error: number of arguments is not 1'
|
80
|
+
exit 1
|
81
|
+
end
|
82
|
+
|
83
|
+
unless File.exist?(args.first)
|
84
|
+
warn "error: file #{args.first} is not found"
|
85
|
+
exit 1
|
86
|
+
end
|
87
|
+
|
88
|
+
if !opts[:issuer].nil? && !File.exist?(opts[:issuer])
|
89
|
+
warn "error: file #{opts[:issuer]} is not found"
|
90
|
+
exit 1
|
91
|
+
end
|
92
|
+
|
93
|
+
[args[0], opts]
|
94
|
+
end
|
95
|
+
# rubocop: enable Metrics/AbcSize
|
96
|
+
# rubocop: enable Metrics/MethodLength
|
97
|
+
|
98
|
+
# @param subject [String]
|
99
|
+
# @param issuer [String]
|
100
|
+
#
|
101
|
+
# @raise [OCSPResponseFetch::Error::Error]
|
102
|
+
#
|
103
|
+
# @return [Array of OpenSSL::X509::Certificate]
|
104
|
+
def read_certs(subject, issuer = nil)
|
105
|
+
subject_cert = OpenSSL::X509::Certificate.new(File.read(subject))
|
106
|
+
issuer_cert = nil
|
107
|
+
if issuer.nil? || issuer.empty?
|
108
|
+
ca_issuer = subject_cert.ca_issuer_uris
|
109
|
+
&.find { |u| URI::DEFAULT_PARSER.make_regexp =~ u }
|
110
|
+
if ca_issuer.nil?
|
111
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
112
|
+
'The subject Certificate does not contain Issuer URL'
|
113
|
+
end
|
114
|
+
|
115
|
+
begin
|
116
|
+
issuer_cert = get_issuer_cert(ca_issuer)
|
117
|
+
rescue OpenSSL::X509::CertificateError, Net::OpenTimeout
|
118
|
+
raise OCSPResponseFetch::Error::FetchFailedEreror,
|
119
|
+
'Failed to get the issuser Certificate'
|
120
|
+
end
|
121
|
+
else
|
122
|
+
begin
|
123
|
+
issuer_cert = OpenSSL::X509::Certificate.new(File.read(issuer))
|
124
|
+
rescue OpenSSL::X509::CertificateError
|
125
|
+
raise OCSPResponseFetch::Error::FetchFailedEreror,
|
126
|
+
'Failed to get the issuser Certificate'
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
[subject_cert, issuer_cert]
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param uri_string [String]
|
134
|
+
#
|
135
|
+
# @return [OpenSSL::X509::Certificate]
|
136
|
+
def get_issuer_cert(uri_string)
|
137
|
+
OpenSSL::X509::Certificate.new(
|
138
|
+
send_http_get(uri_string)
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
# @param uri_string [String]
|
143
|
+
#
|
144
|
+
# @return [String]
|
145
|
+
def send_http_get(uri_string)
|
146
|
+
uri = URI.parse(uri_string)
|
147
|
+
path = uri.path
|
148
|
+
path = '/' if path.nil? || path.empty?
|
149
|
+
http_response = Net::HTTP.start(uri.host, uri.port) do |http|
|
150
|
+
http.get(path)
|
151
|
+
end
|
152
|
+
|
153
|
+
http_response.body
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
# rubocop: enable Metrics/ClassLength
|
158
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
using OCSPResponseFetch::Refinements
|
4
|
+
|
5
|
+
module OCSPResponseFetch
|
6
|
+
class Fetcher
|
7
|
+
# @param subject_cert [OpenSSL::X509::Certificate]
|
8
|
+
# @param issuer_cert [OpenSSL::X509::Certificate]
|
9
|
+
#
|
10
|
+
# @raise [OCSPResponseFetch::Error::Error]
|
11
|
+
def initialize(subject_cert, issuer_cert)
|
12
|
+
@certs_chain = [subject_cert, issuer_cert]
|
13
|
+
@cid = OpenSSL::OCSP::CertificateId.new(subject_cert, issuer_cert)
|
14
|
+
@ocsp_uri = subject_cert.ocsp_uris
|
15
|
+
&.find { |u| URI::DEFAULT_PARSER.make_regexp =~ u }
|
16
|
+
return unless @ocsp_uri.nil?
|
17
|
+
|
18
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
19
|
+
'Certificate does not contain OCSP URL'
|
20
|
+
end
|
21
|
+
|
22
|
+
# @raise [OCSPResponseFetch::Error::Error]
|
23
|
+
#
|
24
|
+
# @return [OpenSSL::OCSP::Response]
|
25
|
+
def run
|
26
|
+
Fetcher.request_and_validate(@cid, @ocsp_uri, @certs_chain)
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
# @param cid [OpenSSL::OCSP::CertificateId]
|
31
|
+
# @param ocsp_uri [String]
|
32
|
+
# @param certs [Array of OpenSSL::X509::Certificate]
|
33
|
+
#
|
34
|
+
# @raise [OCSPResponseFetch::Error::Error]
|
35
|
+
#
|
36
|
+
# @return [OpenSSL::OCSP::Response]
|
37
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
38
|
+
# rubocop: disable Metrics/MethodLength
|
39
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
40
|
+
def request_and_validate(cid, ocsp_uri, certs)
|
41
|
+
ocsp_request = gen_ocsp_request(cid)
|
42
|
+
ocsp_response = nil
|
43
|
+
begin
|
44
|
+
Timeout.timeout(2) do
|
45
|
+
ocsp_response = send_ocsp_request(ocsp_request, ocsp_uri)
|
46
|
+
end
|
47
|
+
rescue Timeout::Error
|
48
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
49
|
+
'Timeout to access OCSP Responder'
|
50
|
+
end
|
51
|
+
|
52
|
+
if ocsp_response&.status != OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
|
53
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
54
|
+
'OCSPResponseStatus is not successful'
|
55
|
+
end
|
56
|
+
|
57
|
+
check_nonce = ocsp_request.check_nonce(ocsp_response.basic)
|
58
|
+
unless [-1, 1].include?(check_nonce)
|
59
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
60
|
+
'OCSPResponse nonce is invalid'
|
61
|
+
end
|
62
|
+
|
63
|
+
store = OpenSSL::X509::Store.new
|
64
|
+
store.set_default_paths
|
65
|
+
unless ocsp_response.basic.verify(certs, store)
|
66
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
67
|
+
'OCSPResponse signature is invalid'
|
68
|
+
end
|
69
|
+
|
70
|
+
status = ocsp_response.basic.find_response(cid)
|
71
|
+
if status.cert_status == OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
|
72
|
+
raise OCSPResponseFetch::Error::FetchFailedError,
|
73
|
+
'OCSPResponse CertStatus is unknown'
|
74
|
+
elsif status.cert_status == OpenSSL::OCSP::V_CERTSTATUS_REVOKED
|
75
|
+
raise OCSPResponseFetch::Error::RevokedError,
|
76
|
+
'OCSPResponse CertStatus is revoked'
|
77
|
+
end
|
78
|
+
|
79
|
+
ocsp_response
|
80
|
+
end
|
81
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
82
|
+
# rubocop: enable Metrics/MethodLength
|
83
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
84
|
+
|
85
|
+
# @param cid [OpenSSL::OCSP::CertificateId]
|
86
|
+
#
|
87
|
+
# @return [OpenSSL::OCSP::Request]
|
88
|
+
def gen_ocsp_request(cid)
|
89
|
+
ocsp_request = OpenSSL::OCSP::Request.new
|
90
|
+
ocsp_request.add_certid(cid)
|
91
|
+
ocsp_request.add_nonce
|
92
|
+
ocsp_request
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param ocsp_request [OpenSSL::OCSP::Request]
|
96
|
+
# @param uri_string [String]
|
97
|
+
#
|
98
|
+
# @return [OpenSSL::OCSP::Response]
|
99
|
+
def send_ocsp_request(ocsp_request, uri_string)
|
100
|
+
uri = URI.parse(uri_string)
|
101
|
+
path = uri.path
|
102
|
+
path = '/' if path.nil? || path.empty?
|
103
|
+
http_response = Net::HTTP.start(uri.host, uri.port) do |http|
|
104
|
+
http.post(
|
105
|
+
path,
|
106
|
+
ocsp_request.to_der,
|
107
|
+
'content-type' => 'application/ocsp-request'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
OpenSSL::OCSP::Response.new(http_response.body)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OCSPResponseFetch
|
4
|
+
module Refinements
|
5
|
+
refine OpenSSL::X509::Certificate do
|
6
|
+
unless method_defined?(:ocsp_uris)
|
7
|
+
define_method(:ocsp_uris) do
|
8
|
+
aia = extensions.find { |ex| ex.oid == 'authorityInfoAccess' }
|
9
|
+
return nil if aia.nil?
|
10
|
+
|
11
|
+
ostr = OpenSSL::ASN1.decode(aia.to_der).value.last
|
12
|
+
ocsp = OpenSSL::ASN1.decode(ostr.value)
|
13
|
+
.map(&:value)
|
14
|
+
.select { |des| des.first.value == 'OCSP' }
|
15
|
+
ocsp&.map { |o| o[1].value }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
refine OpenSSL::X509::Certificate do
|
21
|
+
unless method_defined?(:ca_issuer_uris)
|
22
|
+
define_method(:ca_issuer_uris) do
|
23
|
+
aia = extensions.find { |ex| ex.oid == 'authorityInfoAccess' }
|
24
|
+
return nil if aia.nil?
|
25
|
+
|
26
|
+
ostr = OpenSSL::ASN1.decode(aia.to_der).value.last
|
27
|
+
ocsp = OpenSSL::ASN1.decode(ostr.value)
|
28
|
+
.map(&:value)
|
29
|
+
.select { |des| des.first.value == 'caIssuers' }
|
30
|
+
ocsp&.map { |o| o[1].value }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
refine OpenSSL::OCSP::Response do
|
36
|
+
# @return [String]
|
37
|
+
def to_text
|
38
|
+
cert_status = %w[good revoked unknown]
|
39
|
+
|
40
|
+
basic.responses.map do |res|
|
41
|
+
<<~"OCSP_RESPONSE"
|
42
|
+
OCSP Response Data:
|
43
|
+
OCSP Response Status: (#{format('0x%<status>x', status: status)})
|
44
|
+
Responses:
|
45
|
+
Certificate ID:
|
46
|
+
Hash Algorithm: #{res.certid.hash_algorithm}
|
47
|
+
Issuer Name Hash: #{res.certid.issuer_name_hash.upcase}
|
48
|
+
Issuer Key Hash: #{res.certid.issuer_key_hash.upcase}
|
49
|
+
Serial Number: #{res.certid.serial.to_s(16)}
|
50
|
+
Cert Status: #{cert_status[res.cert_status]}
|
51
|
+
This Update: #{res.this_update}
|
52
|
+
Next Update: #{res.next_update}
|
53
|
+
OCSP_RESPONSE
|
54
|
+
end.join
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'openssl'
|
5
|
+
require 'optparse'
|
6
|
+
require 'timeout'
|
7
|
+
|
8
|
+
require 'ocsp_response_fetch/version'
|
9
|
+
require 'ocsp_response_fetch/error'
|
10
|
+
require 'ocsp_response_fetch/utils'
|
11
|
+
require 'ocsp_response_fetch/fetcher'
|
12
|
+
require 'ocsp_response_fetch/cli'
|
data/ocsprf.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'ocsp_response_fetch/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'ocsprf'
|
9
|
+
spec.version = OCSPResponseFetch::VERSION
|
10
|
+
spec.authors = ['thekuwayama']
|
11
|
+
spec.email = ['thekuwayama@gmail.com']
|
12
|
+
spec.summary = 'OCSP Response Fetch'
|
13
|
+
spec.description = spec.summary
|
14
|
+
spec.homepage = 'https://github.com/thekuwayama/ocsprf'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
spec.required_ruby_version = '>=2.6.0'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = ['ocsprf']
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler'
|
25
|
+
spec.add_dependency 'openssl'
|
26
|
+
end
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe OCSPResponseFetch::CLI do
|
6
|
+
let(:subject) do
|
7
|
+
__dir__ + '/fixtures/rsa_rsassaPss.crt'
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:issuer) do
|
11
|
+
__dir__ + '/fixtures/rsa_ca.crt'
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'read_certs' do
|
15
|
+
it 'should return subject_cert and issuer_cert' do
|
16
|
+
expect(OCSPResponseFetch::CLI.send(:read_certs, subject, issuer))
|
17
|
+
.to eq [
|
18
|
+
OpenSSL::X509::Certificate.new(File.read(subject)),
|
19
|
+
OpenSSL::X509::Certificate.new(File.read(issuer))
|
20
|
+
]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'read_certs (no issuer)' do
|
25
|
+
it 'should return subject_cert and issuer_cert' do
|
26
|
+
allow(OCSPResponseFetch::CLI).to receive(:send_http_get).and_return(
|
27
|
+
OpenSSL::X509::Certificate.new(File.read(issuer)).to_der
|
28
|
+
)
|
29
|
+
|
30
|
+
expect(OCSPResponseFetch::CLI.send(:read_certs, subject))
|
31
|
+
.to eq [
|
32
|
+
OpenSSL::X509::Certificate.new(File.read(subject)),
|
33
|
+
OpenSSL::X509::Certificate.new(File.read(issuer))
|
34
|
+
]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe OCSPResponseFetch::Fetcher do
|
6
|
+
let(:subject_cert) do
|
7
|
+
OpenSSL::X509::Certificate.new(
|
8
|
+
File.read(__dir__ + '/fixtures/rsa_rsassaPss.crt')
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:issuer_cert) do
|
13
|
+
OpenSSL::X509::Certificate.new(
|
14
|
+
File.read(__dir__ + '/fixtures/rsa_ca.crt')
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:cid) do
|
19
|
+
OpenSSL::OCSP::CertificateId.new(subject_cert, issuer_cert)
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:nonce) do
|
23
|
+
'nonce'
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:ocsp_request) do
|
27
|
+
ocsp_request = OpenSSL::OCSP::Request.new
|
28
|
+
ocsp_request.add_certid(cid)
|
29
|
+
ocsp_request.add_nonce(nonce)
|
30
|
+
ocsp_request
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:ocsp_response) do
|
34
|
+
bres = OpenSSL::OCSP::BasicResponse.new
|
35
|
+
bres.add_status(
|
36
|
+
cid,
|
37
|
+
OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL,
|
38
|
+
OpenSSL::OCSP::V_CERTSTATUS_GOOD,
|
39
|
+
Time.now + (60 * 60 * 24 * 365 * 10),
|
40
|
+
Time.now,
|
41
|
+
Time.now + (60 * 60 * 24 * 7),
|
42
|
+
[]
|
43
|
+
)
|
44
|
+
bres.add_nonce(nonce)
|
45
|
+
bres.sign(
|
46
|
+
issuer_cert,
|
47
|
+
OpenSSL::PKey.read(
|
48
|
+
File.read(__dir__ + '/fixtures/rsa_ca.key')
|
49
|
+
)
|
50
|
+
)
|
51
|
+
|
52
|
+
OpenSSL::OCSP::Response.create(
|
53
|
+
OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL,
|
54
|
+
bres
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
before do
|
59
|
+
ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] = __dir__ + '/fixtures/rsa_ca.crt'
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'request_and_validate' do
|
63
|
+
it 'should return a valid OCSP Response' do
|
64
|
+
allow(OCSPResponseFetch::Fetcher).to receive(:new)
|
65
|
+
allow(OCSPResponseFetch::Fetcher).to receive(:gen_ocsp_request)
|
66
|
+
.and_return(ocsp_request)
|
67
|
+
allow(OCSPResponseFetch::Fetcher).to receive(:send_ocsp_request)
|
68
|
+
.and_return(ocsp_response)
|
69
|
+
|
70
|
+
expect(OCSPResponseFetch::Fetcher.request_and_validate(
|
71
|
+
cid,
|
72
|
+
'http://localhost/ocsp',
|
73
|
+
[subject_cert, issuer_cert]
|
74
|
+
)).to eq ocsp_response
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDBTCCAe2gAwIBAgIUKd5CmRpCLwNfg6b0Ws4Ed7DLE7IwDQYJKoZIhvcNAQEL
|
3
|
+
BQAwEjEQMA4GA1UEAwwHdGVzdC1jYTAeFw0yMDAxMTcxODQ5NDZaFw0zMDAxMTQx
|
4
|
+
ODQ5NDZaMBIxEDAOBgNVBAMMB3Rlc3QtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
5
|
+
DwAwggEKAoIBAQDkLEwerUWX9qt43e74ZYYMoS5TJCtItdymFwJU6WeTVW+zfRP5
|
6
|
+
KHlPQ0ZBhqhSMNGgw/KndqQKmQYOX0Bkbi6txe5ubWy5G1hgCLj54gGneLEMAYLN
|
7
|
+
wjkUA0bF8WEHL6QjuUZR/6b05RVDwPRKDJjkhz+LfL2UeiX+ja3bOZlrV6bbzTZ5
|
8
|
+
rdgr5plMt5OLuA5BdBpm+M6ND+PVjkniUYPXp1r9TkRBgYoveH1IXjxe4hYK4Z64
|
9
|
+
NDLrORvq3U+RA/FM/GIXtOeTo/8YogGUF9LTDla8cjG9uR1T/shJDPybVhVWYp50
|
10
|
+
/TsS2VTTo61L6OohADq/0AVGPwch/CSYiPinAgMBAAGjUzBRMB0GA1UdDgQWBBRn
|
11
|
+
WhmG70QfJDGMSzyMD6pOuTpb/zAfBgNVHSMEGDAWgBRnWhmG70QfJDGMSzyMD6pO
|
12
|
+
uTpb/zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDR/X6urm5R
|
13
|
+
Ob18uXXStRaCTYqjcF0OfCknnuXF2gwDRgJ/vk4ODvmLy+ErqMicHVZezAIvXjQC
|
14
|
+
1Tmws6At0TRtezGV8Hxfkry1QEKTq6x0tIMizFAFcCgNwfTukvorsqkUHCTzXwbS
|
15
|
+
1pXz8zBJKMuONMyqzUR70MgVerKBCG8DTG4t7X5Jrla33zyfNgEKY3uK82IwFLoN
|
16
|
+
PivqkBUsk4oqTrOklVu6rXOLIDX+Q4NXx5ouqt+o2q4rOGk5qWK1olgv5c49Mfj5
|
17
|
+
z5fzjiLJbO0GBrOd8idqXs4W2IZGHJ+kM5qMxl/IBOL7tVv+ptTA3fSp1cGj1E37
|
18
|
+
TDewsLQ/BeTA
|
19
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpAIBAAKCAQEA5CxMHq1Fl/areN3u+GWGDKEuUyQrSLXcphcCVOlnk1Vvs30T
|
3
|
+
+Sh5T0NGQYaoUjDRoMPyp3akCpkGDl9AZG4urcXubm1suRtYYAi4+eIBp3ixDAGC
|
4
|
+
zcI5FANGxfFhBy+kI7lGUf+m9OUVQ8D0SgyY5Ic/i3y9lHol/o2t2zmZa1em2802
|
5
|
+
ea3YK+aZTLeTi7gOQXQaZvjOjQ/j1Y5J4lGD16da/U5EQYGKL3h9SF48XuIWCuGe
|
6
|
+
uDQy6zkb6t1PkQPxTPxiF7Tnk6P/GKIBlBfS0w5WvHIxvbkdU/7ISQz8m1YVVmKe
|
7
|
+
dP07EtlU06OtS+jqIQA6v9AFRj8HIfwkmIj4pwIDAQABAoIBAHcDxBCcQJirSXWa
|
8
|
+
DXPzQKCF0iv7ybf7ZEQd7FFuDWCER+dPboOf4Oa/KH41RsbYP/+jNYa0E4yTlN0a
|
9
|
+
QfQgpKhvfZaL7RIAeXBeHF36zPIVugORJjE9BZiyvM+yv/GHoA4iLdPHjcZfV2An
|
10
|
+
3URinb1V7odLYXd43yiPrgeTW7ro2RWvkaSh4sUboD8hfbvdgIMRaaSNjua8Ojg6
|
11
|
+
suxiGVeMq6hGnJbRN0v6QZhmCcAgRhOnGxqS+yhQQ4SB+zM+mhoGt1eSQ3KKdQpu
|
12
|
+
awozclfTjsACrg2VMTg2qILM5PXMoGD87C9FAHVtWB7h/kyer7fAgJ0AG4CR9XT0
|
13
|
+
WcDtMUECgYEA+Yg1FrrIThdd5BBzy0Abh/f1LGJmplcnPKptKiNwZ63cGbrdXcm2
|
14
|
+
Qq9kWMm6BDCQ3h/ctDUPDFFBoTvj7mnTumltLguijFXG5m9Zced/NgZhAqrJLFA0
|
15
|
+
YYL8gj/jtf2fv4ELwG+J7xBC2UDpSexaXE0ph3kIkj4+l4S4wp8dFdsCgYEA6hZc
|
16
|
+
N8xo/R3zuptUpBGnN0KXdUCbmN8Dqm4iDxjjWACHtgyQYIarHwV9L1GXUo44P7yr
|
17
|
+
TGT220S/X7ASPqpCoIHl8b0Jd4sqJSVkra9dBuCkpuY2yQkVTtuN3GbSWL+6155c
|
18
|
+
dc32vmwMOfBabobw5T/bT4sSg58dzqQzCWLfcCUCgYEAqeESC4Rj485CrIaK3p+p
|
19
|
+
XprvviTLr4j7/HAmW56+4sbZZmbyV+yAecvAbDYBvuJ1bdORRNougXs8TBFi7qf7
|
20
|
+
bhhiLQvcwN+QxsadxU8OKvCS0OcGiHWeA5jSYbYvR3IAdLbdEkidRjTy3cc0S5Bu
|
21
|
+
QBKEKGe87xsTL8I6qrz6ZysCgYBS5hJIbGpt4TKUTjf+VoYaSsnqazPvQmNA8vm6
|
22
|
+
0PyKCL4G2ZIzrN1jAgWgQUvOn7EJ2V3skwDdDA6d63O6n38y6m2tW13D6RPGzeRG
|
23
|
+
ogjWy61jsbmLHl1ebOBjQkKRzWx0uY9S5NrR+t+lkRnldIJRHXkkRUMMO8hJitrM
|
24
|
+
IyivxQKBgQCJOuOG+VldtQUWDcwjSCp4WaXPSnnDGq9yiOdxl3n2fTNa3jB8thp6
|
25
|
+
DN2h4UWo4WBcB67nuK/z+8Fj5vdT2yqT+1hX2vz/0p4zqmqWgIYHfTmIah/JXKf6
|
26
|
+
EofzG6tRXmxJteDbHUOGrvvOBV8ZjO0626pxPY8gRyN4VmQcF8J4oA==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,22 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDljCCAk6gAwIBAgIJAMYRH8WIV/+pMD0GCSqGSIb3DQEBCjAwoA0wCwYJYIZI
|
3
|
+
AWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQCAaIDAgEgMBIxEDAOBgNV
|
4
|
+
BAMMB3Rlc3QtY2EwHhcNMjAwMTE3MTg0OTQ2WhcNMzAwMTE0MTg0OTQ2WjAUMRIw
|
5
|
+
EAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
6
|
+
AQC1vsbOx5jU4hZ9ppox2quxYOQVUg0YvLAsuhbb3EebuWtcPvYPzqKh7FkqNFtm
|
7
|
+
doX1gTb0HPiyXvDbivxDkgUxrhbUte1jOMp81MAryyccdPAH2kATXuCYYH3+fIgg
|
8
|
+
Y1P+6dP9R2ZM6zVCIr6g7oJF1OhASu45taW5IbMYM4BwdxXsz1jIFK7Z0sBB7rVo
|
9
|
+
KenCLuFy4XFt1Tj3tn2ilMYvUeBckTwYuLIUKpR49gFNlcShw7uowIbvkvbAyjMd
|
10
|
+
yqaSDGuAS677JiC0gInYFEe/PK//T/92fzoCa9eojxxtp3k4DWU2Fnq+hfETtjAS
|
11
|
+
DhFWaRBTgdyzdgkid174B8IHAgMBAAGjgYwwgYkwCQYDVR0TBAIwADALBgNVHQ8E
|
12
|
+
BAMCAoQwFAYDVR0RBA0wC4IJbG9jYWxob3N0MFkGCCsGAQUFBwEBBE0wSzAhBggr
|
13
|
+
BgEFBQcwAYYVaHR0cDovL2xvY2FsaG9zdC9vY3NwMCYGCCsGAQUFBzAChhpodHRw
|
14
|
+
Oi8vbG9jYWxob3N0L2NhSXNzdWVyczA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFl
|
15
|
+
AwQCAaEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiAwIBIAOCAQEApWVE9xV9
|
16
|
+
M4LnQcrnoKeGIoc+8ux+5u9lmvugGlU+2PX9XBy5Y5KEWQ5q4QdKTFEXxzkkixfd
|
17
|
+
K1goaR2zVTxAEy+Wz8iaouy4FaRYR7h62XrDgxuTCwB4QFmi1UCggjp4u67j4IMm
|
18
|
+
yd7ajQFZW4aZZfGGxD4hrwOSX+f2jRLW14BvKuafoiBmUe4GJMdvds35HkDerS04
|
19
|
+
fIz5TSD8oxRCJf5MKHQvBcFK0CN11I/taL2ZP+U7rpIFWC/lDLaEkDtvsmEx+/c5
|
20
|
+
OsaDJ/N9++aMKfXzWXIQLkFVGTUic17PcpVa4VyAIqAqqqCNCDQdfd/4GbQUEzHN
|
21
|
+
TMskxId96tZnMg==
|
22
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpgIBAAKCAQEAtb7GzseY1OIWfaaaMdqrsWDkFVINGLywLLoW29xHm7lrXD72
|
3
|
+
D86ioexZKjRbZnaF9YE29Bz4sl7w24r8Q5IFMa4W1LXtYzjKfNTAK8snHHTwB9pA
|
4
|
+
E17gmGB9/nyIIGNT/unT/UdmTOs1QiK+oO6CRdToQEruObWluSGzGDOAcHcV7M9Y
|
5
|
+
yBSu2dLAQe61aCnpwi7hcuFxbdU497Z9opTGL1HgXJE8GLiyFCqUePYBTZXEocO7
|
6
|
+
qMCG75L2wMozHcqmkgxrgEuu+yYgtICJ2BRHvzyv/0//dn86AmvXqI8cbad5OA1l
|
7
|
+
NhZ6voXxE7YwEg4RVmkQU4Hcs3YJInde+AfCBwIDAQABAoIBAQC1LqkXP1gW8gUl
|
8
|
+
4k7Z4HtFa6g1sQSpYoHnNOTQ7frdPa3P2lyHSaJL9ki1kFiF/yZwpw5XsgIkIA3R
|
9
|
+
b+8olYtkCX9tMqijP9xtMdMaVX56GgGocmVqai1Q+vDgINm8IAl+mTDi0KkliV4g
|
10
|
+
vm54FQ39+9BFN+3hzjqWIwhd2gnzErSQU53RVSCZkz6he+BkuA13Yup1f2zzOhjD
|
11
|
+
Y6JkP69BvGJlwxbyEPsFPlX7CAV2yzXMZJ00wPZWUksRs9sD5FDfxtzn7BempIF5
|
12
|
+
/jbvGCObX6SU49ygPmsn0eD5Pv+HhBi4WgUkpd+bks0ueqnOssS148v6l5KR3yhR
|
13
|
+
/q79Co8RAoGBAO7s1AZh9alHRT7YSqn0m5GckKNpcozL+nQGyHq/rucHRqMzcxoH
|
14
|
+
MH4zzNvZ/l7TyOZ1QegcLjX0AbARW0xa+OtTyuVjdao4DRN/fs+DVajL7PHp+Jks
|
15
|
+
KaBsuo0QnFEv+WJG5VTDTgeTeroTOcYLr5cTT2jQ6cW4wQ7/FdCvGH4jAoGBAMK7
|
16
|
+
1V5OkX322MyeQQ12jEJQ9A3nCd54Vvby5yu1OcHXfgmcS4cuKN2QCPqHmzMLDs+d
|
17
|
+
aoGMgBeC73WcdwJw6cVyNsj0y2zUepwRO8877febwWfQvNLN2lTrRcj4iwqZKKVp
|
18
|
+
H/yUb/0ccUjJPPmuXHVCfqtZO+cTdsjfWuAHtkDNAoGBAIxRSEArXdQ5yCAddNQV
|
19
|
+
lRvSQFvIPP8VeJSVuz3jvzttWX3VZH7fxAoKMADaKPrWFIHMUZWYn0cOc8NMnjrc
|
20
|
+
np3OSzWm0N7UJlHSKc6DSlZk1VZJ27dRaW0PDgx0uekwbJzcGClMvlHSulv3mJGI
|
21
|
+
IWpva86aCwEU/UTqaIxzmMXTAoGBAJMCAALw508jwek+8zc5rosF2CiCqWWkjWpi
|
22
|
+
V3gcmNyoVMLmlfIYO8t/x/dx1g1DpMvBN71TFwQo2aN9Gi7ilOh037z3aHbhNSqK
|
23
|
+
rA83W9+YWvqHj3TI6LFA5+7fCwBWPWQaJ4ajfKOlDDR5jymiorP3He83L0yz2fGt
|
24
|
+
BEqqJ8FlAoGBAJQpxTdpoYZqGFHR05Blo9xAINsylRS1cHtoxIeYI0citu0g4H7z
|
25
|
+
+G3Ks3fJqiS7nGXdB4uMCDcBINLzVed1MWJCGhoNb8fLNO3aYg0my7H2cYfBALj5
|
26
|
+
MSR2pc8Q1Rl0Q2TdyYKPaqeyo6HoPGpNUqTGKLvbs5SFbyRRUJJQF/ai
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/spec/spec_helper.rb
ADDED
data/spec/utils_spec.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'spec_helper'
|
4
|
+
using OCSPResponseFetch::Refinements
|
5
|
+
|
6
|
+
RSpec.describe OCSPResponseFetch::Refinements do
|
7
|
+
context 'OpenSSL::X509::Certificate#ocsp_uris' do
|
8
|
+
let(:cert) do
|
9
|
+
OpenSSL::X509::Certificate.new(
|
10
|
+
File.read(__dir__ + '/fixtures/rsa_rsassaPss.crt')
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return URL' do
|
15
|
+
expect(cert.ocsp_uris).to eq ['http://localhost/ocsp']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'OpenSSL::X509::Certificate#ca_issuer_uris' do
|
20
|
+
let(:cert) do
|
21
|
+
OpenSSL::X509::Certificate.new(
|
22
|
+
File.read(__dir__ + '/fixtures/rsa_rsassaPss.crt')
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return URL' do
|
27
|
+
expect(cert.ca_issuer_uris).to eq ['http://localhost/caIssuers']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'OpenSSL::OCSP::Response#to_text' do
|
32
|
+
let(:subject_cert) do
|
33
|
+
OpenSSL::X509::Certificate.new(
|
34
|
+
File.read(__dir__ + '/fixtures/rsa_rsassaPss.crt')
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:issuer_cert) do
|
39
|
+
OpenSSL::X509::Certificate.new(
|
40
|
+
File.read(__dir__ + '/fixtures/rsa_rsassaPss.crt')
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:cid) do
|
45
|
+
OpenSSL::OCSP::CertificateId.new(subject_cert, issuer_cert)
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:thisupd) do
|
49
|
+
Time.now
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:nextupd) do
|
53
|
+
Time.now + (60 * 60 * 24 * 7)
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:ocsp_response) do
|
57
|
+
bres = OpenSSL::OCSP::BasicResponse.new
|
58
|
+
bres.add_status(
|
59
|
+
cid,
|
60
|
+
OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL,
|
61
|
+
OpenSSL::OCSP::V_CERTSTATUS_GOOD,
|
62
|
+
Time.now + (60 * 60 * 24 * 365 * 10),
|
63
|
+
thisupd,
|
64
|
+
nextupd,
|
65
|
+
[]
|
66
|
+
)
|
67
|
+
bres.add_nonce
|
68
|
+
bres.sign(
|
69
|
+
subject_cert,
|
70
|
+
OpenSSL::PKey.read(
|
71
|
+
File.read(__dir__ + '/fixtures/rsa_rsassaPss.key')
|
72
|
+
)
|
73
|
+
)
|
74
|
+
|
75
|
+
OpenSSL::OCSP::Response.create(
|
76
|
+
OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL,
|
77
|
+
bres
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should return text' do
|
82
|
+
expect(ocsp_response.to_text).to eq <<~"OCSP_RESPONSE"
|
83
|
+
OCSP Response Data:
|
84
|
+
OCSP Response Status: (0x0)
|
85
|
+
Responses:
|
86
|
+
Certificate ID:
|
87
|
+
Hash Algorithm: sha1
|
88
|
+
Issuer Name Hash: #{cid.issuer_name_hash.upcase}
|
89
|
+
Issuer Key Hash: #{cid.issuer_key_hash.upcase}
|
90
|
+
Serial Number: #{cid.serial.to_s(16)}
|
91
|
+
Cert Status: good
|
92
|
+
This Update: #{thisupd.utc}
|
93
|
+
Next Update: #{nextupd.utc}
|
94
|
+
OCSP_RESPONSE
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ocsprf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- thekuwayama
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: openssl
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: OCSP Response Fetch
|
42
|
+
email:
|
43
|
+
- thekuwayama@gmail.com
|
44
|
+
executables:
|
45
|
+
- ocsprf
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".github/workflows/main.yml"
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rubocop.yml"
|
52
|
+
- Gemfile
|
53
|
+
- LICENSE.txt
|
54
|
+
- README.md
|
55
|
+
- Rakefile
|
56
|
+
- exe/ocsprf
|
57
|
+
- lib/ocsp_response_fetch.rb
|
58
|
+
- lib/ocsp_response_fetch/cli.rb
|
59
|
+
- lib/ocsp_response_fetch/error.rb
|
60
|
+
- lib/ocsp_response_fetch/fetcher.rb
|
61
|
+
- lib/ocsp_response_fetch/utils.rb
|
62
|
+
- lib/ocsp_response_fetch/version.rb
|
63
|
+
- ocsprf.gemspec
|
64
|
+
- spec/cli_spec.rb
|
65
|
+
- spec/fetcher_spec.rb
|
66
|
+
- spec/fixtures/rsa_ca.crt
|
67
|
+
- spec/fixtures/rsa_ca.key
|
68
|
+
- spec/fixtures/rsa_rsassaPss.crt
|
69
|
+
- spec/fixtures/rsa_rsassaPss.key
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/utils_spec.rb
|
72
|
+
homepage: https://github.com/thekuwayama/ocsprf
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 2.6.0
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubygems_version: 3.1.2
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: OCSP Response Fetch
|
95
|
+
test_files:
|
96
|
+
- spec/cli_spec.rb
|
97
|
+
- spec/fetcher_spec.rb
|
98
|
+
- spec/fixtures/rsa_ca.crt
|
99
|
+
- spec/fixtures/rsa_ca.key
|
100
|
+
- spec/fixtures/rsa_rsassaPss.crt
|
101
|
+
- spec/fixtures/rsa_rsassaPss.key
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
- spec/utils_spec.rb
|