ccadb2OneCRL/ccadb/ccadb.go (107 lines of code) (raw):
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package ccadb
import (
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"net/http"
"strings"
log "github.com/sirupsen/logrus"
"github.com/mozilla/OneCRL-Tools/ccadb2OneCRL/set"
"github.com/mozilla/OneCRL-Tools/ccadb2OneCRL/utils"
"github.com/pkg/errors"
"github.com/gocarina/gocsv"
)
const source = "https://ccadb.my.salesforce-sites.com/mozilla/PublicInterCertsReadyToAddToOneCRLPEMCSV"
type OneCRLStatus string
var ReadyToAdd OneCRLStatus = "Ready to Add"
type CCADB = []*Certificate
type Certificate struct {
CAOwner string `csv:"CA Owner"`
RevocationStatus string `csv:"Revocation Status"`
ReasonCode string `csv:"RFC 5280 Revocation Reason Code"`
DateOfRevocation string `csv:"Date of Revocation"`
OneCRLStatus string `csv:"OneCRL Status"`
OneCRLBugNumber string `csv:"OneCRL Bug Number"`
CertificateSerialNumber string `csv:"Certificate Serial Number"`
CaOwnerName string `csv:"CA Owner/Certificate Name"`
CertificateIssuerName string `csv:"Certificate Issuer Common Name"`
CertificateIssuerOrganization string `csv:"Certificate Issuer Organization"`
CertificateSubjectCommonName string `csv:"Certificate Subject Common Name"`
CertificateSubjectOrganization string `csv:"Certificate Subject Organization"`
Fingerprint string `csv:"SHA-256 Fingerprint"`
SubjectSPKIHash string `csv:"Subject + SPKI SHA256"`
NotBefore string `csv:"Valid From [GMT]"`
NotAfter string `csv:"Valid To [GMT]"`
KeyAlgorithm string `csv:"Public Key Algorithm"`
SignatureAlgorithm string `csv:"Signature Hash Algorithm"`
CRLs string `csv:"CRL URL(s)"`
AlternativeCRL string `csv:"Alternate CRL"`
Comments string `csv:"Comments"`
PemInfo string `csv:"PEM Info"`
}
func Default() ([]*Certificate, error) {
return FromURL(source)
}
func FromURL(url string) ([]*Certificate, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return FromReader(resp.Body)
}
func FromReader(reader io.Reader) ([]*Certificate, error) {
report := make([]*Certificate, 0)
return report, gocsv.Unmarshal(reader, &report)
}
// IssuerSerial parses the X.509 certificate retrieved from the CCADB,
// extracts the issuer (https://tools.ietf.org/html/rfc5280#section-4.1.2.4) and
// serial number (https://tools.ietf.org/html/rfc5280#section-4.1.2.2)
//
// An error will be logged and a nil IssuerSerial returned if no certificate is present or if
// the certificate cannot be parsed..
func (c *Certificate) IssuerSerial() *set.IssuerSerial {
cert, err := c.ParseCertificate()
if err != nil {
log.WithError(err).
WithField("revocation", c).
Warn("failed to parse the CCADB certificate when constructing a Issuer:Serial pair")
return nil
}
serial, err := utils.RawSerialBytes(cert.RawTBSCertificate)
if err != nil {
log.WithError(err).
WithField("revocation", c).
Warn("failed to parse serial number")
return nil
}
is := set.NewIssuerSerial(cert.RawIssuer, serial)
return &is
}
// SubjectKeyHash parses the X.509 certificate retrieved from the CCADB,
// extracts the subject (https://tools.ietf.org/html/rfc5280#section-4.1.2.6) and
// SPKI (https://tools.ietf.org/html/rfc5280#section-4.1.2.7). The SPKI is hashed
// with SHA256.
//
// An error will be logged and a nil SubjectKeyHash returned if no certificate is present or if
// the certificate cannot be parsed..
func (c *Certificate) SubjectKeyHash() *set.SubjectKeyHash {
cert, err := c.ParseCertificate()
if err != nil {
log.WithError(err).
WithField("revocation", c).
Warn("failed to parse the CCADB certificate when constructing a Subject:KeyHash pair")
return nil
}
hasher := sha256.New()
hasher.Write(cert.RawSubjectPublicKeyInfo)
hash := hasher.Sum(nil)
skh := set.NewSubjectKeyHash(cert.RawSubject, hash)
return &skh
}
// ParseCertificate returns the parsed x509.Certificate.
//
// A nil certificate and an error is returned if the CCADB does not
// have a certificate, the certificate cannot be PEM decoded, or
// the certificate cannot be x509 decoded.
func (c *Certificate) ParseCertificate() (*x509.Certificate, error) {
p := c.PEM()
if p == "" {
return nil, errors.New("CCADB record has an empty certificate field")
}
b, _ := pem.Decode([]byte(p))
if b == nil {
return nil, fmt.Errorf("fail to decode pem from CCADB: '%s'", c.PemInfo)
}
return x509.ParseCertificate(b.Bytes)
}
// PEM returns a parseable PEM string from the PemInfo field.
// If you want to do something with the certificate then you should use
// this method rather than accessing the raw PemInfo field as the CCADB has
// as the habit of double encoding strings with inner single quotes.
func (c *Certificate) PEM() string {
return strings.TrimSpace(strings.Trim(c.PemInfo, "'"))
}
// Since the CCADB has the physical certificate, we can represent ourselves as
// either an IssuerSerial OR a SubjectKeyHash.
func (c *Certificate) Type() set.Type {
return set.Either
}