From 9cbc52009585b064e3ec78e4f8c8f0eb36f9a9da Mon Sep 17 00:00:00 2001 From: Sehrope Sarkuni Date: Tue, 6 Dec 2016 18:01:25 -0500 Subject: [PATCH] Add connection properties to specify a custom TrustManager Adds two new connection properties that can be used to specify a custom TrustManager implementation: trustManagerClass - Class name of the custom TrustManager trustManagerConstructorArg - Optional argument to pass to the constructor of the custom TrustManager. If encryption is enabled and the trustManagerClass property is specified, it will be retrieved via Class.forName(...). If the optional property trustManagerConstructorArg is specified, then a constructor will be retrieved via getDeclaredConstructors(String.class). The TrustManager will then be instantiated by specified the optional argument as a parameter. If the optional property trustManagerConstructorArg is not specfied, then the default no argument constructor of the class will be retrieved and instantiated. --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 24 ++++++++++++++++++- .../sqlserver/jdbc/SQLServerConnection.java | 17 +++++++++++++ .../sqlserver/jdbc/SQLServerDriver.java | 4 ++++ .../sqlserver/jdbc/SQLServerResource.java | 2 ++ 4 files changed, 46 insertions(+), 1 deletion(-) 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."},