GO
main.go
package main
import (
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"reflect"
"time"
)
func loggedCloser(c io.Closer) {
if err := c.Close(); err != nil {
log.Fatal(err)
}
}
func getCertificateChainHashesApi(domain string) (error, []string) {
//goland:noinspection SpellCheckingInspection
type CertificateInChain struct {
CertificatePem string `json:"certificate_pem"`
Pem struct {
Hashes struct {
Sha265 string `json:"sha256"`
} `json:"hashes"`
} `json:"pem"`
}
// only parse as little as possible
type CertistReply struct {
Certificate struct {
Pem string `json:"pem"`
} `json:"certificate"`
CertificatesInTheChain []CertificateInChain `json:"chain"`
}
var myClient = &http.Client{Timeout: 15 * time.Second}
r, err := myClient.Get(fmt.Sprintf("https://api.cert.ist/%s", domain))
if err != nil {
return err, []string{}
}
defer loggedCloser(r.Body)
bod, _ := ioutil.ReadAll(r.Body)
api := new(CertistReply)
err = json.Unmarshal(bod, &api)
if err != nil {
return err, []string{}
}
all := make([]string, len(api.CertificatesInTheChain))
for c, cert := range api.CertificatesInTheChain {
all[c] = cert.Pem.Hashes.Sha265
}
return nil, all
}
func getCertificateChainHashesLocally(address string) (error, []string) {
conn, err := tls.Dial("tcp", address, &tls.Config{
InsecureSkipVerify: true,
})
if err != nil {
return err, []string{}
}
defer loggedCloser(conn)
all := make([]string, len(conn.ConnectionState().PeerCertificates))
for count, cert := range conn.ConnectionState().PeerCertificates {
var b bytes.Buffer
err := pem.Encode(&b, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
})
if err != nil {
return err, []string{}
}
sum256 := sha256.Sum256([]byte(b.String()))
all[count] = fmt.Sprintf("%x", sum256)
}
return nil, all
}
func validateCertificate(domain string) {
e, hashesFromLocal := getCertificateChainHashesLocally(fmt.Sprintf("%s:443", domain))
if e != nil {
panic(e)
}
e, hashesFromApi := getCertificateChainHashesApi(domain)
if e != nil {
panic(e)
}
areEqual := reflect.DeepEqual(hashesFromLocal, hashesFromApi)
log.Printf("Domain: %s, Certificates matched? %t", domain, areEqual)
}
func main() {
validateCertificate("urip.io")
validateCertificate("cert.ist")
validateCertificate("tilltrump.com")
validateCertificate("asciirange.com")
}
Compile and run
$ export GOPATH=${HOME}/go
$ go build -o pin_example main.go
$ ./pin_example
2020/10/01 15:40:18 Domain: urip.io, Certificates matched? true
2020/10/01 15:40:19 Domain: cert.ist, Certificates matched? true
2020/10/01 15:40:20 Domain: tilltrump.com, Certificates matched? true
2020/10/01 15:40:21 Domain: asciirange.com, Certificates matched? true
Github