ruby-sslyze 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +4 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +19 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +8 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +70 -0
  11. data/Rakefile +23 -0
  12. data/lib/sslyze.rb +3 -0
  13. data/lib/sslyze/cert_info.rb +55 -0
  14. data/lib/sslyze/certificate.rb +139 -0
  15. data/lib/sslyze/certificate/domain_name.rb +77 -0
  16. data/lib/sslyze/certificate/extensions.rb +127 -0
  17. data/lib/sslyze/certificate/extensions/authority_information_access.rb +38 -0
  18. data/lib/sslyze/certificate/extensions/extension.rb +26 -0
  19. data/lib/sslyze/certificate/extensions/x509v3_basic_constraints.rb +60 -0
  20. data/lib/sslyze/certificate/extensions/x509v3_certificate_policies.rb +50 -0
  21. data/lib/sslyze/certificate/extensions/x509v3_crl_distribution_points.rb +32 -0
  22. data/lib/sslyze/certificate/extensions/x509v3_extended_key_usage.rb +32 -0
  23. data/lib/sslyze/certificate/extensions/x509v3_key_usage.rb +50 -0
  24. data/lib/sslyze/certificate/extensions/x509v3_subject_alternative_name.rb +71 -0
  25. data/lib/sslyze/certificate/issuer.rb +56 -0
  26. data/lib/sslyze/certificate/public_key.rb +9 -0
  27. data/lib/sslyze/certificate/subject.rb +117 -0
  28. data/lib/sslyze/certificate/subject_public_key_info.rb +53 -0
  29. data/lib/sslyze/certificate/validity.rb +9 -0
  30. data/lib/sslyze/certificate_chain.rb +89 -0
  31. data/lib/sslyze/certificate_validation.rb +44 -0
  32. data/lib/sslyze/cipher_suite.rb +237 -0
  33. data/lib/sslyze/key_exchange.rb +106 -0
  34. data/lib/sslyze/ocsp_response.rb +87 -0
  35. data/lib/sslyze/program.rb +66 -0
  36. data/lib/sslyze/protocol.rb +133 -0
  37. data/lib/sslyze/target.rb +295 -0
  38. data/lib/sslyze/task.rb +65 -0
  39. data/lib/sslyze/types.rb +17 -0
  40. data/lib/sslyze/version.rb +4 -0
  41. data/lib/sslyze/xml.rb +139 -0
  42. data/ruby-sslyze.gemspec +24 -0
  43. data/spec/cert_info_spec.rb +29 -0
  44. data/spec/certificate/subject_name_spec.rb +72 -0
  45. data/spec/certificate_chain_spec.rb +61 -0
  46. data/spec/certificate_spec.rb +330 -0
  47. data/spec/certificate_validation_spec.rb +27 -0
  48. data/spec/cipher_suite_spec.rb +50 -0
  49. data/spec/issuer_spec.rb +33 -0
  50. data/spec/key_exchange_spec.rb +97 -0
  51. data/spec/ocsp_response_spec.rb +59 -0
  52. data/spec/protocol_spec.rb +99 -0
  53. data/spec/spec_helper.rb +9 -0
  54. data/spec/sslyze.xml +2798 -0
  55. data/spec/sslyze_spec.rb +8 -0
  56. data/spec/subject_public_key_info_spec.rb +35 -0
  57. data/spec/subject_spec.rb +67 -0
  58. data/spec/target_spec.rb +176 -0
  59. data/spec/xml_examples.rb +9 -0
  60. data/spec/xml_spec.rb +72 -0
  61. metadata +162 -0
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ module SSLyze
2
+ # ruby-sslyze version
3
+ VERSION = "0.1.0"
4
+ 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
@@ -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