Extracting SSL Fingerprints: My Custom Scripts
I was looking for a script that can extract the SSL fingerprint from any SSL certificate, given you have the URL. I found a few scripts, but none of them did exactly what I expected, so I decided to write my own based on what I found. I created three versions of the script: one in Perl, one in Linux Shell scripting, and one in PHP. Note that all of them perform the same task and produce the same results.
The next plan was to develop an automated standalone application that could perform this task from multiple locations automatically (SSLEYE). It’s important to understand that secure browser connections can be intercepted and decrypted by anyone (Man-in-the-Middle, MITM) who could spoof the authentic site’s certificate and act on your behalf. This also allows them to read your traffic in clear text. Fortunately, the authentic site’s fingerprint cannot be duplicated, which is the main advantage of having such an application.
Perl script:
#!/usr/bin/perl
# Perl SSL Fingerprint Checker written by W. Al Maawali
# (c) 2013 Founder of Eagle Eye Digital Solutions
# https://www.digi77.com
# http://www.om77.net
# script starts here:
# Usage: $perl sslf.pl -d yourdomain
# Example: $perl sslf.pl -d google.com
#libs used
use Net::SSLeay qw(get_https3);
use Getopt::Std;
# get args
getopts("o:i:d:s:e:hvb", \%args);
# set our input vars to easy names
$domain = $args{d};
# gotta have at least the domain and log file
if (!$args{d}) {
print "\t Domain is blank google.com will be used\n\n";
$domain ="google.com";
}
$host= $domain;
$port = 443;
($p, $resp, $hdrs, $server_cert) = get_https3($host, $port, '/');
#get finger print
print Net::SSLeay::X509_get_fingerprint($server_cert, "sha1");
print "\n";
PerlShell script:
#!/bin/bash
# =========================================
# SSL Fingerprint Checker
# =========================================
#
# Version: 1.5
# Script written by Warith Al Maawali
#
# Discord channel: https://discord.gg/KEFErEx
# Twitter: http://twitter.com/warith2020
# Linkedin: http://www.linkedin.com/in/warith1977
# Website: https://www.digi77.com
# (c) 2024
#
# Description:
# This script retrieves the SSL fingerprint for one or multiple domains.
# It supports reading domains from command-line arguments or from a file (.txt or .csv).
# Includes fallback mechanisms and improved error handling for stability.
# Supports output in text or JSON format via the -o option without altering terminal output.
#
# Usage: ./sslfp.sh [options]
#
# Usage Examples:
# Check a Single Domain:
# ./sslfp.sh -d google.com
# Check Multiple Domains via Command-Line:
# ./sslfp.sh -d google.com -d example.com -d github.com
# Check Multiple Domains from a File:
# Create a domains.txt file:
# google.com
# example.com
# github.com
#
# Run the script:
# ./sslfp.sh -f domains.txt
# Output in JSON format to a file:
# ./sslfp.sh -d google.com -o json
# Display Help:
# ./sslfp.sh -h
# =========================================
# Enable debug mode (uncomment the next line for debugging)
# set -x
# Constants
SSL_PORT=443
RETRY_COUNT=3
TIMEOUT=15
DEFAULT_OUTPUT_FORMAT="text"
# Colors for terminal output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# Declare domains array globally
declare -a domains=()
# Functions
# Display usage instructions
usage() {
cat <<EOF
Usage: $0 [options]
Options:
-d DOMAIN Specify a single domain to check. Can be used multiple times.
-f FILE Specify a file containing a list of domains (one per line).
-o FORMAT Specify output format for the file: 'text' or 'json'. Defaults to 'text'.
-h, --help Display this help message.
Examples:
$0 -d google.com
$0 -f domains.txt
$0 -d google.com -o json
EOF
exit 1
}
# Check if openssl is installed, if not, install it
install_openssl() {
echo -e "${YELLOW}openssl is not installed. Installing now...${NC}"
if command -v apt-get &>/dev/null; then
sudo apt-get update
sudo apt-get install -y openssl
elif command -v yum &>/dev/null; then
sudo yum install -y openssl
else
echo -e "${RED}Package manager not supported. Please install openssl manually.${NC}"
exit 1
fi
if ! command -v openssl &>/dev/null; then
echo -e "${RED}Failed to install openssl. Please install it manually and run this script again.${NC}"
exit 1
fi
}
# Function to fetch SSL fingerprint using openssl
get_fingerprint_openssl() {
local domain="$1"
local port="$2"
local fingerprint
fingerprint=$(timeout "${TIMEOUT}" bash -c "echo | openssl s_client -connect '${domain}:${port}' -servername '${domain}' 2>/dev/null" |
openssl x509 -noout -fingerprint 2>/dev/null | cut -d'=' -f2)
echo "$fingerprint"
}
# Function to fetch SSL fingerprint with retry mechanism
get_fingerprint() {
local domain="$1"
local port="$2"
local attempt=1
local fingerprint
while [ "$attempt" -le "$RETRY_COUNT" ]; do
fingerprint=$(get_fingerprint_openssl "$domain" "$port")
if [ -n "$fingerprint" ]; then
echo "$fingerprint"
return 0
else
echo -e "${YELLOW}Attempt $attempt: Failed to retrieve fingerprint for $domain. Retrying...${NC}" >&2
attempt=$((attempt + 1))
sleep 1
fi
done
return 1
}
# Parse command-line arguments
parse_arguments() {
if [ "$#" -eq 0 ]; then
usage
fi
OUTPUT_FORMAT="$DEFAULT_OUTPUT_FORMAT"
while [[ "$#" -gt 0 ]]; do
case "$1" in
-d)
if [ -n "$2" ] && [[ ! "$2" =~ ^- ]]; then
domains+=("$2")
shift 2
else
echo -e "${RED}ERROR: -d requires a non-empty argument.${NC}"
usage
fi
;;
-f)
if [ -n "$2" ] && [[ ! "$2" =~ ^- ]]; then
if [ -f "$2" ]; then
while IFS= read -r line || [ -n "$line" ]; do
# Trim whitespace
line=$(echo "$line" | xargs)
# Skip empty lines and comments
[[ -z "$line" || "$line" =~ ^# ]] && continue
domains+=("$line")
done < "$2"
else
echo -e "${RED}ERROR: File '$2' not found.${NC}"
exit 1
fi
shift 2
else
echo -e "${RED}ERROR: -f requires a valid file path.${NC}"
usage
fi
;;
-o)
if [ -n "$2" ] && [[ ! "$2" =~ ^- ]]; then
if [[ "$2" == "text" || "$2" == "json" ]]; then
OUTPUT_FORMAT="$2"
else
echo -e "${RED}ERROR: Invalid output format. Use 'text' or 'json'.${NC}"
usage
fi
shift 2
else
echo -e "${RED}ERROR: -o requires a valid format (text or json).${NC}"
usage
fi
;;
-h|--help)
usage
;;
*)
echo -e "${RED}ERROR: Unknown option: $1${NC}"
usage
;;
esac
done
# Remove duplicate domains
unique_domains=($(printf "%s\n" "${domains[@]}" | sort -u))
domains=("${unique_domains[@]}")
# Check if domains are provided
if [ "${#domains[@]}" -eq 0 ]; then
echo -e "${RED}ERROR: No domains provided.${NC}"
usage
fi
# Debugging statement (optional)
# echo "DEBUG: OUTPUT_FORMAT is '$OUTPUT_FORMAT'"
}
# Output results to terminal with colors
output_to_terminal() {
local domains_ref=("${!1}")
declare -n results_ref=$2
# Output header
echo -e "${GREEN}Domain SSL Fingerprint${NC}"
echo -e "${GREEN}------ --------------${NC}"
for domain in "${domains_ref[@]}"; do
fingerprint="${results_ref[$domain]}"
if [ "$fingerprint" != "FAILED" ]; then
printf "%-30s ${GREEN}%-20s${NC}\n" "$domain" "$fingerprint"
else
printf "%-30s ${RED}%-20s${NC}\n" "$domain" "$fingerprint"
fi
done
}
# Save results to a file in specified format
save_to_file() {
local domains_ref=("${!1}")
declare -n results_ref=$2
local format="$3"
timestamp=$(date +%Y%m%d_%H%M%S)
if [ "$format" == "json" ]; then
output_file="ssl_fingerprints_${timestamp}.json"
{
echo "{"
echo ' "domains": ['
total=${#domains_ref[@]}
for ((i=0; i < total; i++)); do
domain="${domains_ref[$i]}"
fingerprint="${results_ref[$domain]}"
echo " {"
echo " \"domain\": \"${domain}\","
if [ "$fingerprint" != "FAILED" ]; then
echo " \"fingerprint\": \"${fingerprint}\""
else
echo " \"fingerprint\": \"FAILED\""
fi
if [ "$i" -lt "$((total - 1))" ]; then
echo " },"
else
echo " }"
fi
done
echo ' ]'
echo "}"
} > "$output_file"
elif [ "$format" == "text" ]; then
output_file="ssl_fingerprints_${timestamp}.txt"
{
echo "Domain SSL Fingerprint"
echo "------ --------------"
for domain in "${domains_ref[@]}"; do
fingerprint="${results_ref[$domain]}"
printf "%-30s %-20s\n" "$domain" "$fingerprint"
done
} > "$output_file"
else
echo -e "${RED}ERROR: Unsupported format '$format'. Use 'text' or 'json'.${NC}"
exit 1
fi
echo -e "\n${GREEN}Results saved to $output_file${NC}"
}
# Main Execution
# Check if openssl is installed
if ! command -v openssl &>/dev/null; then
install_openssl
fi
# Parse arguments and set global variables
parse_arguments "$@"
# Debugging statement (optional)
# echo "DEBUG: Parsed domains: ${domains[@]}"
# echo "DEBUG: Output format: $OUTPUT_FORMAT"
# Declare an associative array to store results
declare -A results
# Fetch fingerprints
for domain in "${domains[@]}"; do
fingerprint=$(get_fingerprint "$domain" "$SSL_PORT")
if [ "$?" -eq 0 ]; then
results["$domain"]="$fingerprint"
else
results["$domain"]="FAILED"
fi
done
# Output to terminal
output_to_terminal domains[@] results
# Save results to a file in the specified format
save_to_file domains[@] results "$OUTPUT_FORMAT"
ShellScriptPHP script:
<?php
# PHP SSL Fingerprint Checker written by W. Al Maawali
# (c) 2013 Founder of Eagle Eye Digital Solutions
# https://www.digi77.com
# http://www.om77.net
# script starts here:
# Usage: http://www.yourdomain.com/sslf.php
# Example: https://www.digi77.com/software/fingerprint/fp-public.php?hosts=www.facebook.com
//avoid timeouts
set_time_limit(0);
//For String variable use prevent sql injections
function StringInputCleaner($data)
{
$data = trim($data);
$data = stripslashes($data);
$data=(filter_var($data, FILTER_SANITIZE_STRING));
return $data;
}
function getSllCertificate($hostname, $port = 443)
{
$context = stream_context_create(array("ssl" => array("capture_peer_cert" => true)));
$socket = @stream_socket_client("ssl://$hostname:$port", $errno, $errstr, ini_get("default_socket_timeout"), STREAM_CLIENT_CONNECT, $context);
if(!$socket)
return array("md5" => "error", "sha1" => "error");
$contextdata = stream_context_get_params($socket);
$contextparams = $contextdata['options']['ssl']['peer_certificate'];
fclose($socket);
openssl_x509_export($contextparams, $cert, true);
openssl_x509_free($contextparams);
$repl = array("\r", "\n", "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
$repw = array("", "", "", "");
$cert = str_replace($repl, $repw, $cert);
$decoded = base64_decode($cert);
$fingerprints = array(
"md5" => md5($decoded),
"sha1" => sha1($decoded),
);
return $fingerprints ;
}
$host=$_REQUEST['hosts'];
//clean string safer coding
$host=StringInputCleaner($host);
$port=443;
$hashes = getSllCertificate($host, $port);
print_r($hashes['sha1']);
?>
PHP