From 589b8b1170f34e68daf3ef22bcb64f565a143b33 Mon Sep 17 00:00:00 2001 From: Kory Prince Date: Tue, 24 Jan 2023 10:33:00 -0600 Subject: [PATCH] Manually verify SHA1 certificates (fixes #854) (#855) --- pkg/crypto/apple.go | 40 +++++++++++++++++----- pkg/crypto/apple_test.go | 46 ++++++++++++++++++++++++++ pkg/crypto/testdata/appleca_signed.pem | 17 ++++++++++ 3 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 pkg/crypto/apple_test.go create mode 100644 pkg/crypto/testdata/appleca_signed.pem diff --git a/pkg/crypto/apple.go b/pkg/crypto/apple.go index c602728c..9d5ace03 100644 --- a/pkg/crypto/apple.go +++ b/pkg/crypto/apple.go @@ -1,6 +1,9 @@ package crypto import ( + "crypto" + "crypto/rsa" + "crypto/sha1" "crypto/x509" "encoding/pem" "fmt" @@ -91,19 +94,38 @@ pyQPbp8orlXe+tA8JA== -----END CERTIFICATE----- ` -// VerifyFromAppleDeviceCA verifies a certificate was signed by Apple's iPhone Device CA. -// TODO: We want to have more intensive verification (like the whole provided chain). -// TODO: Implement some sort of cache so we don't need to parse PEM & DER every invocation. -func VerifyFromAppleDeviceCA(c *x509.Certificate) error { +func mustParsePublicKey(cert string) *rsa.PublicKey { block, _ := pem.Decode([]byte(appleiPhoneDeviceCAPEM)) if block == nil || block.Type != "CERTIFICATE" { panic("appleiPhoneDeviceCAPEM: invalid PEM block") } - parent, err := x509.ParseCertificate(block.Bytes) + c, err := x509.ParseCertificate(block.Bytes) if err != nil { - panic(fmt.Sprintf("appleiPhoneDeviceCAPEM: err parsing: %s", err)) + panic(fmt.Sprintf("appleiPhoneDeviceCAPEM: err parsing: %v", err)) } - // Note we CheckSignatureFrom() as we cannot Verify the certificate chain - // (known expired intermediate) - return c.CheckSignatureFrom(parent) + + key, ok := c.PublicKey.(*rsa.PublicKey) + if !ok { + panic("appleiPhoneDeviceCAPEM: invalid key type") + } + + return key +} + +// extract public key from appleiPhoneDeviceCAPEM +var appleiPhoneDeviceCAPublicKey = mustParsePublicKey(appleiPhoneDeviceCAPEM) + +// VerifyFromAppleDeviceCA verifies a certificate was signed by Apple's iPhone Device CA. +// Manually verify the certificate since Go has deprecated verifying SHA1WithRSA x509 certificates. +func VerifyFromAppleDeviceCA(c *x509.Certificate) error { + if c.SignatureAlgorithm != x509.SHA1WithRSA { + return x509.ErrUnsupportedAlgorithm + } + + hashed := sha1.Sum(c.RawTBSCertificate) + if err := rsa.VerifyPKCS1v15(appleiPhoneDeviceCAPublicKey, crypto.SHA1, hashed[:], c.Signature); err != nil { + return err + } + + return nil } diff --git a/pkg/crypto/apple_test.go b/pkg/crypto/apple_test.go new file mode 100644 index 00000000..7f889b08 --- /dev/null +++ b/pkg/crypto/apple_test.go @@ -0,0 +1,46 @@ +package crypto + +import ( + "crypto/x509" + "encoding/pem" + "testing" +) + +func TestCmpAppleDeviceCAPublicKey(t *testing.T) { + block, _ := pem.Decode([]byte(appleiPhoneDeviceCAPEM)) + if block == nil || block.Type != "CERTIFICATE" { + t.Fatalf("appleiPhoneDeviceCAPEM: invalid PEM block") + } + parent, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("appleiPhoneDeviceCAPEM: err parsing: %v", err) + } + + if !appleiPhoneDeviceCAPublicKey.Equal(parent.PublicKey) { + t.Error("appleiPhoneDeviceCAPEM: public keys not equal") + } +} + +func TestVerifyFromAppleDeviceCA(t *testing.T) { + // extracted from /Library/Keychains/apsd.keychain + cert, _ := ReadPEMCertificateFile("testdata/appleca_signed.pem") + + if err := VerifyFromAppleDeviceCA(cert); err != nil { + t.Errorf("received error verifying Apple Device CA: %v", err) + } + + // test invalidly-signed cert + // reuse cert so we don't need to generate one. This should fail since it's not self-signed. + block, _ := pem.Decode([]byte(appleiPhoneDeviceCAPEM)) + if block == nil || block.Type != "CERTIFICATE" { + t.Fatalf("appleiPhoneDeviceCAPEM: invalid PEM block") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("appleiPhoneDeviceCAPEM: err parsing: %v", err) + } + + if err := VerifyFromAppleDeviceCA(cert); err == nil { + t.Error("expected error verifying non-apple-device-signed cert") + } +} diff --git a/pkg/crypto/testdata/appleca_signed.pem b/pkg/crypto/testdata/appleca_signed.pem new file mode 100644 index 00000000..1033b1a3 --- /dev/null +++ b/pkg/crypto/testdata/appleca_signed.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICnjCCAgegAwIBAgIKATVAHcKbZdw3hDANBgkqhkiG9w0BAQUFADBaMQswCQYD +VQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEVMBMGA1UECxMMQXBwbGUgaVBo +b25lMR8wHQYDVQQDExZBcHBsZSBpUGhvbmUgRGV2aWNlIENBMB4XDTE4MTEwODE0 +MzYzNloXDTE5MTEwODE0NDEzNlowLzEtMCsGA1UEAxYkNjAwMUE5NTctNkZDNS00 +ODU2LThDNUUtREMyRTMyQzYxQ0I5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDhOLz0DpG1DmYqtLl/qsMDNDg9EI4MwC2XiHTq2kixj7otzqQYvIxwjm1ou31p +QIfyqVgpKRKEBiHtWQS5lD77yTJqch5X0zuOF1rw8+s2NXxbQaGXh2YstwhySoLd +679t6Zn4ftlkpkB5qO427i2nCiJ8TpJ+bwQQCOgDYoomaQIDAQABo4GVMIGSMB8G +A1UdIwQYMBaAFLL+ISNEhpVqedWBJo5zENinTI50MB0GA1UdDgQWBBS1DsHPDXar +x0iuOBXnfPZeHU3CYjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDAgBgNV +HSUBAf8EFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEAYKKoZIhvdjZAYKBAQCBQAw +DQYJKoZIhvcNAQEFBQADgYEAx9vvPrfMBQaPJirj7cGK9Bp0bNhcmZ9iOOn4Y0kK +3ygbclt2lPhB+wl+rMTPo0qky/JsCN9G2YvyEwOXYpJEOz+UNYGIXrfGW4pFJFR+ +pVroqXfkAqUgg2ipZwaR7fWugPakRjrD5SDLHH43y1bch3+Zy1Rg0RI3RrUa12Th +uHM= +-----END CERTIFICATE-----