diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index f31bcdc6c..14d5edb5c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -1605,7 +1605,21 @@ void enableSSL(String host, int port) throws SQLServerException tm = new TrustManager[] { new PermissiveX509TrustManager(this) }; } - + // Otherwise, we'll check if a specific TrustManager implemenation has been requested and + // if so instantiate it, optionally specifying a constructor argument to customize it. + else if (con.getTrustManagerClass() != null) + { + Class tmClazz = Class.forName(con.getTrustManagerClass()); + if (!TrustManager.class.isAssignableFrom(tmClazz)) { + throw new IllegalArgumentException("The class specified by the trustManagerClass property must implement javax.net.ssl.TrustManager"); + } + String constructorArg = con.getTrustManagerConstructorArg(); + if (constructorArg == null) { + tm = new TrustManager[] {(TrustManager) tmClazz.getDeclaredConstructor().newInstance()}; + } else { + tm = new TrustManager[] {(TrustManager) tmClazz.getDeclaredConstructor(String.class).newInstance(constructorArg)}; + } + } // Otherwise, we'll validate the certificate using a real TrustManager obtained // from the a security provider that is capable of validating X.509 certificates. else @@ -1801,6 +1815,14 @@ void enableSSL(String host, int port) throws SQLServerException // It is important to get the localized message here, otherwise error messages won't match for different locales. String errMsg = e.getLocalizedMessage(); + // If the message is null replace it with the non-localized message or a dummy string. This can happen if a custom + // TrustManager implementation is specified that does not provide localized messages. + if (errMsg == null) { + errMsg = e.getMessage(); + } + if (errMsg == null) { + errMsg = ""; + } // The error message may have a connection id appended to it. Extract the message only for comparison. // This client connection id is appended in method checkAndAppendClientConnId(). diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 05ef07ef2..bccb667b9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -282,6 +282,20 @@ final byte getNegotiatedEncryptionLevel() return negotiatedEncryptionLevel; } + private String trustManagerClass = null; + final String getTrustManagerClass() + { + assert TDS.ENCRYPT_INVALID != requestedEncryptionLevel; + return trustManagerClass; + } + + private String trustManagerConstructorArg = null; + final String getTrustManagerConstructorArg() + { + assert TDS.ENCRYPT_INVALID != requestedEncryptionLevel; + return trustManagerConstructorArg; + } + static final String RESERVED_PROVIDER_NAME_PREFIX = "MSSQL_"; String columnEncryptionSetting = null; boolean isColumnEncryptionSettingEnabled() { @@ -1177,6 +1191,9 @@ Connection connectInternal(Properties propsIn, SQLServerPooledConnection pooledC trustServerCertificate = booleanPropertyOn(sPropKey,sPropValue); + trustManagerClass = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString()); + trustManagerConstructorArg = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString()); + sPropKey = SQLServerDriverStringProperty.SELECT_METHOD.toString(); sPropValue = activeConnectionProperties.getProperty(sPropKey); if (sPropValue == null) sPropValue = SQLServerDriverStringProperty.SELECT_METHOD.getDefaultValue(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index bf2ec1601..14d7c5fd4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -242,6 +242,8 @@ enum SQLServerDriverStringProperty SERVER_SPN("serverSpn", ""), TRUST_STORE("trustStore", ""), TRUST_STORE_PASSWORD("trustStorePassword", ""), + TRUST_MANAGER_CLASS("trustManagerClass", ""), + TRUST_MANAGER_CONSTRUCTOR_ARG("trustManagerConstructorArg", ""), USER("user", ""), WORKSTATION_ID("workstationID", Util.WSIDNotAvailable), AUTHENTICATION_SCHEME("authenticationScheme", AuthenticationScheme.nativeAuthentication.toString()), @@ -375,6 +377,8 @@ public final class SQLServerDriver implements java.sql.Driver new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.toString(), Boolean.toString(SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.getDefaultValue()), false, TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.TRUST_STORE.toString(), SQLServerDriverStringProperty.TRUST_STORE.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString(), SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.getDefaultValue(), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString(), SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.getDefaultValue(), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString(), SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.toString(), Boolean.toString(SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue()), false, TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.USER.toString(), SQLServerDriverStringProperty.USER.getDefaultValue(), true, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.WORKSTATION_ID.toString(), SQLServerDriverStringProperty.WORKSTATION_ID.getDefaultValue(), false, null), diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 793f21f27..f5af8c870 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -196,6 +196,8 @@ protected Object[][] getContents() {"R_trustServerCertificatePropertyDescription", "Determines if the driver should validate the SQL Server Secure Sockets Layer (SSL) certificate."}, {"R_trustStorePropertyDescription", "The path to the certificate trust store file."}, {"R_trustStorePasswordPropertyDescription", "The password used to check the integrity of the trust store data."}, + {"R_trustManagerClassPropertyDescription", "The class to instantiate as the TrustManager for SSL connections."}, + {"R_trustManagerConstructorArgPropertyDescription", "The optional argument to pass to the constructor specified by trustManagerClass."}, {"R_hostNameInCertificatePropertyDescription", "The host name to be used when validating the SQL Server Secure Sockets Layer (SSL) certificate."}, {"R_sendTimeAsDatetimePropertyDescription", "Determines whether to use the SQL Server datetime data type to send java.sql.Time values to the database."}, {"R_TransparentNetworkIPResolutionPropertyDescription", "Determines whether to use the Transparent Network IP Resolution feature."},