riok/Kreya

Server certificate validation on Mac fails when validity period exceeds 398 days

Closed this issue · 6 comments

timw commented

Describe the bug
In our development environment we use a locally generated TLS keychain, consisting of:

  • root-ca (Root CA)
    • local-ca (Intermediate CA)
      • grpc_server (TLS cert)

The grpc_server certificate has Subject Alt Names that nominate the localhost DNS name.

Connecting to this server with Kreya works with Server Certificate validation disabled, but fails when Server Certificate validation is Enabled.

The error presented on failure is:

Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch
This is distinct from errors relating to self signed trust roots, which report RemoteCertificateChainErrors errors.

To Reproduce
Steps to reproduce the behavior:

  1. Configure a gRPC endpoint to use TLS on localhost
  2. Specify Service Certificate (TLS/SSL) validation as Enabled
  3. (probably necessary) import the CA cert for the gRPC server certificate into the Mac keychain and trust it for all usages
  4. Invoke a gRPC method

Expected behavior
TLS endpoints should work on localhost if the certificates are well formed, and the trust roots are appropriately established.

Screenshots
If applicable, add screenshots to help explain your problem.

Environment (if possible, copy the information from the error dialog or the About menu):
{
"kreyaVersion": "1.13.0-alpha.1",
"releaseChannel": "alpha",
"osDescription": "Darwin 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:18 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6000",
"osVersion": "Unix 14.2.1",
"osArch": "Arm64",
"processArch": "Arm64",
"runtimeIdentifier": "osx-arm64",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)"
}

Additional context
OpenSSL connects successfully to this server, with server and hostname validation enabled:

openssl s_client -connect localhost:9090 -verify_return_error -CAfile ./local-ca.cert.pem -verify_hostname localhost

Google Chrome also connects to this server as an HTTPS resource, validating the certificate against the local CA in the Mac keychain.

Full X509 of the grpc-server certificate:


Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 6142795339930154397 (0x553f97d0d476359d)
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: O=Orchestral, OU=Indexity, OU=Development, CN=local-ca
        Validity
            Not Before: Jan 16 01:53:57 2024 GMT
            Not After : Jan 14 01:53:57 2029 GMT
        Subject: O=Orchestral, OU=Indexity, OU=Development, CN=grpc-server
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:e2:c7:07:ba:ea:32:71:5f:0f:4b:55:7c:3d:16:
                    be:7a:53:51:91:8b:f8:c4:6a:ab:91:7b:e6:e7:3c:
                    81:4c:6f:31:45:e2:d9:c2:3c:21:c0:44:92:7f:0c:
                    56:8a:61:f4:63:3e:87:a3:1f:50:f4:8a:dc:79:99:
                    a7:42:a7:d5:2d
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Subject Key Identifier: 
                A0:01:95:65:CC:FF:7B:64:E9:20:0F:69:09:E9:4C:A1:CA:E0:57:FB
            X509v3 Authority Key Identifier: 
                keyid:6F:5C:C5:54:6E:85:06:7F:02:94:5F:FF:E5:35:A4:0A:CA:8C:C6:C9
                DirName:/O=Orchestral/OU=Indexity/OU=Development/CN=root-ca
                serial:60:AA:A6:68:F1:D0:DD:4B
            X509v3 Subject Alternative Name: 
                DNS:localhost, IP Address:127.0.0.1
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:45:02:20:23:7f:62:97:4c:12:2b:50:30:23:f6:d1:ac:43:
        16:d7:b9:02:1f:30:7d:62:3f:c0:b3:4f:b8:23:e6:28:13:ff:
        02:21:00:b0:27:5d:1e:e8:ee:71:9d:aa:e1:09:1f:c6:db:09:
        b7:fc:8c:9d:1f:e2:c2:5b:d4:3f:62:5f:f0:9e:e5:77:79
-----BEGIN CERTIFICATE-----
MIICmDCCAj6gAwIBAgIIVT+X0NR2NZ0wCgYIKoZIzj0EAwIwUTETMBEGA1UECgwK
T3JjaGVzdHJhbDERMA8GA1UECwwISW5kZXhpdHkxFDASBgNVBAsMC0RldmVsb3Bt
ZW50MREwDwYDVQQDDAhsb2NhbC1jYTAeFw0yNDAxMTYwMTUzNTdaFw0yOTAxMTQw
MTUzNTdaMFQxEzARBgNVBAoMCk9yY2hlc3RyYWwxETAPBgNVBAsMCEluZGV4aXR5
MRQwEgYDVQQLDAtEZXZlbG9wbWVudDEUMBIGA1UEAwwLZ3JwYy1zZXJ2ZXIwWTAT
BgcqhkjOPQIBBggqhkjOPQMBBwNCAATixwe66jJxXw9LVXw9Fr56U1GRi/jEaquR
e+bnPIFMbzFF4tnCPCHARJJ/DFaKYfRjPoejH1D0itx5madCp9Uto4H8MIH5MAwG
A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB
BggrBgEFBQcDAjAdBgNVHQ4EFgQUoAGVZcz/e2TpIA9pCelMocrgV/swfwYDVR0j
BHgwdoAUb1zFVG6FBn8ClF//5TWkCsqMxsmhVKRSMFAxEzARBgNVBAoMCk9yY2hl
c3RyYWwxETAPBgNVBAsMCEluZGV4aXR5MRQwEgYDVQQLDAtEZXZlbG9wbWVudDEQ
MA4GA1UEAwwHcm9vdC1jYYIIYKqmaPHQ3UswGgYDVR0RBBMwEYIJbG9jYWxob3N0
hwR/AAABMAoGCCqGSM49BAMCA0gAMEUCICN/YpdMEitQMCP20axDFte5Ah8wfWI/
wLNPuCPmKBP/AiEAsCddHujucZ2q4QkfxtsJt/yMnR/iwlvUP2Jf8J7ld3k=
-----END CERTIFICATE-----

I suspect the issue is because the Subject is CN=grpc-server, but you connect to localhost. We use the default .NET TLS validation implementation, which in turn calls the native macOS TLS library as far as I know.

Chrome ignores the Common Name from the Subject and relies solely on the Subject Alternative Name, which may explain the difference

timw commented

The "usual" endpoint identification approach is the HTTPS approach defined in rfc2818, which specifies the use of subject alt names if present, and the CN if not (although use of CN is deprecated), although it does appear that Chrome doesn't like certs without SAN anymore.

When I use a certificate with CN=localhost with or without subject alt names, I get the same error in Kreya.
Notably a cert with CN=localhost and no SANs works with openssl.

Does the certificate work in Safari?

timw commented

Safari doesn't like the certificate.

To be specific:

  • the related root and intermediate CA certs are installed in the System keychain, and trusted
  • Safari claims that the certificate is "not standards compliant"

Importing the server/leaf certificate into the System Keychain manually and trusting it allows it to be accepted by Safari and Kreya (with cert validation enabled).
Generating the cert with a custom domain (and aliasing that via /etc/hosts) has the same behaviour, so it's not an issue with the use of localhost.

A bit of googling based on the Safari error led to golang/go#51991 (comment).
Restarting didn't work, but shortening the validity period to 1 year (was 5 years, and apparently Apple now requires certs to be valid for no more than 398 days) makes it work in both Safari and Kreya.

timw commented

Looks like this is user error :), but at least this issue is now here for posterity given how obscure the root cause is.

Thank you for your in-depth analysis!