Get certificate fingerprint in golang
Fingerprint is a great way to get a "hash" for a specific version of certificate. Openssl provides a -fingerprint
option to get that hash.
➤ echo | openssl s_client -connect abhi.host:443 -servername abhi.host 2>&1| openssl x509 -noout -fingerprint -md5
MD5 Fingerprint=82:D4:F7:0C:EB:F4:A9:A4:AD:00:11:9E:CC:D4:64:60
➤
From the manpage:-
-fingerprint
Calculates and outputs the digest of the DER encoded version of the entire certificate (see digest options). This is commonly called a "fingerprint". Because of the nature of message digests, the fingerprint of a certificate is unique to that certificate and two certificates with the same fingerprint can be considered to be the same.
So, we need to get the DER (Distinguised Encoding Rules) encoded bytes and use that as the data to get the md5 hash.
From the Golang docs, https://golang.org/pkg/crypto/x509/#Certificate,
type Certificate struct {
Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
Perfect, Raw
field in x509.Certificate
provides the DER content we want.
- To create a TLS connection, we'll be using tls.Dial.
- That returns a tls.ConnectionState.
- Under tls.ConnectionState,
PeerCertificates
gives the certificates for that TLS connection.
Here's the full code to get the fingerprint from a live endpoint.
package main
import (
"bytes"
"crypto/md5"
"crypto/tls"
"flag"
"fmt"
)
func main() {
// Parse cmdline arguments using flag package
server := flag.String("server", "abhi.host", "Server to ping")
port := flag.Uint("port", 443, "Port that has TLS")
flag.Parse()
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", *server, *port), &tls.Config{})
if err != nil {
panic("failed to connect: " + err.Error())
}
// Get the ConnectionState struct as that's the one which gives us x509.Certificate struct
cert := conn.ConnectionState().PeerCertificates[0]
fingerprint := md5.Sum(cert.Raw)
var buf bytes.Buffer
for i, f := range fingerprint {
if i > 0 {
fmt.Fprintf(&buf, ":")
}
fmt.Fprintf(&buf, "%02X", f)
}
fmt.Printf("Fingerprint for %s: %s", *server, buf.String())
conn.Close()
}
Usage:-
➤ go run main.go
Fingerprint for abhi.host: 82:D4:F7:0C:EB:F4:A9:A4:AD:00:11:9E:CC:D4:64:60
➤