// Package crtutil provides utility functions for working with certificates. package crtutil import ( "encoding/pem" "fmt" ct "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/x509" ) // CertificateChainFromPEM parses a certificate chain in PEM format. At least // one certificate must be in the chain. The first certificate must be a leaf, // whereas all other certificates must CA certificates (intermdiates/roots). func CertificateChainFromPEM(b []byte) ([]x509.Certificate, error) { var chain []x509.Certificate for { block, rest := pem.Decode(b) if block == nil { break } crt, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, fmt.Errorf("parse certificate: %v", err) } chain = append(chain, *crt) b = rest } if len(chain) == 0 { return nil, fmt.Errorf("no certificates in the provided chain") } if chain[0].IsCA { return nil, fmt.Errorf("leaf certificate has the CA bit set") } for _, crt := range chain[1:] { if !crt.IsCA { return nil, fmt.Errorf("non-leaf certificate without the CA bit set") } } return chain, nil } // CertificateFromLogEntry parses the (pre-)certificate in a log entry func CertificateFromLogEntry(leafData, extraData []byte) (x509.Certificate, error) { entry, err := ct.LogEntryFromLeaf(0, &ct.LeafEntry{LeafInput: leafData, ExtraData: extraData}) if err != nil { return x509.Certificate{}, fmt.Errorf("parse leaf: %v", err) } if entry.Precert == nil && entry.X509Cert == nil { return x509.Certificate{}, fmt.Errorf("neither precertificate nor certificate in leaf") } if entry.Precert != nil && entry.X509Cert != nil { return x509.Certificate{}, fmt.Errorf("both certificate and precertificate in leaf") } if entry.Precert != nil { return *entry.Precert.TBSCertificate, nil } return *entry.X509Cert, nil } // UniqueID derives a unique certificate ID. The same value is derived // regardless of if the (pre-)certificate is logged multiple times. func UniqueID(crt x509.Certificate) string { // The CAB BRs state in §7.1.2.7 that serial numbers "MUST be a // non‐sequential number greater than zero (0) and less than 2^159". // https://cabforum.org/working-groups/server/baseline-requirements/documents/TLSBRv2.0.4.pdf // // So, we can assume that serial numbers are unique across CAs. It would be // an improvement to derive this from the certificate bytes though, such // that the identifier is the same for certificates and pre-certificates. return crt.SerialNumber.String() }