Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robstradling/0077ff59a942eec33460219f5aea2d44 to your computer and use it in GitHub Desktop.
Save robstradling/0077ff59a942eec33460219f5aea2d44 to your computer and use it in GitHub Desktop.
Produce final certificate from precertificate
package main
import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"fmt"
"math/big"
"time"
)
// These type definitions are based on non-exported types defined at https://cs.opensource.google/go/go/+/refs/tags/go1.20.5:src/crypto/x509/x509.go;l=171
// The intention is to decode nested ASN.1 elements minimally rather than deeply, to reduce the risk of introducing any deviations when re-encoding.
type certificate struct {
TBSCertificate tbsCertificate
SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString
}
type tbsCertificate struct {
Version int `asn1:"optional,explicit,default:0,tag:0"`
SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer asn1.RawValue
Validity validity
Subject asn1.RawValue
PublicKey asn1.RawValue // x509.go has "publicKeyInfo", but we want to use the SPKI as is (rather than decode and re-encode it).
UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
}
type validity struct {
NotBefore, NotAfter time.Time
}
func finalTBSCertificateFromPrecertificate(precertificate []byte, sct_list []byte) ([]byte, *pkix.AlgorithmIdentifier) {
// Decode the precertificate.
var c certificate
rest, err := asn1.Unmarshal(precertificate, &c)
if err != nil {
fmt.Printf("ERROR: asn1.Unmarshal(precertificate) => %v\n", err)
return nil, nil
} else if len(rest) > 0 {
fmt.Printf("ERROR: asn1.Unmarshal(precertificate) => trailing data\n")
return nil, nil
}
// Find the CT Poison extension.
poison_index := -1
for i, extension := range c.TBSCertificate.Extensions {
if extension.Id.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}) {
if poison_index != -1 {
fmt.Printf("ERROR: Multiple CT Poison extensions found\n")
return nil, nil
}
poison_index = i
}
}
if poison_index == -1 {
fmt.Printf("ERROR: No CT Poison extension found\n")
return nil, nil
}
// Replace the CT Poison extension with a CT Precert SCT List extension.
sct_list_encoded, err := asn1.Marshal(asn1.RawValue{
Tag: asn1.TagOctetString,
Bytes: sct_list,
})
if err != nil {
fmt.Printf("ERROR: asn1.Marshal(sct_list) => %v\n", err)
return nil, nil
}
c.TBSCertificate.Extensions[poison_index] = pkix.Extension{
Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2},
Critical: false,
Value: sct_list_encoded,
}
// Re-encode the (modified) TBSCertificate.
tbs_certificate_new, err := asn1.Marshal(c.TBSCertificate)
if err != nil {
fmt.Printf("ERROR: asn1.Marshal(c.TBSCertificate) => %v", err)
return nil, nil
}
// Return the newly encoded TBSCertificate.
// Also return the signature AlgorithmIdentifier, so that the signer doesn't need to decode the TBSCertificate again to obtain that necessary detail.
return tbs_certificate_new, &c.TBSCertificate.SignatureAlgorithm
}
func main() {
// https://api.certspotter.com/v1/certs/22700bd0d70ac5790e6ae5b5f10afc998bb062b4fb1a153fd71b3f1b98fb8b00.pem
precert_b64 := []byte(`-----BEGIN CERTIFICATE-----
MIIFGjCCBAKgAwIBAgISA+Ime3hrfjODF93WLnZPyzxxMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzA2MTUxNDM2MTZaFw0yMzA5MTMxNDM2MTVaMB4xHDAaBgNVBAMM
EyouN2FjbnIubW9uZ29kYi5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCjLiLXI/mTBSEkSKVucC3NcnXGu/M2qwLIk1uenifnoNMmdJmEyp+oWFUS
n9rIXtHw27YTlJLRRYLSIzqqujDV5PmXzFrSJ/9JrgIbNUowaVF3j9bf1+NPENEH
81RnNGevtKUN5NoEo3fAmZaMWrGjWioNnpIsegSjvvuHeqMqC7SNrGSvtKLBiPkO
bL5oScPYj/cHzt3RYJ17ru6xWgUDV6aqvEblrxcXvPmd/1SxB3Vkdkc+bCuSLSNM
/NmcET0YUhWizanjodJarpYJRuW1SjGmPda0jBAQZQDPmZHCEgwTBcCEIg5J3XzA
fFUZPPlTVgE+7Mbjd/DK7iz46D0uHOigVTZto3lPYRdRiyVFNUMAN0GLAlkaJ7Td
0FnAxvhE74lSjI7lFqDNtiyA8ovp/JbKfPmnvfH+fQa7vEFbR5H9v4UZt0XLeI6W
dV4pYoCwuK5mfr0NQLCy/015OAU8WF4MLM+Fyt+GG+sOk2Maz6ysAShMOvdNH7B3
GSn65xBVgBxlPWyYpodW9SS1NSVgrgbKMg0yHzx/PdosQehyh9p6OpuTaeEi2iQg
yTODKGHX+cmjzUx0iCG2ByC9bvMo32eZXiC+itZCaHb0FGXh+K7UcOCsvsi7NLGR
ngVKK7u7gZmPu4UkVUBpF3jz/OK3OsudHcflZIGd6nf8w4lp0wIDAQABo4IBPDCC
ATgwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBREcOX3VXl7+uM7aqTQ/coniJsAAjAf
BgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcw
IQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYW
aHR0cDovL3IzLmkubGVuY3Iub3JnLzA4BgNVHREEMTAvghgqLjdhY25yLm1lc2gu
bW9uZ29kYi5uZXSCEyouN2FjbnIubW9uZ29kYi5uZXQwEwYDVR0gBAwwCjAIBgZn
gQwBAgEwEwYKKwYBBAHWeQIEAwEB/wQCBQAwDQYJKoZIhvcNAQELBQADggEBALIU
rHns6TWfT/kfJ60D9R1Ek4YGB/jVsrh2d3uiIU2hiRBBjgDkCLyKd7oXM761uXX3
LL4H4JPegqTrZAPO88tUtzBSb3IF4yA0o1NWhE6ceLnBk9fl5TRCC8QASliApsOi
gDgRi1VFmyFOHpHnVZdbpPucy6T+CdKXKfj4iNw+aOZcoQxJ70XECXxQbdqJ7VdY
f0B+wtk5HZU8cuVVCj1i/iDv1zqITCzaavbz870QugiHO/8rj2ctrA07SX3Ovs4J
GbCGuMzlpxeIFtQDWVufVbu1ZZltzPlSHFqv6mPKW9stYtt8JCjmPwNW6UdrlBtN
gvFgkgDpz+Q6/Vu+u7g=
-----END CERTIFICATE-----`)
precert, _ := pem.Decode(precert_b64)
// Extracted from https://api.certspotter.com/v1/certs/c0916d24ac8844522b3695093fb66b3e9ff71b5f25b6d563030113ed03833f0b.pem
sctList_b64 := `APAAdgC3Pvsk35xNunXyOcW6WPRsXfxCz3qfNcSeHQmBJe20mQAAAYi/s0QZAAAEAwBHMEUCID4v
c7PNWNauTkmkS7CqSwdiyOV+LYIT9g8KygWW4atTAiEA6Re4Cz7BsEMi+/U8G+r9LmqbqwGXGS4m
XG7RiEfeQEcAdgB6MoxU2LcttiDqOOBSHumEFnAyE4VNO9IrwTpXo1LrUgAAAYi/s0RQAAAEAwBH
MEUCIQD95SqDycwXGZ+JKBUVBR+hBxn4BRIQ7EPIaMTI/+854gIgDpJm5BFX9vKUf5tKWn9f/Fag
ktt5J6hPnrmURSV/egA=`
sctList, _ := base64.StdEncoding.DecodeString(sctList_b64)
tbsFinalCert, sigAlg := finalTBSCertificateFromPrecertificate(precert.Bytes, sctList)
if tbsFinalCert != nil {
fmt.Printf("TBSCertificate for final certificate: %s\n", base64.StdEncoding.EncodeToString(tbsFinalCert))
fmt.Printf("AlgorithmIdentifier for final certificate's signature: %v\n", sigAlg)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment