Key store providers cannot be set more than once(Always Encrypted with JDBC and Java 8)

Subin Sankaran 1 Reputation point
2021-11-09T04:53:17.773+00:00

We are trying to use SQL Always Encrypted feature in our Java Web Application.

Java 8 is the JRE and uses mssql-jdbc-9.2.1.jre8.jar as the jdbc driver.

Azure-Key-vault as the column master key store provider

Our application uses below code to obtain a DB connection whenever needed(No Connection Pooling).

  String  conStr="jdbc:sqlserver://x.x.x.x:x;DatabaseName=MYDB;User=x;Password=x;columnEncryptionSetting=Enabled;keyStoreAuthentication=KeyVaultClientSecret;keyStorePrincipalId=x-x-x-x-x;keyStoreSecret=x"
    DriverManager.getConnection(conStr);

When we run the application , DB connection is successful on the first connection. From second connection attempt onwards ,we are getting the below exception.

com.microsoft.sqlserver.jdbc.SQLServerException: Key store providers cannot be set more than once.
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.registerColumnEncryptionKeyStoreProviders(SQLServerConnection.java:753)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.registerKeyVaultProvider(SQLServerConnection.java:1414)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.registerKeyStoreProviderOnConnection(SQLServerConnection.java:1388)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1642)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:1291)
 at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:881)
 at java.sql.DriverManager.getConnection(DriverManager.java:664)
 at java.sql.DriverManager.getConnection(DriverManager.java:270)

When I checked code , could find the code which is causing the exception.

public static synchronized void registerColumnEncryptionKeyStoreProviders(
            Map<String, SQLServerColumnEncryptionKeyStoreProvider> clientKeyStoreProviders) throws SQLServerException {
        loggerExternal.entering(loggingClassName, "registerColumnEncryptionKeyStoreProviders",
                "Registering Column Encryption Key Store Providers");

        if (null == clientKeyStoreProviders) {
            throw new SQLServerException(null, SQLServerException.getErrString("R_CustomKeyStoreProviderMapNull"), null,
                    0, false);
        }

        if (null != globalCustomColumnEncryptionKeyStoreProviders
                && !globalCustomColumnEncryptionKeyStoreProviders.isEmpty()) {
            throw new SQLServerException(null, SQLServerException.getErrString("R_CustomKeyStoreProviderSetOnce"), null,
                    0, false);
        }

//continue......
}

Root cause is , globalCustomColumnEncryptionKeyStoreProviders is a static HashMap variable and is populated with the KeyStoreProvider instance on the first connection attempt itself. So from second connection attempt onwards , the exception is thrown since globalCustomColumnEncryptionKeyStoreProviders is not empty.

Can I resolve the issue without changing DriverManager.getConnection() ? Am I missing something here ?

There is public static synchronized void unregisterColumnEncryptionKeyStoreProviders() method in com.microsoft.sqlserver.jdbc.SQLServerConnection class which will clear the globalCustomColumnEncryptionKeyStoreProviders map.
But calling that before DriverManage.getConnection() doesn't seem to solve the issue when there are multiple connection attempts happening in parallel.

SQL Server | Other
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.