From 89f9efd54327c9e05706810af48699a2f689a1c0 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 17 Mar 2020 17:55:08 -0700 Subject: [PATCH 1/4] Provide an option to configure trusted AKV endpoints --- ...ColumnEncryptionAzureKeyVaultProvider.java | 79 ++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 2e21f69fb..0fb2bdf40 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -7,6 +7,8 @@ import static java.nio.charset.StandardCharsets.UTF_16LE; +import java.io.FileInputStream; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; @@ -14,8 +16,12 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import java.util.Properties; import java.util.concurrent.ExecutorService; +import java.util.logging.Level; import com.microsoft.azure.AzureResponseBuilder; import com.microsoft.azure.keyvault.KeyVaultClient; @@ -43,6 +49,8 @@ */ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerColumnEncryptionKeyStoreProvider { + private final static java.util.logging.Logger akvLogger = java.util.logging.Logger + .getLogger("com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionAzureKeyVaultProvider"); /** * Column Encryption Key Store Provider string */ @@ -50,15 +58,12 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol private final String baseUrl = "https://{vaultBaseUrl}"; - /** - * List of Azure trusted endpoints https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault - */ - private final String azureTrustedEndpoints[] = {"vault.azure.net", // default - "vault.azure.cn", // Azure China - "vault.usgovcloudapi.net", // US Government - "vault.microsoftazure.de" // Azure Germany - }; - + private static final String MSSQL_JDBC_PROPERTIES = "mssql-jdbc.properties"; + private static final String AKV_TRUSTED_ENDPOINTS_KEYWORD = "AKVTrustedEndpoints"; + private static final List akvTrustedEndpoints; + static { + akvTrustedEndpoints = getTrustedEndpoints(); + } private final String rsaEncryptionAlgorithmWithOAEPForAKV = "RSA-OAEP"; /** @@ -455,7 +460,7 @@ private void ValidateNonEmptyAKVPath(String masterKeyPath) throws SQLServerExcep if (null != host) { host = host.toLowerCase(Locale.ENGLISH); } - for (final String endpoint : azureTrustedEndpoints) { + for (final String endpoint : akvTrustedEndpoints) { if (null != host && host.endsWith(endpoint)) { return; } @@ -628,4 +633,58 @@ public boolean verifyColumnMasterKeyMetadata(String masterKeyPath, boolean allow throw new SQLServerException(SQLServerException.getErrString("R_NoSHA256Algorithm"), e); } } + + private static List getTrustedEndpoints() { + Properties mssqlJdbcProperties = getMssqlJdbcProperties(); + List trustedEndpoints = new ArrayList(); + boolean append = true; + if (null != mssqlJdbcProperties) { + String endpoints = mssqlJdbcProperties.getProperty(AKV_TRUSTED_ENDPOINTS_KEYWORD); + if (null != endpoints && !endpoints.isBlank()) { + endpoints = endpoints.trim(); + // Append if the list starts with a semicolon. + if (';' != endpoints.charAt(0)) { + append = false; + } else { + endpoints = endpoints.substring(1); + } + String[] entries = endpoints.split(";"); + for (String entry : entries) { + if (null != entry && !entry.isBlank()) { + trustedEndpoints.add(entry.trim()); + } + } + } + } + /* + * List of Azure trusted endpoints + * https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault + */ + if (append) { + trustedEndpoints.add("vault.azure.net"); + trustedEndpoints.add("vault.azure.cn"); + trustedEndpoints.add("vault.usgovcloudapi.net"); + trustedEndpoints.add("vault.microsoftazure.de"); + } + System.out.println(trustedEndpoints); + return trustedEndpoints; + } + + /** + * Attempt to read MSSQL_JDBC_PROPERTIES. + * + * @return corresponding Properties object or null if failed to read the file. + */ + private static Properties getMssqlJdbcProperties() { + Properties props = null; + try (FileInputStream in = new FileInputStream(MSSQL_JDBC_PROPERTIES)) { + props = new Properties(); + props.load(in); + } catch (IOException e) { + if (akvLogger.isLoggable(Level.FINER)) { + akvLogger.finer("Unable to load the mssql-jdbc.properties file: " + e); + } + } + return (null != props && !props.isEmpty()) ? props : null; + } } From 563ee0168f8fe011adec4fef0878ca9220fe06d3 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 17 Mar 2020 19:17:53 -0700 Subject: [PATCH 2/4] Fix | Remove debug line --- .../jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 0fb2bdf40..c673728fe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -666,7 +666,6 @@ private static List getTrustedEndpoints() { trustedEndpoints.add("vault.usgovcloudapi.net"); trustedEndpoints.add("vault.microsoftazure.de"); } - System.out.println(trustedEndpoints); return trustedEndpoints; } From 372ea3f962584fb6d9a0f2f4ed50968b75142747 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 18 Mar 2020 16:22:09 -0700 Subject: [PATCH 3/4] Testing scenario when appending endpoint from properties file --- .../sqlserver/testframework/AbstractTest.java | 21 +++++++++++++++++-- .../sqlserver/testframework/Constants.java | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index 246785546..987ac12f2 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -6,9 +6,12 @@ package com.microsoft.sqlserver.testframework; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintStream; import java.sql.Connection; import java.sql.ResultSet; @@ -145,8 +148,22 @@ public static void setup() throws Exception { } if (null == akvProvider) { - akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID, applicationKey); - map.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider); + File file = null; + try { + file = new File("mssql-jdbc.properties"); + try (OutputStream os = new FileOutputStream(file);) { + Properties props = new Properties(); + // Append to the list of hardcoded endpoints. + props.setProperty("AKVTrustedEndpoints", ";vault.azure.net"); + props.store(os, ""); + } + akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID, applicationKey); + map.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider); + } finally { + if (null != file) { + file.delete(); + } + } } if (!isKspRegistered) { diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Constants.java b/src/test/java/com/microsoft/sqlserver/testframework/Constants.java index e6f16958e..b7d452bc4 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Constants.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Constants.java @@ -43,6 +43,8 @@ private Constants() {} public static final String JDBC_PREFIX = "jdbc:sqlserver://"; public static final String DEFAULT_DRIVER_LOG = "Driver.log"; public static final String MSSQL_JDBC_PACKAGE = "com.microsoft.sqlserver.jdbc"; + public static final String MSSQL_JDBC_PROPERTIES = "mssql-jdbc.properties"; + public static final String AKV_TRUSTED_ENDPOINTS_KEYWORD = "AKVTrustedEndpoints"; public static final String DEFAULT_WRAP_IDENTIFIER = "\'"; public static final String CREATE_TABLE = "CREATE TABLE"; From 9808a13c0429bd34efb9a1f88ac50c0802768701 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 18 Mar 2020 16:30:56 -0700 Subject: [PATCH 4/4] User constants --- .../com/microsoft/sqlserver/testframework/AbstractTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index 987ac12f2..159f382f8 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -150,11 +150,11 @@ public static void setup() throws Exception { if (null == akvProvider) { File file = null; try { - file = new File("mssql-jdbc.properties"); + file = new File(Constants.MSSQL_JDBC_PROPERTIES); try (OutputStream os = new FileOutputStream(file);) { Properties props = new Properties(); // Append to the list of hardcoded endpoints. - props.setProperty("AKVTrustedEndpoints", ";vault.azure.net"); + props.setProperty(Constants.AKV_TRUSTED_ENDPOINTS_KEYWORD, ";vault.azure.net"); props.store(os, ""); } akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID, applicationKey);