ruby-sslyze 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|