microsoft / mssql-jdbc

The Microsoft JDBC Driver for SQL Server is a Type 4 JDBC driver that provides database connectivity with SQL Server through the standard JDBC application program interfaces (APIs).
MIT License
1.06k stars 424 forks source link

Server certificate Subject Alternative Name of type IP Address is not taken into account in server name verification #2529

Open d-richter-qdt opened 1 week ago

d-richter-qdt commented 1 week ago

Driver version

JDBC driver version (e.g. 12.8.1).

SQL Server version

Microsoft SQL Server 2022 (RTM-CU15-GDR) (KB5046059) - 16.0.4150.1 (X64) Sep 25 2024 17:34:41 Copyright (C) 2022 Microsoft Corporation Developer Edition (64-bit) on Linux (Ubuntu 22.04.5 LTS)

JAVA/JVM version

21.0.4

Problem description

Server certificate subject alternative name of type iPAddress is not taken into account in server name verification.

Expected behavior

If server address is entered as IP address and server certificate contain subject alternative name extension with that IP address defined connection attempt will be successful.

Actual behavior

Server name check fails.

Error message/stack trace

javax.net.ssl.SSLHandshakeException: Failed to validate the server name "<IP_ADDRESS>"in a certificate during Secure Sockets Layer (SSL) initialization. Name in certificate "<DNS_NAME>"
        at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:378)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:316)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:647)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:467)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:363)
        at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:393)
        at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:476)
        at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:447)
        at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:201)
        at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
        at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)
        at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421)
        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
        at com.microsoft.sqlserver.jdbc.TDSChannel.enableSSL(IOBuffer.java:1854)
        ... 53 more
Caused by: java.security.cert.CertificateException: Failed to validate the server name "<IP_ADDRESS>"in a certificate during Secure Sockets Layer (SSL) initialization. Name in certificate "<DNS_NAME>"
        at com.microsoft.sqlserver.jdbc.SQLServerCertificateUtils.validateServerNameInCertificate(SQLServerCertificateUtils.java:269)
        at com.microsoft.sqlserver.jdbc.HostNameOverrideX509TrustManager.checkServerTrusted(SQLServerTrustManager.java:99)
        at java.base/sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:1430)
        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:631)
        ... 65 more

Any other details that can be helpful

com.microsoft.sqlserver.jdbc.SQLServerCertificateUtils.validateServerNameInCertificate takes only constant 2 (DNS name) into account

JDBC URL: jdbc:sqlserver://<IP_ADDRESS>:<PORT>;databaseName=<DB_NAME>;socketTimeout=60000;encrypt=true;trustServerCertificate=false;trustStore=<PATH_TO_TRUSTSTORE>;trustStorePassword=<TRUSTSTORE_PWD>

possible work around using parameter hostNameInCertificate with DNS name used in certificate

lilgreenbird commented 14 hours ago

hi @d-richter-qdt

I'm not sure what the question is, did the hostNameInCertificate property work for you? That is what this property is used for, it can be used to specify the host name in situations where the name, or names, used in the certificate doesn't match the name passed in to the serverName property. For more information on descriptions of connection properties for the driver, please see the doc Setting the connection properties.

d-richter-qdt commented 3 hours ago

Hello @lilgreenbird,

thank you for your response. It is not question but bug report. I believe that driver should be able to connect without using that property in this case, as the certificate contains IP address of DB server in subjAltName extension (Even tho I agree it is kind of an edge case). As to your question yes the property works.

To be exact: Here: https://github.com/microsoft/mssql-jdbc/blob/ffa5e1fa9cc1a490ed10d86b39a7c8b887488d89/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCertificateUtils.java#L224 is missing handling of key == 7 https://docs.oracle.com/javase/6/docs/api/java/security/cert/X509Certificate.html#getSubjectAlternativeNames()