Created
January 30, 2020 23:12
-
-
Save drbrain/e5b7480c98c0cd95d139460710ff652d to your computer and use it in GitHub Desktop.
Ruby TLS tools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
## | |
# Use this tool to extract X509 certificates from an HTTP server you want to | |
# connect to that is using a self-signed certificate. This is a | |
# Trust-on-First-Use operation so you are still at risk of various attacks | |
# including man-in-the-middle. | |
# | |
# This does allow you to uniformly use VERIFY_PEER across all HTTPS | |
# connections, as you now will be verifying the possibly-suspect connection | |
# using the self-signed certificate along with all others. | |
# | |
# To use this script first save the output of this script to "certs.pem". | |
# | |
# Then in your HTTP program load the "certs.pem": | |
# | |
# store = OpenSSL::X509::Store.new | |
# store.set_default_paths # optional, if you are connecting to servers | |
# # with and without self-signed certificates | |
# store.add_cert "/path/to/certs.pem" | |
# | |
# With Net::HTTP: | |
# | |
# http = Net::HTTP.new hostname, port | |
# http.use_ssl = true | |
# http.cert_store = store | |
# http.verify_mode = OpenSSL::SSL::VERIFY_PEER | |
# # … other setup | |
# http.start | |
# # … use connection | |
# | |
# With Net::HTTP::Persistent | |
# | |
# http = Net::HTTP::Persistent.new | |
# http.cert_store = store | |
# # … use connection | |
# | |
# With Mechanize: | |
# | |
# mechanize = Mechanize.new | |
# mechanize.cert_store = store | |
# # … use connection | |
require "openssl" | |
require "socket" | |
require "uri" | |
abort "usage: #$0 URI" if ARGV.empty? | |
uri = URI.parse ARGV.shift | |
abort "You must use an https URI" unless uri.scheme.downcase == "https" | |
certificates = [] | |
context = OpenSSL::SSL::SSLContext.new | |
# This records the certificates presented by the server and marks them OK so | |
# we can see all the rest. | |
context.verify_callback = proc do |preverify_ok, store_context| | |
certificate = store_context.current_cert | |
certificates << certificate | |
true | |
end | |
socket = TCPSocket.new uri.hostname, uri.port | |
ssl_socket = OpenSSL::SSL::SSLSocket.new socket, context | |
# for Server name indication | |
ssl_socket.hostname = uri.hostname | |
# Connect and close to fetch certificates but do nothing else | |
ssl_socket.connect | |
ssl_socket.close | |
certificates.each do |certificate| | |
subject = certificate.subject | |
_, name, = subject.to_a.find { |name,| | |
name == "CN" | |
} | |
name ||= subject | |
alt_name = certificate.extensions.find { |extension| | |
extension.oid == "subjectAltName" | |
} | |
# Print canonical name and hostnames (if any) this certificate is supposed | |
# to work with, the subject alone if none are detected. | |
puts "# #{name}" | |
puts "# #{alt_name.value}" if alt_name | |
puts certificate.to_pem | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment