// Package crtutil provides utility functions for working with certificates. package crtutil import ( "crypto/sha256" "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 { h := sha256.Sum256([]byte(crt.SerialNumber.String())) return fmt.Sprintf("FIXME:%x", h[:]) // not a secure mapping }