Created
June 16, 2023 13:12
-
-
Save robstradling/0077ff59a942eec33460219f5aea2d44 to your computer and use it in GitHub Desktop.
Produce final certificate from precertificate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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