Manually verify SHA1 certificates (fixes #854) (#855)

This commit is contained in:
Kory Prince
2023-01-24 10:33:00 -06:00
committed by GitHub
parent 26175c15f8
commit 589b8b1170
3 changed files with 94 additions and 9 deletions

View File

@@ -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
}

46
pkg/crypto/apple_test.go Normal file
View File

@@ -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")
}
}

17
pkg/crypto/testdata/appleca_signed.pem vendored Normal file
View File

@@ -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-----