ruby-sslyze 0.1.0
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/.document +3 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.travis.yml +19 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +8 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +20 -0
- data/README.md +70 -0
- data/Rakefile +23 -0
- data/lib/sslyze.rb +3 -0
- data/lib/sslyze/cert_info.rb +55 -0
- data/lib/sslyze/certificate.rb +139 -0
- data/lib/sslyze/certificate/domain_name.rb +77 -0
- data/lib/sslyze/certificate/extensions.rb +127 -0
- data/lib/sslyze/certificate/extensions/authority_information_access.rb +38 -0
- data/lib/sslyze/certificate/extensions/extension.rb +26 -0
- data/lib/sslyze/certificate/extensions/x509v3_basic_constraints.rb +60 -0
- data/lib/sslyze/certificate/extensions/x509v3_certificate_policies.rb +50 -0
- data/lib/sslyze/certificate/extensions/x509v3_crl_distribution_points.rb +32 -0
- data/lib/sslyze/certificate/extensions/x509v3_extended_key_usage.rb +32 -0
- data/lib/sslyze/certificate/extensions/x509v3_key_usage.rb +50 -0
- data/lib/sslyze/certificate/extensions/x509v3_subject_alternative_name.rb +71 -0
- data/lib/sslyze/certificate/issuer.rb +56 -0
- data/lib/sslyze/certificate/public_key.rb +9 -0
- data/lib/sslyze/certificate/subject.rb +117 -0
- data/lib/sslyze/certificate/subject_public_key_info.rb +53 -0
- data/lib/sslyze/certificate/validity.rb +9 -0
- data/lib/sslyze/certificate_chain.rb +89 -0
- data/lib/sslyze/certificate_validation.rb +44 -0
- data/lib/sslyze/cipher_suite.rb +237 -0
- data/lib/sslyze/key_exchange.rb +106 -0
- data/lib/sslyze/ocsp_response.rb +87 -0
- data/lib/sslyze/program.rb +66 -0
- data/lib/sslyze/protocol.rb +133 -0
- data/lib/sslyze/target.rb +295 -0
- data/lib/sslyze/task.rb +65 -0
- data/lib/sslyze/types.rb +17 -0
- data/lib/sslyze/version.rb +4 -0
- data/lib/sslyze/xml.rb +139 -0
- data/ruby-sslyze.gemspec +24 -0
- data/spec/cert_info_spec.rb +29 -0
- data/spec/certificate/subject_name_spec.rb +72 -0
- data/spec/certificate_chain_spec.rb +61 -0
- data/spec/certificate_spec.rb +330 -0
- data/spec/certificate_validation_spec.rb +27 -0
- data/spec/cipher_suite_spec.rb +50 -0
- data/spec/issuer_spec.rb +33 -0
- data/spec/key_exchange_spec.rb +97 -0
- data/spec/ocsp_response_spec.rb +59 -0
- data/spec/protocol_spec.rb +99 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/sslyze.xml +2798 -0
- data/spec/sslyze_spec.rb +8 -0
- data/spec/subject_public_key_info_spec.rb +35 -0
- data/spec/subject_spec.rb +67 -0
- data/spec/target_spec.rb +176 -0
- data/spec/xml_examples.rb +9 -0
- data/spec/xml_spec.rb +72 -0
- metadata +162 -0
data/lib/sslyze/task.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rprogram/task'
|
2
|
+
|
3
|
+
module SSLyze
|
4
|
+
#
|
5
|
+
# Represents options for {Program}.
|
6
|
+
#
|
7
|
+
class Task < RProgram::Task
|
8
|
+
|
9
|
+
# Options:
|
10
|
+
long_option flag: '--version'
|
11
|
+
long_option flag: '--help'
|
12
|
+
long_option flag: '--xml_out'
|
13
|
+
long_option flag: '--targets_in'
|
14
|
+
long_option flag: '--timeout'
|
15
|
+
long_option flag: '--nb_retries'
|
16
|
+
long_option flag: '--https_tunnel'
|
17
|
+
long_option flag: '--starttls'
|
18
|
+
long_option flag: '--xmpp_to'
|
19
|
+
long_option flag: '--sni'
|
20
|
+
long_option flag: '--regular'
|
21
|
+
|
22
|
+
# Client certificate support:
|
23
|
+
long_option flag: '--cert'
|
24
|
+
long_option flag: '--certfrom'
|
25
|
+
long_option flag: '--key'
|
26
|
+
long_option flag: '--keyfrom'
|
27
|
+
long_option flag: '--pass'
|
28
|
+
|
29
|
+
# PluginHeartbleed:
|
30
|
+
long_option flag: '--heartbleed'
|
31
|
+
|
32
|
+
# PluginOpenSSLCipherSuites:
|
33
|
+
# Scans the server(s) for supported OpenSSL cipher suites.
|
34
|
+
long_option flag: '--sslv2'
|
35
|
+
long_option flag: '--sslv3'
|
36
|
+
long_option flag: '--tlsv1'
|
37
|
+
long_option flag: '--tlsv1_1'
|
38
|
+
long_option flag: '--tlsv1_2'
|
39
|
+
long_option flag: '--http_get'
|
40
|
+
long_option flag: '--hide_rejected_ciphers'
|
41
|
+
|
42
|
+
# PluginSessionRenegotiation:
|
43
|
+
long_option flag: '--reneg'
|
44
|
+
|
45
|
+
# PluginCertInfo:
|
46
|
+
long_option flag: '--certinfo'
|
47
|
+
|
48
|
+
# PluginHSTS:
|
49
|
+
long_option flag: '--hsts'
|
50
|
+
|
51
|
+
# PluginSessionResumption:
|
52
|
+
# Analyzes the target server's SSL session resumption capabilities.
|
53
|
+
long_option flag: '--resum'
|
54
|
+
long_option flag: '--resum_rate'
|
55
|
+
|
56
|
+
# PluginChromeSha1Deprecation:
|
57
|
+
long_option flag: '--chrome_sha1'
|
58
|
+
|
59
|
+
# PluginCompression:
|
60
|
+
long_option flag: '--compression'
|
61
|
+
|
62
|
+
non_option name: :targets, tailing: true
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/sslyze/types.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module SSLyze
|
2
|
+
module Types
|
3
|
+
# Maps `"True"` and `"False"` to boolean values.
|
4
|
+
Boolean = {
|
5
|
+
'True' => true,
|
6
|
+
'False' => false
|
7
|
+
}
|
8
|
+
|
9
|
+
# Maps `"None"` to `nil`
|
10
|
+
None = proc { |value|
|
11
|
+
case value
|
12
|
+
when 'None' then nil
|
13
|
+
else value
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
data/lib/sslyze/xml.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'sslyze/target'
|
2
|
+
require 'sslyze/types'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module SSLyze
|
6
|
+
#
|
7
|
+
# Represents the XML output from sslyze.
|
8
|
+
#
|
9
|
+
class XML
|
10
|
+
|
11
|
+
include Types
|
12
|
+
|
13
|
+
#
|
14
|
+
# Initializes the XML.
|
15
|
+
#
|
16
|
+
# @param [Nokogiri::XML::Document] doc
|
17
|
+
# The XML document.
|
18
|
+
#
|
19
|
+
def initialize(doc)
|
20
|
+
@doc = doc
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Parses the XML.
|
25
|
+
#
|
26
|
+
# @param [String] xml
|
27
|
+
# The raw XML.
|
28
|
+
#
|
29
|
+
# @return [XML]
|
30
|
+
#
|
31
|
+
def self.parse(xml)
|
32
|
+
new(Nokogiri::XML.parse(xml))
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Opens the XML file.
|
37
|
+
#
|
38
|
+
# @param [String] path
|
39
|
+
# Path to the XML file.
|
40
|
+
#
|
41
|
+
# @return [XML]
|
42
|
+
#
|
43
|
+
def self.open(path)
|
44
|
+
new(Nokogiri::XML(File.open(path)))
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# The version of the XML output.
|
49
|
+
#
|
50
|
+
# @return [String]
|
51
|
+
#
|
52
|
+
def version
|
53
|
+
@version ||= @doc.at('/document/@SSLyzeVersion').value.split(' ',2).last
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# The default timeout used.
|
58
|
+
#
|
59
|
+
# @return [Integer]
|
60
|
+
#
|
61
|
+
def default_timeout
|
62
|
+
@default_time ||= @doc.at('/document/results/@defaultTimeout').value.to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Whether an HTTPS tunnel was used.
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
#
|
70
|
+
def https_tunnel
|
71
|
+
@https_tunnel ||= Boolean[@doc.at('/document/results/@httpsTunnel').value]
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Specifies whether STARTTLS was enabled.
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
#
|
79
|
+
def start_tls
|
80
|
+
@start_tls ||= Boolean[@doc.at('/document/results/@startTLS').value]
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Duration of the scan.
|
85
|
+
#
|
86
|
+
# @return [Float]
|
87
|
+
#
|
88
|
+
def total_scan_time
|
89
|
+
@start_tls ||= @doc.at('/document/results/@totalScanTime').value.to_f
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# The invalid targets.
|
94
|
+
#
|
95
|
+
# @raise [NotImplementedError]
|
96
|
+
#
|
97
|
+
def invalid_targets
|
98
|
+
raise(NotImplementedError,"#{self.class}##{__method__} not implemented")
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Enumerates over each target.
|
103
|
+
#
|
104
|
+
# @yield [target]
|
105
|
+
#
|
106
|
+
# @yieldparam [Target] target
|
107
|
+
#
|
108
|
+
# @return [Enumerator]
|
109
|
+
#
|
110
|
+
def each_target
|
111
|
+
return enum_for(__method__) unless block_given?
|
112
|
+
|
113
|
+
@doc.search('/document/results/target').each do |target|
|
114
|
+
yield Target.new(target)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
alias each each_target
|
119
|
+
|
120
|
+
#
|
121
|
+
# @return [Array<Target>]
|
122
|
+
#
|
123
|
+
# @see #each_target
|
124
|
+
#
|
125
|
+
def targets
|
126
|
+
each_target.to_a
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# The first target.
|
131
|
+
#
|
132
|
+
# @return [Target, nil]
|
133
|
+
#
|
134
|
+
def target
|
135
|
+
each_target.first
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
data/ruby-sslyze.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/sslyze/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "ruby-sslyze"
|
7
|
+
gem.version = SSLyze::VERSION
|
8
|
+
gem.summary = %q{Ruby interface to sslyze}
|
9
|
+
gem.description = %q{A ruby interface to the sslyze python utility}
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.authors = ["Hal Brodigan"]
|
12
|
+
gem.email = "hal@trailofbits.com"
|
13
|
+
gem.homepage = "https://github.com/trailofbits/ruby-sslyze#readme"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'rprogram', '~> 0.3'
|
21
|
+
gem.add_dependency 'nokogiri', '~> 1.0'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'bundler', '~> 1.0'
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'xml_examples'
|
3
|
+
require 'sslyze/cert_info'
|
4
|
+
|
5
|
+
describe SSLyze::CertInfo do
|
6
|
+
include_examples "XML specs"
|
7
|
+
|
8
|
+
subject { described_class.new(xml.at('/document/results/target/certinfo')) }
|
9
|
+
|
10
|
+
describe "#chain" do
|
11
|
+
it "should return a CertificateChain object" do
|
12
|
+
expect(subject.chain).to be_a(CertificateChain)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#validation" do
|
17
|
+
it "should return a CertificateValidation element" do
|
18
|
+
expect(subject.validation).to be_kind_of(CertificateValidation)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#ocsp_response" do
|
23
|
+
subject { described_class.new(xml.at('/document/results/target/certinfo[ocspStapling/ocspResponse]')) }
|
24
|
+
|
25
|
+
it "should return a OCSPResponse object" do
|
26
|
+
expect(subject.ocsp_response).to be_kind_of(OCSPResponse)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sslyze/certificate/domain_name'
|
3
|
+
|
4
|
+
describe SSLyze::Certificate::DomainName do
|
5
|
+
let(:name) { 'twitter.com' }
|
6
|
+
|
7
|
+
subject { described_class.new(name) }
|
8
|
+
|
9
|
+
describe "#==" do
|
10
|
+
context "when the domain names are the same" do
|
11
|
+
let(:other) { described_class.new(name) }
|
12
|
+
|
13
|
+
it "should return true" do
|
14
|
+
expect(subject == other).to be true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when the domain names are different" do
|
19
|
+
let(:other) { described_class.new(name + 'XXX') }
|
20
|
+
|
21
|
+
it "should return true" do
|
22
|
+
expect(subject == other).to be false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#include?" do
|
28
|
+
context "when the domain name is literal" do
|
29
|
+
it "should compare the given domain to the domain name" do
|
30
|
+
expect(subject.include?(name)).to be true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when the domain name has a wildcard" do
|
35
|
+
let(:wildcard) { "*.#{name}" }
|
36
|
+
|
37
|
+
subject { described_class.new(wildcard) }
|
38
|
+
|
39
|
+
it "should match the domain" do
|
40
|
+
expect(subject.include?(name)).to be true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should match any sub-domain" do
|
44
|
+
expect(subject.include?("foo.#{name}")).to be true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#to_s" do
|
50
|
+
it "should return the domain name" do
|
51
|
+
expect(subject.to_s).to be name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#to_str" do
|
56
|
+
it "should return the domain name" do
|
57
|
+
expect(subject.to_str).to be name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#inspect" do
|
62
|
+
subject { super().inspect }
|
63
|
+
|
64
|
+
it "should include the class name" do
|
65
|
+
expect(subject).to include(described_class.name)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should include the domain name" do
|
69
|
+
expect(subject).to include(name)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'xml_examples'
|
3
|
+
require 'sslyze/certificate_chain'
|
4
|
+
|
5
|
+
describe SSLyze::CertificateChain do
|
6
|
+
include_examples "XML specs"
|
7
|
+
|
8
|
+
subject { described_class.new(xml.at('/document/results/target/certinfo/certificateChain')) }
|
9
|
+
|
10
|
+
describe "#each" do
|
11
|
+
context "when given a block" do
|
12
|
+
it "should yield Certificate objects" do
|
13
|
+
expect { |b|
|
14
|
+
subject.each(&b)
|
15
|
+
}.to yield_successive_args(Certificate, Certificate)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when not given a block" do
|
20
|
+
it "should return an Enumerator" do
|
21
|
+
expect(subject.each).to be_kind_of(Enumerator)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#leaf" do
|
27
|
+
it "should return a Certificate with position leaf" do
|
28
|
+
expect(subject.leaf).to be_a(Certificate)
|
29
|
+
expect(subject.leaf.position).to be :leaf
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#each_intermediate" do
|
34
|
+
context "when given a block" do
|
35
|
+
it "should return Certificates with position intermediate" do
|
36
|
+
expect { |b|
|
37
|
+
subject.each_intermediate(&b)
|
38
|
+
}.to yield_successive_args(Certificate)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when not given a block" do
|
43
|
+
it "should return an Enumerator" do
|
44
|
+
expect(subject.each_intermediate).to be_kind_of(Enumerator)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#intermediate" do
|
50
|
+
it "should return all intermediate certificates" do
|
51
|
+
expect(subject.intermediate).to all(be_kind_of(Certificate))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#root" do
|
56
|
+
it "should find the last intermediate certificate" do
|
57
|
+
expect(subject.root.sha1_fingerprint).to be == subject.intermediate.to_a.last.sha1_fingerprint
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,330 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'xml_examples'
|
3
|
+
require 'sslyze/certificate'
|
4
|
+
|
5
|
+
describe SSLyze::Certificate do
|
6
|
+
include_examples "XML specs"
|
7
|
+
|
8
|
+
subject { described_class.new(xml.at('/document/results/target/certinfo/certificateChain/certificate')) }
|
9
|
+
|
10
|
+
describe "#position" do
|
11
|
+
it "should parse the position attribute" do
|
12
|
+
expect(subject.position).to be :leaf
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#sha1_fingerprint" do
|
17
|
+
it "should parse the sha1Fingerprint attribute" do
|
18
|
+
expect(subject.sha1_fingerprint).to be == 'a0c4a74600eda72dc0becb9a8cb607ca58ee745e'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#as_pem" do
|
23
|
+
it "should parse the asPEM element" do
|
24
|
+
expect(subject.as_pem).to be == %{
|
25
|
+
-----BEGIN CERTIFICATE-----
|
26
|
+
MIIF4DCCBMigAwIBAgIQDACTENIG2+M3VTWAEY3chzANBgkqhkiG9w0BAQsFADB1
|
27
|
+
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
28
|
+
d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk
|
29
|
+
IFZhbGlkYXRpb24gU2VydmVyIENBMB4XDTE0MDQwODAwMDAwMFoXDTE2MDQxMjEy
|
30
|
+
MDAwMFowgfAxHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
|
31
|
+
BAGCNzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQF
|
32
|
+
Ewc1MTU3NTUwMRcwFQYDVQQJEw41NDggNHRoIFN0cmVldDEOMAwGA1UEERMFOTQx
|
33
|
+
MDcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T
|
34
|
+
YW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xEzARBgNVBAMTCmdp
|
35
|
+
dGh1Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx1Nw8r/3z
|
36
|
+
Tu3BZ63myyLot+KrKPL33GJwCNEMr9YWaiGwNksXDTZjBK6/6iBRlWVm8r+5TaQM
|
37
|
+
Kev1FbHoNbNwEJTVG1m0Jg/Wg1dZneF8Cd3gE8pNb0Obzc+HOhWnhd1mg+2TDP4r
|
38
|
+
bTgceYiQz61YGC1R0cKj8keMbzgJubjvTJMLy4OUh+rgo7XZe5trD0P5yu6ADSin
|
39
|
+
dvEl9ME1PPZ0rd5qM4J73P1LdqfC7vJqv6kkpl/nLnwO28N0c/p+xtjPYOs2ViG2
|
40
|
+
wYq4JIJNeCS66R2hiqeHvmYlab++O3JuT+DkhSUIsZGJuNZ0ZXabLE9iH6H6Or6c
|
41
|
+
JL+fyrDFwGeNAgMBAAGjggHuMIIB6jAfBgNVHSMEGDAWgBQ901Cl1qCt7vNKYApl
|
42
|
+
0yHU+PjWDzAdBgNVHQ4EFgQUakOQfTuYFHJSlTqqKApD+FF+06YwJQYDVR0RBB4w
|
43
|
+
HIIKZ2l0aHViLmNvbYIOd3d3LmdpdGh1Yi5jb20wDgYDVR0PAQH/BAQDAgWgMB0G
|
44
|
+
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5o
|
45
|
+
dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1ldi1zZXJ2ZXItZzEuY3JsMDSg
|
46
|
+
MqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1ldi1zZXJ2ZXItZzEu
|
47
|
+
Y3JsMEIGA1UdIAQ7MDkwNwYJYIZIAYb9bAIBMCowKAYIKwYBBQUHAgEWHGh0dHBz
|
48
|
+
Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgYgGCCsGAQUFBwEBBHwwejAkBggrBgEF
|
49
|
+
BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFIGCCsGAQUFBzAChkZodHRw
|
50
|
+
Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyRXh0ZW5kZWRWYWxp
|
51
|
+
ZGF0aW9uU2VydmVyQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
|
52
|
+
ggEBAG/nbcuC8++QhwnXDxUiLIz+06scipbbXRJd0XjAMbD/RciJ9wiYUhcfTEsg
|
53
|
+
ZGpt21DXEL5+q/4vgNipSlhBaYFyGQiDm5IQTmIte0ZwQ26jUxMf4pOmI1v3kj43
|
54
|
+
FHU7uUskQS6lPUgND5nqHkKXxv6V2qtHmssrA9YNQMEK93ga2rWDpK21mUkgLviT
|
55
|
+
PB5sPdE7IzprOCp+Ynpf3RcFddAkXb6NqJoQRPrStMrv19C1dqUmJRwIQdhkkqev
|
56
|
+
ff6IQDlhC8BIMKmCNK33cEYDfDWROtW7JNgBvBTwww8jO1gyug8SbGZ6bZ3k8OV8
|
57
|
+
XX4C2NesiZcLYbc2n7B9O+63M2k=
|
58
|
+
-----END CERTIFICATE-----
|
59
|
+
}.strip
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#subject_public_key_info" do
|
64
|
+
it "must return a Certificate::SubjectPublicKeyInfo object" do
|
65
|
+
expect(subject.subject_public_key_info).to be_kind_of(described_class::SubjectPublicKeyInfo)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#version" do
|
70
|
+
it "should parse the version element" do
|
71
|
+
expect(subject.version).to be == 2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#extensions" do
|
76
|
+
subject { super().extensions }
|
77
|
+
|
78
|
+
it "should return an Extensions object" do
|
79
|
+
expect(subject).to be_kind_of(described_class::Extensions)
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#x509v3_subject_key_identifier" do
|
83
|
+
subject { super().x509v3_subject_key_identifier }
|
84
|
+
|
85
|
+
it "should parse the X509v3SubjectKeyIdentifier element" do
|
86
|
+
expect(subject).to be == '6A:43:90:7D:3B:98:14:72:52:95:3A:AA:28:0A:43:F8:51:7E:D3:A6'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#x509v3_extended_key_usage" do
|
91
|
+
subject { super().x509v3_extended_key_usage }
|
92
|
+
|
93
|
+
it "should return a X509v3ExtendedKeyUsage object" do
|
94
|
+
expect(subject).to be_kind_of(described_class::Extensions::X509v3ExtendedKeyUsage)
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#tls_web_client_authentication" do
|
98
|
+
subject { super().tls_web_client_authentication }
|
99
|
+
|
100
|
+
it "should parse the TLSWebClientAuthentication element" do
|
101
|
+
expect(subject).to be == ''
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#tls_web_server_authentication" do
|
106
|
+
subject { super().tls_web_server_authentication }
|
107
|
+
|
108
|
+
it "should parse the TLSWebServerAuthentication element" do
|
109
|
+
expect(subject).to be == ''
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#authority_information_access" do
|
115
|
+
subject { super().authority_information_access }
|
116
|
+
|
117
|
+
it "should return an AuthorityInformationAccess object" do
|
118
|
+
expect(subject).to be_kind_of(described_class::Extensions::AuthorityInformationAccess)
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#ca_issuers" do
|
122
|
+
subject { super().ca_issuers }
|
123
|
+
|
124
|
+
it "should parse the CAIssuers element" do
|
125
|
+
expect(subject).to be == [
|
126
|
+
URI('http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt')
|
127
|
+
]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#ocsp" do
|
132
|
+
subject { super().ocsp }
|
133
|
+
|
134
|
+
it "should parse the OCSP element" do
|
135
|
+
expect(subject).to be == [
|
136
|
+
URI('http://ocsp.digicert.com')
|
137
|
+
]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "#x509v3_crl_distribution_points" do
|
143
|
+
subject { super().x509v3_crl_distribution_points }
|
144
|
+
|
145
|
+
it "should return a X509v3CRLDistributionPoints object" do
|
146
|
+
expect(subject).to be_kind_of(described_class::Extensions::X509v3CRLDistributionPoints)
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#full_name" do
|
150
|
+
subject { super().full_name }
|
151
|
+
|
152
|
+
it "should parse the FullName element" do
|
153
|
+
expect(subject).to be == ['', '']
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#uri" do
|
158
|
+
subject { super().uri }
|
159
|
+
|
160
|
+
it "should parse the URI element" do
|
161
|
+
expect(subject).to be == [
|
162
|
+
URI('http://crl3.digicert.com/sha2-ev-server-g1.crl'),
|
163
|
+
URI('http://crl4.digicert.com/sha2-ev-server-g1.crl')
|
164
|
+
]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "#x509v3_basic_constraints" do
|
170
|
+
subject { super().x509v3_basic_constraints }
|
171
|
+
|
172
|
+
it "should parse the X509v3BasicConstraints element" do
|
173
|
+
expect(subject).to be_kind_of(described_class::Extensions::X509v3BasicConstraints)
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#ca?" do
|
177
|
+
subject { super().ca? }
|
178
|
+
|
179
|
+
it "should parse the 'CA:' constraint" do
|
180
|
+
expect(subject).to be false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#path_length" do
|
185
|
+
subject { super().path_length }
|
186
|
+
|
187
|
+
pending "need data" do
|
188
|
+
it "should parse the 'pathLen:' constraint" do
|
189
|
+
expect(subject).to be == 0
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "#x509v3_key_usage" do
|
196
|
+
subject { super().x509v3_key_usage }
|
197
|
+
|
198
|
+
it "should return a X509v3KeyUsage object" do
|
199
|
+
expect(subject).to be_kind_of(described_class::Extensions::X509v3KeyUsage)
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "#key_encipherment" do
|
203
|
+
subject { super().key_encipherment }
|
204
|
+
|
205
|
+
it "should parse the KeyEncipherment element" do
|
206
|
+
expect(subject).to be == ''
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "#digital_signature" do
|
211
|
+
subject { super().digital_signature }
|
212
|
+
|
213
|
+
it "should parse the DigitalSignature element" do
|
214
|
+
expect(subject).to be == ''
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "#x509v3_subject_alternative_name" do
|
220
|
+
subject { super().x509v3_subject_alternative_name }
|
221
|
+
|
222
|
+
it "should parse the X509v3SubjectAlternativeName elements" do
|
223
|
+
expect(subject).to be_kind_of(described_class::Extensions::X509v3SubjectAlternativeName)
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "#dns" do
|
227
|
+
subject { super().dns }
|
228
|
+
|
229
|
+
it "should parse the DNS/listEntry elements" do
|
230
|
+
expect(subject).to be == [
|
231
|
+
described_class::DomainName.new('github.com'),
|
232
|
+
described_class::DomainName.new('www.github.com')
|
233
|
+
]
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "#x509v3_authority_key_identifier" do
|
239
|
+
subject { super().x509v3_authority_key_identifier }
|
240
|
+
|
241
|
+
it "should parse the X509v3AuthorityKeyIdentifier element" do
|
242
|
+
expect(subject).to be == 'keyid:3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F'
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "#x509v3_certificate_policies" do
|
247
|
+
subject { super().x509v3_certificate_policies }
|
248
|
+
|
249
|
+
it "should return a X509v3CertificatePolicies object" do
|
250
|
+
expect(subject).to be_kind_of(described_class::Extensions::X509v3CertificatePolicies)
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "#policy" do
|
254
|
+
subject { super().policy }
|
255
|
+
|
256
|
+
it "should parse the Policy element" do
|
257
|
+
expect(subject).to be == ['2.16.840.1.114412.2.1']
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "#explicit_text" do
|
262
|
+
subject { super().explicit_text }
|
263
|
+
|
264
|
+
it "should parse the ExplicitText element" do
|
265
|
+
expect(subject).to be == []
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe "#cps" do
|
270
|
+
subject { super().cps }
|
271
|
+
|
272
|
+
it "should parse the CPS element" do
|
273
|
+
expect(subject).to be == ['https://www.digicert.com/CPS']
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe "#user_notice" do
|
278
|
+
subject { super().user_notice }
|
279
|
+
|
280
|
+
it "should parse the UserNotice element" do
|
281
|
+
expect(subject).to be == []
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "#signature_value" do
|
288
|
+
it "should parse the signatureValue element" do
|
289
|
+
expect(subject.signature_value).to be == %{6f:e7:6d:cb:82:f3:ef:90:87:09:d7:0f:15:22:2c:8c:fe:d3:ab:1c:8a:96:db:5d:12:5d:d1:78:c0:31:b0:ff:45:c8:89:f7:08:98:52:17:1f:4c:4b:20:64:6a:6d:db:50:d7:10:be:7e:ab:fe:2f:80:d8:a9:4a:58:41:69:81:72:19:08:83:9b:92:10:4e:62:2d:7b:46:70:43:6e:a3:53:13:1f:e2:93:a6:23:5b:f7:92:3e:37:14:75:3b:b9:4b:24:41:2e:a5:3d:48:0d:0f:99:ea:1e:42:97:c6:fe:95:da:ab:47:9a:cb:2b:03:d6:0d:40:c1:0a:f7:78:1a:da:b5:83:a4:ad:b5:99:49:20:2e:f8:93:3c:1e:6c:3d:d1:3b:23:3a:6b:38:2a:7e:62:7a:5f:dd:17:05:75:d0:24:5d:be:8d:a8:9a:10:44:fa:d2:b4:ca:ef:d7:d0:b5:76:a5:26:25:1c:08:41:d8:64:92:a7:af:7d:fe:88:40:39:61:0b:c0:48:30:a9:82:34:ad:f7:70:46:03:7c:35:91:3a:d5:bb:24:d8:01:bc:14:f0:c3:0f:23:3b:58:32:ba:0f:12:6c:66:7a:6d:9d:e4:f0:e5:7c:5d:7e:02:d8:d7:ac:89:97:0b:61:b7:36:9f:b0:7d:3b:ee:b7:33:69}
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "#signature_algorithm" do
|
294
|
+
it "should parse the signatureAlgorithm element" do
|
295
|
+
expect(subject.signature_algorithm).to be == 'sha256WithRSAEncryption'
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "#serial_number" do
|
300
|
+
it "should parse the serialNumber element" do
|
301
|
+
expect(subject.serial_number).to be == '0C009310D206DBE337553580118DDC87'
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "#subject" do
|
306
|
+
it "should return a Subject object" do
|
307
|
+
expect(subject.subject).to be_kind_of(described_class::Subject)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
describe "#validity" do
|
312
|
+
it "should return a Validity object" do
|
313
|
+
expect(subject.validity).to be_kind_of(described_class::Validity)
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should parse the validity/notAfter element" do
|
317
|
+
expect(subject.validity.not_after).to be == Date.parse('Apr 12 12:00:00 2016 GMT')
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should parse the validity/notBefore element" do
|
321
|
+
expect(subject.validity.not_before).to be == Date.parse('Apr 8 00:00:00 2014 GMT')
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "#issuer" do
|
326
|
+
it "should return an Issuer object" do
|
327
|
+
expect(subject.issuer).to be_kind_of(described_class::Issuer)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|