# frozen_string_literal: true # # Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com) # # Ronin is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ronin is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ronin. If not, see . # require 'ronin/cli/value_processor_command' require 'ronin/cli/host_and_port' require 'ronin/support/crypto/cert' require 'ronin/support/network/ssl' require 'command_kit/printing/indent' require 'command_kit/printing/fields' require 'command_kit/printing/lists' require 'uri' module Ronin class CLI module Commands # # Prints information for SSL/TLS certificates. # # ## Usage # # ronin cert-dump [options] {HOST:PORT | URL | FILE} ... # # ## Options # # -f, --file FILE Optional file to read values from # -C, --common-name Only prints the Common Name (CN) # -A, --subject-alt-names Only prints the subjectAltNames # -E, --extensions Print all certificate extensions # -h, --help Print help information # # ## Arguments # # HOST:PORT | URL | FILE ... A HOST:PORT, URL, or cert FILE # # ## Examples # # ronin cert-dump ssl.crt # ronin cert-dump github.com:443 # ronin cert-dump https://github.com/ # ronin cert-dump -C 93.184.216.34:443 # ronin cert-dump -A wired.com:443 # class CertDump < ValueProcessorCommand include CommandKit::Printing::Indent include CommandKit::Printing::Fields include CommandKit::Printing::Lists include HostAndPort command_name 'cert-dump' usage '[options] {HOST:PORT | URL | FILE} ...' option :common_name, short: '-C', desc: 'Only prints the Common Name (CN)' option :subject_alt_names, short: '-A', desc: 'Only prints the subjectAltNames' option :extensions, short: '-E', desc: 'Print all certificate extensions' argument :target, required: true, repeats: true, usage: 'HOST:PORT | URL | FILE', desc: 'A HOST:PORT, URL, or cert FILE' description "Prints SSL/TLS certificate information" examples [ 'ssl.crt', 'github.com:443', 'https://github.com/', '-C 93.184.216.34:443', '-A wired.com:443' ] man_page 'ronin-cert-dump.1' # # Runs the `ronin cert-dump` command. # # @param [String] value # The `HOST:PORT`, `URL`, or `FILE` value to process. # def process_value(value) case value when /\A[^:]+:\d+\z/ host, port = host_and_port(value) grab_cert(host,port) when /\Ahttps:/ host, port = host_and_port_from_url(value) grab_cert(host,port) else unless File.file?(value) print_error "no such file or directory: #{value}" exit(1) end cert = Support::Crypto::Cert.load_file(value) print_cert(cert) end end # # Gets the certs from the host and port, and then print it. # # @param [String] host # # @param [Integer] port # # @since 2.1.0 # def grab_cert(host,port) cert = Support::Network::SSL.get_cert(host,port) print_cert(cert) end # # Prints the certificate. # # @param [Ronin::Support::Crypto::Cert] cert # def print_cert(cert) if options[:common_name] puts "#{cert.common_name}" elsif options[:subject_alt_names] if (alt_names = cert.subject_alt_names) alt_names.each { |name| puts name } end else print_full_cert(cert) end end # # Prints the full verbose information about the certificate. # # @param [Ronin::Support::Crypto::Cert] cert # def print_full_cert(cert) fields = {} fields["Serial"] = cert.serial fields["Version"] = cert.version fields["Not Before"] = cert.not_before if cert.not_before fields["Not After"] = cert.not_after if cert.not_after print_fields(fields) puts print_public_key(cert.public_key) puts puts "Subject:" indent do print_cert_name(cert.subject) if (alt_names = cert.subject_alt_names) puts "Alt Names:" puts indent do alt_names.each { |name| puts name } end end end puts puts "Issuer:" indent do print_cert_name(cert.issuer) end puts if options[:extensions] puts "Extensions:" indent do print_extensions(cert) end end end # # Prints the public key. # # @param [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] public_key # def print_public_key(public_key) puts "Public Key:" indent do fields = {} case public_key when OpenSSL::PKey::RSA fields['Type'] = 'RSA' when OpenSSL::PKey::EC fields['Type'] = 'EC' end print_fields(fields) public_key.to_text.each_line do |line| puts line end end end # # Prints the X509 name. # # @param [Ronin::Support::Crypto::Cert::Name] name # def print_cert_name(name) fields = {} if name.common_name fields["Common Name"] = name.common_name end if name.organization fields["Organization"] = name.organization end if name.organizational_unit fields["Organizational Unit"] = name.organizational_unit end if name.locality fields["Locality"] = name.locality end if name.state fields["State"] = name.state end if name.country fields["Country"] = name.country end print_fields(fields) end # # Prints the certificates extensions. # # @param [Ronin::Support::Crypto::Cert] cert # def print_extensions(cert) cert.extensions.each_with_index do |ext,index| puts if index > 0 print_extension(ext) end end # # Prints a certificate extension. # # @param [OpenSSL::X509::Extension] ext # def print_extension(ext) puts "#{ext.oid}:" indent do ext.value.each_line do |line| puts line end end end end end end end