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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c4013d128b21d94004ca8c15715b85531acfe46f
|
4
|
+
data.tar.gz: bfcbc3a8309309dbc5e03986f153fdd1792174a3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce2fd295c02224d87157bedfec3cf4d63f4b0d9f9558a12fa00063c104c3eef986bfae25c2ec1378136a17db1fb4ff24589a0ad902df1591e80b95559a134662
|
7
|
+
data.tar.gz: 9d5cf5e4dca31f544e6cae4cdad4a7b09ba4a2c80350bdac8094e793f2626622d30c42489ae6baec63749ab7729ba66b11af36fa079a95b633b117425b21092f
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
language: ruby
|
2
|
+
sudo: false
|
3
|
+
rvm:
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0
|
6
|
+
- 2.1
|
7
|
+
- ruby-head
|
8
|
+
- jruby-19mode
|
9
|
+
- jruby-head
|
10
|
+
- rbx-2
|
11
|
+
matrix:
|
12
|
+
allow_failures:
|
13
|
+
- rvm: rbx-2
|
14
|
+
addons:
|
15
|
+
code_climate:
|
16
|
+
repo_token: 2a03fa37ce5a5cb21bb117a736be5d83dcf9f1c3ea2b248f7af4c0a7b330d8c8
|
17
|
+
notifications:
|
18
|
+
slack:
|
19
|
+
secure: IfKhtia5nM6KA9nK8jiSkNnVOLN96er6gK5jgjYKFNrVyWAKRUJZ0TB9L+igjUWDq7t+tRvj8yGT2k61xVJgF+ZDlQiWvyazTsgQeqbjieCxCrj/BTGZLyD1hhOLg7vqpyeQvp/34hDahx6XNp6XPvkxeofjc0H6STv2UjJkpQk=
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "ruby-sslyze Documentation" --protected
|
data/ChangeLog.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org/'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem 'rake'
|
7
|
+
gem 'rubygems-tasks', '~> 0.2'
|
8
|
+
|
9
|
+
gem 'rspec', '~> 3.0'
|
10
|
+
|
11
|
+
gem 'yard', '~> 0.8'
|
12
|
+
gem 'kramdown'
|
13
|
+
end
|
14
|
+
|
15
|
+
group :test do
|
16
|
+
gem 'json'
|
17
|
+
gem 'codeclimate-test-reporter', require: nil
|
18
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014-2015 Hal Brodigan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# ruby-sslyze
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/trailofbits/ruby-sslyze)
|
4
|
+
[](https://codeclimate.com/github/trailofbits/ruby-sslyze)
|
5
|
+
[](https://travis-ci.org/trailofbits/ruby-sslyze)
|
6
|
+
|
7
|
+
* [Homepage](https://github.com/trailofbits/ruby-sslyze#readme)
|
8
|
+
* [Issues](https://github.com/trailofbits/ruby-sslyze/issues)
|
9
|
+
* [Documentation](http://rubydoc.info/gems/ruby-sslyze/frames)
|
10
|
+
* [Email](mailto:hal at trailofbits.com)
|
11
|
+
|
12
|
+
## Description
|
13
|
+
|
14
|
+
A Ruby interface to [sslyze] python utility.
|
15
|
+
|
16
|
+
## Features
|
17
|
+
|
18
|
+
* Provides a Ruby interface to `sslyze.py`.
|
19
|
+
* Provides a Parser for consuming the sslyze XML output.
|
20
|
+
* [sslyze] >= 0.12
|
21
|
+
|
22
|
+
## Examples
|
23
|
+
|
24
|
+
Analyze a domain:
|
25
|
+
|
26
|
+
require 'sslyze'
|
27
|
+
|
28
|
+
SSLyze::Program.analyze(targets: 'twitter.com', regular: true, timeout: 5)
|
29
|
+
|
30
|
+
Analyze multiple domains:
|
31
|
+
|
32
|
+
SSLyze::Program.analyze(
|
33
|
+
targets: ['twitter.com', 'github.com'],
|
34
|
+
regular: true,
|
35
|
+
timeout: 5
|
36
|
+
)
|
37
|
+
|
38
|
+
Output to XML:
|
39
|
+
|
40
|
+
SSLyze::Program.analyze(
|
41
|
+
targets: 'twitter.com',
|
42
|
+
regular: true,
|
43
|
+
timeout: 5,
|
44
|
+
xml_out: 'path/to/xml'
|
45
|
+
)
|
46
|
+
|
47
|
+
Parsing sslyze XML output:
|
48
|
+
|
49
|
+
xml = SSLyze::XML.open('path/to/xml')
|
50
|
+
|
51
|
+
## Requirements
|
52
|
+
|
53
|
+
* [rprogram] ~> 0.3
|
54
|
+
* [nokogiri] ~> 1.0
|
55
|
+
* [sslyze] >= 0.12
|
56
|
+
|
57
|
+
## Install
|
58
|
+
|
59
|
+
$ gem install ruby-sslyze
|
60
|
+
|
61
|
+
## Copyright
|
62
|
+
|
63
|
+
Copyright (c) 2014 Hal Brodigan
|
64
|
+
|
65
|
+
See {file:LICENSE.txt} for details.
|
66
|
+
|
67
|
+
[sslyze]: https://github.com/nabla-c0d3/sslyze#readme
|
68
|
+
|
69
|
+
[rpgoram]: https://github.com/postmodern/rprogram#readme
|
70
|
+
[nokogiri]: http://www.nokogiri.org/
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError => e
|
4
|
+
abort e.message
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rake'
|
8
|
+
require 'rubygems/tasks'
|
9
|
+
Gem::Tasks.new
|
10
|
+
|
11
|
+
require 'rspec/core/rake_task'
|
12
|
+
RSpec::Core::RakeTask.new
|
13
|
+
|
14
|
+
task :test => :spec
|
15
|
+
task :default => :spec
|
16
|
+
|
17
|
+
require 'yard'
|
18
|
+
YARD::Rake::YardocTask.new
|
19
|
+
task :doc => :yard
|
20
|
+
|
21
|
+
file 'spec/sslyze.xml' do
|
22
|
+
sh 'sslyze.py --xml_out spec/sslyze.xml --regular --timeout 5 twitter.com github.com:443 yahoo.com:443'
|
23
|
+
end
|
data/lib/sslyze.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'sslyze/certificate_chain'
|
2
|
+
require 'sslyze/certificate_validation'
|
3
|
+
require 'sslyze/ocsp_response'
|
4
|
+
|
5
|
+
module SSLyze
|
6
|
+
#
|
7
|
+
# Represents the `<certinfo>` element.
|
8
|
+
#
|
9
|
+
class CertInfo
|
10
|
+
|
11
|
+
#
|
12
|
+
# Initializes the cert info.
|
13
|
+
#
|
14
|
+
# @param [Nokogiri::XML::Node] node
|
15
|
+
# The `<certinfo>` element.
|
16
|
+
#
|
17
|
+
def initialize(node)
|
18
|
+
@node = node
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Certificate chain.
|
23
|
+
#
|
24
|
+
# @return [CertificateChain, nil]
|
25
|
+
#
|
26
|
+
def chain
|
27
|
+
@chain ||= if (cert_chain = @node.at('certificateChain'))
|
28
|
+
CertificateChain.new(cert_chain)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Certificate validation information.
|
34
|
+
#
|
35
|
+
# @return [CertificateValidation]
|
36
|
+
#
|
37
|
+
def validation
|
38
|
+
@validation ||= CertificateValidation.new(@node.at('certificateValidation'))
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# OCSP response stapling information.
|
43
|
+
#
|
44
|
+
# @return [OCSPResponse, nil]
|
45
|
+
#
|
46
|
+
def ocsp_response
|
47
|
+
@ocsp_response ||= if (ocsp_response = @node.at('ocspStapling/ocspResponse'))
|
48
|
+
OCSPResponse.new(ocsp_response)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
alias ocsp_stapling ocsp_response
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'sslyze/certificate/subject_public_key_info'
|
2
|
+
require 'sslyze/certificate/extensions'
|
3
|
+
require 'sslyze/certificate/subject'
|
4
|
+
require 'sslyze/certificate/validity'
|
5
|
+
require 'sslyze/certificate/issuer'
|
6
|
+
|
7
|
+
require 'date'
|
8
|
+
|
9
|
+
module SSLyze
|
10
|
+
#
|
11
|
+
# Represents the `<certificate>` XML element.
|
12
|
+
#
|
13
|
+
class Certificate
|
14
|
+
|
15
|
+
#
|
16
|
+
# Initializes the certificate.
|
17
|
+
#
|
18
|
+
# @param [Nokogiri::XML::Node] node
|
19
|
+
# The `<certificate>` XML element.
|
20
|
+
#
|
21
|
+
def initialize(node)
|
22
|
+
@node = node
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# The position of the certificate within the cert chain.
|
27
|
+
#
|
28
|
+
# @return [:leaf, :intermediate]
|
29
|
+
#
|
30
|
+
def position
|
31
|
+
@position ||= @node['position'].to_sym
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# The SHA1 fingerprint of the cert.
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
#
|
39
|
+
def sha1_fingerprint
|
40
|
+
@sha1_fingerprint ||= @node['sha1Fingerprint']
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# The AS PEM information.
|
45
|
+
#
|
46
|
+
# @return [String]
|
47
|
+
#
|
48
|
+
def as_pem
|
49
|
+
@as_pem ||= @node.at('asPEM').inner_text
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# The subject public key information.
|
54
|
+
#
|
55
|
+
# @return [SubjectPublicKeyInfo]
|
56
|
+
#
|
57
|
+
def subject_public_key_info
|
58
|
+
@subject_public_key_info ||= SubjectPublicKeyInfo.new(
|
59
|
+
@node.at('subjectPublicKeyInfo')
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# The certificate SSL version.
|
65
|
+
#
|
66
|
+
# @return [Integer]
|
67
|
+
#
|
68
|
+
def version
|
69
|
+
@version ||= @node.at('version').inner_text.to_i
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# The SSL extensions.
|
74
|
+
#
|
75
|
+
# @return [Extensions]
|
76
|
+
#
|
77
|
+
def extensions
|
78
|
+
@extensions ||= Extensions.new(@node.at('extensions'))
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# The certificate signature.
|
83
|
+
#
|
84
|
+
# @return [String]
|
85
|
+
#
|
86
|
+
def signature_value
|
87
|
+
@signature_value ||= @node.at('signatureValue').inner_text
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# The certificate signature algorithm.
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
#
|
95
|
+
def signature_algorithm
|
96
|
+
@signature_algorithm ||= @node.at('signatureAlgorithm').inner_text
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# The certificate serial number.
|
101
|
+
#
|
102
|
+
# @return [String]
|
103
|
+
#
|
104
|
+
def serial_number
|
105
|
+
@serial_number ||= @node.at('serialNumber').inner_text
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# The certificate subject information.
|
110
|
+
#
|
111
|
+
# @return [Subject]
|
112
|
+
#
|
113
|
+
def subject
|
114
|
+
@subject ||= Subject.new(@node.at('subject'))
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# The certificate validity.
|
119
|
+
#
|
120
|
+
# @return [Validity]
|
121
|
+
#
|
122
|
+
def validity
|
123
|
+
@validity ||= Validity.new(
|
124
|
+
Date.parse(@node.at('validity/notAfter').inner_text),
|
125
|
+
Date.parse(@node.at('validity/notBefore').inner_text)
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# The certificate issuer.
|
131
|
+
#
|
132
|
+
# @return [Issuer]
|
133
|
+
#
|
134
|
+
def issuer
|
135
|
+
@issuer ||= Issuer.new(@node.at('issuer'))
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module SSLyze
|
2
|
+
class Certificate
|
3
|
+
#
|
4
|
+
# Represents a domain name pattern.
|
5
|
+
#
|
6
|
+
class DomainName
|
7
|
+
|
8
|
+
# The subject name.
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# The domain part of the subject name.
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
attr_reader :domain
|
17
|
+
|
18
|
+
# The literal suffix of the subject name.
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
attr_reader :suffix
|
22
|
+
|
23
|
+
#
|
24
|
+
# Initializes the subject name.
|
25
|
+
#
|
26
|
+
# @param [String] name
|
27
|
+
# The subject name.
|
28
|
+
#
|
29
|
+
def initialize(name)
|
30
|
+
@name = name
|
31
|
+
|
32
|
+
if @name.start_with?('*.')
|
33
|
+
@suffix = @name[1..-1]
|
34
|
+
@domain = @name[2..-1]
|
35
|
+
else
|
36
|
+
@domain = @name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Compares two subject names.
|
42
|
+
#
|
43
|
+
# @return [Boolean]
|
44
|
+
#
|
45
|
+
def ==(other)
|
46
|
+
@name == other.name
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Tests whether the domain is matched by the subject name.
|
51
|
+
#
|
52
|
+
def include?(domain)
|
53
|
+
if @name.start_with?('*.') # wildcard
|
54
|
+
domain.end_with?(@suffix) || # does the domain share the suffix
|
55
|
+
domain == @domain # does the domain match the suffix
|
56
|
+
else # exact match
|
57
|
+
domain == @name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
alias === include?
|
62
|
+
|
63
|
+
alias to_s name
|
64
|
+
alias to_str name
|
65
|
+
|
66
|
+
#
|
67
|
+
# Inspects the subject name.
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
#
|
71
|
+
def inspect
|
72
|
+
"#<#{self.class}: #{self}>"
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|