summaryrefslogtreecommitdiff
path: root/bin-src/pem-diff.go
diff options
context:
space:
mode:
Diffstat (limited to 'bin-src/pem-diff.go')
-rw-r--r--bin-src/pem-diff.go140
1 files changed, 140 insertions, 0 deletions
diff --git a/bin-src/pem-diff.go b/bin-src/pem-diff.go
new file mode 100644
index 0000000..da27a62
--- /dev/null
+++ b/bin-src/pem-diff.go
@@ -0,0 +1,140 @@
+package main
+
+import (
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "sort"
+ "strings"
+)
+
+func handleErr(err error, str string, a ...interface{}) {
+ a = append([]interface{}{err}, a...)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, str, a...)
+ os.Exit(1)
+ }
+}
+
+type Cert struct {
+ Url string
+ X509 *x509.Certificate
+}
+
+func (cert Cert) WriteTo(w io.Writer, action string) error {
+ block := pem.Block{
+ Type: "CERTIFICATE",
+ Headers: map[string]string{
+ "X-Crt-Sh-Url": cert.Url,
+ "X-Diff-Action": action,
+ },
+ Bytes: cert.X509.Raw,
+ }
+ return pem.Encode(w, &block)
+}
+
+func readTLS(filename string) (map[string]Cert, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ data, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make(map[string]Cert)
+ for len(data) > 0 {
+ var certPem *pem.Block
+ certPem, data = pem.Decode(data)
+ certX509, err := x509.ParseCertificate(certPem.Bytes)
+ if err != nil {
+ url, err2 := url.Parse(certPem.Headers["X-Socket"])
+ if err2 != nil {
+ fmt.Fprintf(os.Stderr, "Could not get cert or even parse URL:\ncert: %v\nurl: %v\n", err, err2)
+ os.Exit(1)
+ }
+ ret[strings.Split(url.Host, ":")[0]] = Cert{
+ X509: new(x509.Certificate),
+ }
+ } else {
+ ret[certX509.Subject.CommonName] = Cert{
+ Url: fmt.Sprintf("https://crt.sh/?serial=%036x", certX509.SerialNumber),
+ X509: certX509,
+ }
+ }
+ }
+ return ret, nil
+}
+
+func readCrtSh(filename string, hosts []string) (map[string]Cert, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ data, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make(map[string]Cert)
+ for len(data) > 0 {
+ var certPem *pem.Block
+ certPem, data = pem.Decode(data)
+ certX509, err := x509.ParseCertificate(certPem.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ for _, host := range hosts {
+ if certX509.VerifyHostname(host) == nil {
+ if old, haveold := ret[host]; !haveold || certX509.NotBefore.After(old.X509.NotBefore) {
+ ret[host] = Cert{
+ Url: certPem.Headers["X-Crt-Sh-Url"],
+ X509: certX509,
+ }
+ }
+ }
+ }
+ }
+ return ret, nil
+}
+
+func keys(m map[string]Cert) []string {
+ ret := make([]string, len(m))
+ i := 0
+ for k := range m {
+ ret[i] = k
+ i++
+ }
+ sort.Strings(ret)
+ return ret
+}
+
+func main() {
+ if len(os.Args) != 3 {
+ fmt.Fprintf(os.Stderr, "Usage: %s TLS-file crt.sh-file\n", os.Args[0])
+ }
+ certsTLS, err := readTLS(os.Args[1])
+ handleErr(err, "Could load TLS file: %v\n")
+ hostsTLS := keys(certsTLS)
+ certsCrtSh, err := readCrtSh(os.Args[2], hostsTLS)
+ handleErr(err, "Could load crt.sh file: %v\n")
+
+ for _, host := range hostsTLS {
+ certTLS := certsTLS[host]
+ certCrtSh, haveCrtSh := certsCrtSh[host]
+
+ if !haveCrtSh {
+ handleErr(certTLS.WriteTo(os.Stdout, "del"), "Could not encode PEM: %v\n")
+ } else if !certTLS.X509.Equal(certCrtSh.X509) {
+ handleErr(certTLS.WriteTo(os.Stdout, "del"), "Could not encode PEM: %v\n")
+ handleErr(certCrtSh.WriteTo(os.Stdout, "add"), "Could not encode PEM: %v\n")
+ } else {
+ handleErr(certCrtSh.WriteTo(os.Stdout, "ctx"), "Could not encode PEM: %v\n")
+ }
+ }
+}