Skip to content

Instantly share code, notes, and snippets.

@cpu
Last active August 1, 2019 15:14

Revisions

  1. cpu revised this gist Aug 1, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions sctTestCerts.go
    Original file line number Diff line number Diff line change
    @@ -13,6 +13,8 @@
    // generated certificate lifetime (months) (default 3)
    // -scts int
    // number of embedded mock SCTs
    // -poison bool
    // if true, add the CT poison extension to issue a precertificate
    //
    // Example:
    // go run sctTestCerts.go -lifetime 24 -scts 2
  2. cpu revised this gist Aug 1, 2019. 1 changed file with 21 additions and 2 deletions.
    23 changes: 21 additions & 2 deletions sctTestCerts.go
    Original file line number Diff line number Diff line change
    @@ -44,6 +44,14 @@ import (
    ctx509 "github.com/google/certificate-transparency-go/x509"
    )

    var (
    // oidExtensionCTPoison is defined in RFC 6962 s3.1.
    // The `ctx509` package exports this but we're using the normal `x509` package
    // to create the cert so to avoid an incompatible type we redefine the OID
    // here.
    oidExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
    )

    func mustRandomKey() *rsa.PrivateKey {
    randKey, err := rsa.GenerateKey(rand.Reader, 512)
    if err != nil {
    @@ -60,7 +68,7 @@ func mustRandomSerial() *big.Int {
    return randSerial
    }

    func sctTestCert(months int, numScts int, differentLogs bool) *x509.Certificate {
    func sctTestCert(months int, numScts int, differentLogs bool, poison bool) *x509.Certificate {
    template := &x509.Certificate{
    Subject: pkix.Name{
    CommonName: "lint_ct_sct_policy_count_unsatisified_test CA",
    @@ -102,6 +110,16 @@ func sctTestCert(months int, numScts int, differentLogs bool) *x509.Certificate
    template.ExtraExtensions = []pkix.Extension{*sctExt}
    }

    // Add the precertificate poison extension if indicated
    if poison {
    template.ExtraExtensions = append(template.ExtraExtensions,
    pkix.Extension{
    Id: oidExtensionCTPoison,
    Critical: true,
    Value: []byte{0x05, 0x00}, // ASN.1 null value
    })
    }

    // Issue the certificate with the self-signed issuer
    subjectCertDer, err := x509.CreateCertificate(rand.Reader, template, issuerCert, subjectKey.Public(), issuerKey)
    if err != nil {
    @@ -176,9 +194,10 @@ func main() {
    months := flag.Int("lifetime", 3, "generated certificate lifetime (months)")
    sctCount := flag.Int("scts", 0, "number of embedded mock SCTs")
    differentLogs := flag.Bool("differentLogs", true, "Use a different Log ID per SCT")
    poison := flag.Bool("poison", false, "Add CT Poison extension to make a precertificate")
    flag.Parse()

    cert := sctTestCert(*months, *sctCount, *differentLogs)
    cert := sctTestCert(*months, *sctCount, *differentLogs, *poison)

    var buf bytes.Buffer
    err := pem.Encode(&buf, &pem.Block{
  3. cpu revised this gist Apr 6, 2019. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions sctTestCerts.go
    Original file line number Diff line number Diff line change
    @@ -10,15 +10,15 @@
    // -differentLogs
    // Use a different Log ID per SCT (default true)
    // -lifetime int
    // generated certificate liftime (months) (default 3)
    // generated certificate lifetime (months) (default 3)
    // -scts int
    // number of embedded mock SCTs
    //
    // Example:
    // go run sctTestCerts.go -lifetime 24 -scts 2
    // - generate a test certificate with a lifetime of 24 months that has two
    // embedded SCTs with unique log IDs.

    //
    // go run sctTestCerts.go -lifetime 39 -scts 4 -differentLogs=false
    // - generate a test certificate with a lifetime of 39 months that has four
    // embedded SCTs with the same log ID.
    @@ -173,7 +173,7 @@ func sctsListExtension(numScts int, differentLogs bool) *pkix.Extension {
    }

    func main() {
    months := flag.Int("lifetime", 3, "generated certificate liftime (months)")
    months := flag.Int("lifetime", 3, "generated certificate lifetime (months)")
    sctCount := flag.Int("scts", 0, "number of embedded mock SCTs")
    differentLogs := flag.Bool("differentLogs", true, "Use a different Log ID per SCT")
    flag.Parse()
  4. cpu created this gist Apr 6, 2019.
    193 changes: 193 additions & 0 deletions sctTestCerts.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,193 @@
    // sctTestCerts is a small program written to generate test subscriber
    // certificates for the zlint `sct_policy_count_unsatisified.go` unit tests. It
    // depends on `github.com/google/certificate-transparency-go` for providing
    // needed SCT types.
    //
    // Usage:
    // 1) go get github.com/google/certificate-transparency-go
    // 2) go run sctTestCerts.go -differentLogs=[true/false] -lifetime [int] -scts [int]
    //
    // -differentLogs
    // Use a different Log ID per SCT (default true)
    // -lifetime int
    // generated certificate liftime (months) (default 3)
    // -scts int
    // number of embedded mock SCTs
    //
    // Example:
    // go run sctTestCerts.go -lifetime 24 -scts 2
    // - generate a test certificate with a lifetime of 24 months that has two
    // embedded SCTs with unique log IDs.

    // go run sctTestCerts.go -lifetime 39 -scts 4 -differentLogs=false
    // - generate a test certificate with a lifetime of 39 months that has four
    // embedded SCTs with the same log ID.
    package main

    import (
    "bytes"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/asn1"
    "encoding/pem"
    "flag"
    "fmt"
    "math"
    "math/big"
    "time"

    ct "github.com/google/certificate-transparency-go"
    cttls "github.com/google/certificate-transparency-go/tls"
    ctx509 "github.com/google/certificate-transparency-go/x509"
    )

    func mustRandomKey() *rsa.PrivateKey {
    randKey, err := rsa.GenerateKey(rand.Reader, 512)
    if err != nil {
    panic(err)
    }
    return randKey
    }

    func mustRandomSerial() *big.Int {
    randSerial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
    if err != nil {
    panic(err)
    }
    return randSerial
    }

    func sctTestCert(months int, numScts int, differentLogs bool) *x509.Certificate {
    template := &x509.Certificate{
    Subject: pkix.Name{
    CommonName: "lint_ct_sct_policy_count_unsatisified_test CA",
    },
    SerialNumber: mustRandomSerial(),
    NotBefore: time.Now(),
    NotAfter: time.Now().AddDate(30, 0, 0),
    KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
    BasicConstraintsValid: true,
    IsCA: true,
    }

    issuerKey := mustRandomKey()

    // Create a self-signed issuer certificate to use
    issuerDer, err := x509.CreateCertificate(rand.Reader, template, template, issuerKey.Public(), issuerKey)
    if err != nil {
    panic(err)
    }
    issuerCert, err := x509.ParseCertificate(issuerDer)
    if err != nil {
    panic(err)
    }

    // Reconfigure the template for a subscriber cert
    subjectKey := mustRandomKey()
    template.SerialNumber = mustRandomSerial()
    template.Subject.CommonName = "zmap.io"
    template.DNSNames = []string{"zmap.io"}
    template.NotAfter = time.Now().AddDate(0, months, 0)
    template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
    template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
    template.IsCA = false

    // Generate and embed an SCT list extension if numScts > 0
    sctExt := sctsListExtension(numScts, differentLogs)
    if sctExt != nil {
    template.ExtraExtensions = []pkix.Extension{*sctExt}
    }

    // Issue the certificate with the self-signed issuer
    subjectCertDer, err := x509.CreateCertificate(rand.Reader, template, issuerCert, subjectKey.Public(), issuerKey)
    if err != nil {
    panic(err)
    }
    subjectCert, err := x509.ParseCertificate(subjectCertDer)
    if err != nil {
    panic(err)
    }
    return subjectCert
    }

    func logID(num int) ct.LogID {
    input := fmt.Sprintf("zlint-test-log %d", num)
    sum := sha256.Sum256([]byte(input))
    return ct.LogID{
    KeyID: sum,
    }
    }

    func sctsListExtension(numScts int, differentLogs bool) *pkix.Extension {
    if numScts == 0 {
    return nil
    }

    // Create a list of marshalled test SCTs
    sctList := ctx509.SignedCertificateTimestampList{}
    for i := 0; i < numScts; i++ {
    id := logID(i)
    if !differentLogs {
    id = logID(0)
    }
    sigBytes := sha256.Sum256([]byte("cool sct"))
    // Create a mock SCT
    sct := ct.SignedCertificateTimestamp{
    SCTVersion: ct.V1,
    LogID: id,
    Timestamp: uint64(time.Now().UnixNano() / int64(time.Millisecond)),
    Extensions: ct.CTExtensions{},
    Signature: ct.DigitallySigned{
    Algorithm: cttls.SignatureAndHashAlgorithm{
    Hash: cttls.SHA256,
    Signature: cttls.ECDSA,
    },
    Signature: sigBytes[:],
    },
    }
    // Marshal the SCT and add it to the list
    sctBytes, err := cttls.Marshal(sct)
    if err != nil {
    panic(err)
    }
    sctList.SCTList = append(sctList.SCTList, ctx509.SerializedSCT{Val: sctBytes})
    }

    // Marshal the list of marshalled scts
    sctListBytes, err := cttls.Marshal(sctList)

    // Serialize the marshalled list to ASN1 for the cert extension.
    asn1ListBytes, err := asn1.Marshal(sctListBytes)
    if err != nil {
    panic(err)
    }
    return &pkix.Extension{
    // See https://tools.ietf.org/html/rfc6962 Section 3.3
    Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2},
    Value: asn1ListBytes,
    }
    }

    func main() {
    months := flag.Int("lifetime", 3, "generated certificate liftime (months)")
    sctCount := flag.Int("scts", 0, "number of embedded mock SCTs")
    differentLogs := flag.Bool("differentLogs", true, "Use a different Log ID per SCT")
    flag.Parse()

    cert := sctTestCert(*months, *sctCount, *differentLogs)

    var buf bytes.Buffer
    err := pem.Encode(&buf, &pem.Block{
    Type: "CERTIFICATE",
    Bytes: cert.Raw,
    })
    if err != nil {
    panic(err)
    }

    fmt.Printf("%s", string(buf.Bytes()))
    }