ocsprf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ Gemfile.lock
4
+ .config
5
+ .rvmrc
6
+ /.bundle/
7
+ /vendor/
8
+ /lib/bundler/man/
9
+ /pkg/
10
+ /.yardoc/
11
+ /_yardoc/
12
+ /doc/
13
+ /rdoc/
14
+ /coverage/
15
+ /spec/reports/
16
+ /tmp/
17
+ *.rdb
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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'openssl'
6
+ gem 'rake'
7
+
8
+ group :test do
9
+ gem 'pry'
10
+ gem 'pry-byebug'
11
+ gem 'rspec', '3.9.0'
12
+ gem 'rubocop', '0.78.0'
13
+ end
14
+
15
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rubocop/rake_task'
5
+ require 'rspec/core/rake_task'
6
+
7
+ RuboCop::RakeTask.new
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: %i[rubocop spec]
data/exe/ocsprf ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH << __dir__ + '/../lib'
5
+
6
+ require 'ocsp_response_fetch'
7
+
8
+ OCSPResponseFetch::CLI.run
@@ -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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OCSPResponseFetch
4
+ module Error
5
+ # Generic error
6
+ class Error < StandardError; end
7
+
8
+ class RevokedError < Error; end
9
+ class FetchFailedError < Error; end
10
+ end
11
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OCSPResponseFetch
4
+ VERSION = '0.0.1'
5
+ 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-----
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.configure(&:disable_monkey_patching!)
4
+
5
+ require 'ocsp_response_fetch'
@@ -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