ocsprf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/ocsprf.svg)](https://badge.fury.io/rb/ocsprf)
|
4
|
+
[![Actions Status](https://github.com/thekuwayama/ocsprf/workflows/CI/badge.svg)](https://github.com/thekuwayama/ocsprf/actions?workflow=CI)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/4d5bb71e2dca46f5a239/maintainability)](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
|