« Fix for Firefox 22 & CA ServiceDesk Issues | Main | Apache HTTP Server 2.4.6 Released »

07/20/2013

Here's a New Version of skeypatrol.pl

A couple weeks ago I released a small Perl script named skeypatrol.pl that let you know the size of the public key embedded inside your site's ssl certificate. I released this because at the end of the year, CA's are going to be revoking certificates that have key lengths below 2048 bits and an easy way to determine the size of the key was not so easy to find. Today, I'm releasing verion 0.2 of this script that adds a useful batch mode feature allowing you to scan a bunch of hostnames and ports stored in a CSV file. You'll need cpan module Text::CSV to run this version in addition to the other required modules.

You'll need to create the CSV file yourself but the syntax of the file's entries couldn't be easier. Just create the file with the hostname followed by the port number the web site listens on separated by a comma. One host and port combination per line.

Other changes in this version from the previous version is that I've removed the -i/--insecure switch. The assumption here is that if you are running this script, I'm assuming that you are the administrator of the web sites that are being scanned and if you don't know the hostname that corresponds with the common name on the cert, well, let's just say that you should. SSL is a pain but it is not that painful! Since peer verification is assumed, the new version of skeypatrol.pl no longer outputs that certificate peer validation was successful. The script will still halt if peer vaidation fails, so make sure your csv entries are valid and make sure they are all reachable from the network you are scanning from.

#!/usr/bin/env perl

use Modern::Perl;
use Term::ANSIColor qw(:constants);
use IO::Socket::SSL 1.33 'debug1';
use Mozilla::CA;
use Text::CSV;
use Getopt::Long;
use Data::Dumper;

my $scriptname = "skeypatrol.pl";
my $version = "v0.2";
my $host;
my $port;
my $file;
my $verifymode = "SSL_VERIFY_PEER";
my $pkeybits;
my $sslclient;
my $x509;

# Command Line Usage() subroutine
sub usage {
    say "Usage: $scriptname --host <host> --port <port> --OR-- --file <csv_file>\n\t\t-h|--host\tSSL host or common name \n\t\t-p|--port\tPort number site listens on (e.g. 443\n\t\t-f|--file\tOpen a CSV file to scan many hosts at once)";
    exit;
    }

#Execute usage unless certain arguments are provided
usage() if ( ! GetOptions(
        "h|host=s" => \$host, 
        "p|port=s" => \$port, 
        "f|file=s" => \$file, 
        ));

if ( $file ) {
        parse_csv();
    } else {
        ssl_client();
        get_key_size();
        key_length();
}

sub parse_csv{
    my $csv = Text::CSV->new( { sep_char => ',', eol => $/ } ) or die "Can not use CSV: ".Text::CSV->error_diag ();
    open my $fh, '<', $file or die "Can not open file $!";

    while ( <$fh> ) {
        if ($csv->parse($_)) {
            my @columns = $csv->fields();
            $host = $columns[0];
            $port = $columns[1];
        } else {
            my $err = $csv->error_input;
            say "Failed to parse line: $err";
        }
        foreach ($host) { 
        ssl_client();
        get_key_size();
        key_length();
        };

    }
    close $fh

}

sub key_length {
    if ( $pkeybits lt 2048 ) {
        print RED, "Certificate for $host uses a $pkeybits bit key\n", RESET;
    }
    else {
        print GREEN, "Certificate for $host uses a $pkeybits bit key\n", RESET;
    }
}

sub ssl_client {
    $sslclient = IO::Socket::SSL->new(
        PeerAddr => $host,
        PeerPort => $port,
        SSL_ca_file => Mozilla::CA::SSL_ca_file(),
        SSL_verify_mode => $verifymode,
        SSL_version => 'TLSv1',
        SSL_cipher => 'RC4-SHA',
        Proto => 'tcp',
        Timeout => '15'
        )
            ||die("Certificate Peer Verification Failed for host: $host.\n$!,$SSL_ERROR");
        $sslclient->verify_hostname($host, "http")
            ||die("Hostname verification failed.\n$host does not match common name on certificate.");
        
        $x509 = get_cert($sslclient);

}

sub get_cert {
    my $sslclient = shift()->_get_ssl_object || return;
    return Net::SSLeay::get_peer_certificate($sslclient);
}

sub get_key_size {
    $pkeybits = Net::SSLeay::EVP_PKEY_bits(Net::SSLeay::X509_get_pubkey($x509));
}

#Begin perldoc

__END__
=pod

=head1 TITLE

skeypatrol.pl

=head1 VERSION

Version 0.2

=head1 DATE

June 24, 2013

=head1 AUTHOR

Chris Mahns Contact me at: techstacks [at] gmail [dot] com or follow me on Twitter: @techstacks

=head1 DESCRIPTION

skeypatrol.pl was written because I wanted to know whether the public key at the end of the ssl connection was 1024 bits or not. This is useful in case you're looking to know what ssl certificates you need to replace before they stop working on January 1, 2014.

=head1 USAGE

skeypatrol.pl --host <host_to_scan> --port <port_number> --OR-- --file <csv_file>

Both parameters are required in this version and I assume that the script is marked user-executable.

=head1 DEPENDENCIES

CPAN modules Modern::Perl, Term::ANSIColor, IO::Socket::SSL, Getopt::Long, and Mozilla::CA are used. If you have already used cryptonark, you've already got these.

=head1 VERSION HISTORY

=head2 VERSION 0.1

+ Initial release.

=head2 VERSION 0.2

+ Batch mode. Scan a bunch of hosts from a csv file. It needs to be a very basic CSV consisting of hostname and port, separated by a comma.

+ Removed -i|--insecure switch. Disabing peer verification isn't very responsible.

+ Since the insecure switch was removed and it is assumed you know the hostnames and ports for the sites you are scanning, printing a message indicating that certificate peer validation was successful was no longer necessary.

=cut

TrackBack

TrackBack URL for this entry:
https://www.typepad.com/services/trackback/6a01156fbc6fe6970c0192ac1cfc0f970d

Listed below are links to weblogs that reference Here's a New Version of skeypatrol.pl:

Comments