From f953099a122de7def1cc5a509a6d2444a350c763 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Fri, 29 Sep 2017 12:49:20 -0700 Subject: [PATCH 1/2] update AKV to v1.0.0 --- pom.xml | 2 +- .../sqlserver/jdbc/KeyVaultCredential.java | 76 +++++++------- ...ColumnEncryptionAzureKeyVaultProvider.java | 98 +++++++------------ ...LServerKeyVaultAuthenticationCallback.java | 27 ----- .../sqlserver/jdbc/SQLServerResource.java | 1 + 5 files changed, 77 insertions(+), 127 deletions(-) delete mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java diff --git a/pom.xml b/pom.xml index edee813ed..d6ad0bb8a 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ com.microsoft.azure azure-keyvault - 0.9.7 + 1.0.0 true diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java index 70d99421a..8ca52a717 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultCredential.java @@ -8,13 +8,14 @@ package com.microsoft.sqlserver.jdbc; -import java.util.Map; - -import org.apache.http.Header; -import org.apache.http.message.BasicHeader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials; -import com.microsoft.windowsazure.core.pipeline.filter.ServiceRequestContext; /** * @@ -22,43 +23,46 @@ * */ class KeyVaultCredential extends KeyVaultCredentials { - - // this is the only supported access token type - // https://msdn.microsoft.com/en-us/library/azure/dn645538.aspx - private final String accessTokenType = "Bearer"; - - SQLServerKeyVaultAuthenticationCallback authenticationCallback = null; String clientId = null; String clientKey = null; - String accessToken = null; - KeyVaultCredential(SQLServerKeyVaultAuthenticationCallback authenticationCallback) { - this.authenticationCallback = authenticationCallback; + KeyVaultCredential(String clientId, + String clientKey) { + this.clientId = clientId; + this.clientKey = clientKey; } - /** - * Authenticates the service request - * - * @param request - * the ServiceRequestContext - * @param challenge - * used to get the accessToken - * @return BasicHeader - */ - @Override - public Header doAuthenticate(ServiceRequestContext request, - Map challenge) { - assert null != challenge; - - String authorization = challenge.get("authorization"); - String resource = challenge.get("resource"); - - accessToken = authenticationCallback.getAccessToken(authorization, resource, ""); - return new BasicHeader("Authorization", accessTokenType + " " + accessToken); + public String doAuthenticate(String authorization, + String resource, + String scope) { + AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey); + return token.getAccessToken(); } - void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } + private static AuthenticationResult getAccessTokenFromClientCredentials(String authorization, + String resource, + String clientId, + String clientKey) { + AuthenticationContext context = null; + AuthenticationResult result = null; + ExecutorService service = null; + try { + service = Executors.newFixedThreadPool(1); + context = new AuthenticationContext(authorization, false, service); + ClientCredential credentials = new ClientCredential(clientId, clientKey); + Future future = context.acquireToken(resource, credentials, null); + result = future.get(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + finally { + service.shutdown(); + } + if (result == null) { + throw new RuntimeException("authentication result was null"); + } + return result; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 9a1d89677..ab128f520 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -17,15 +17,12 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -import org.apache.http.impl.client.HttpClientBuilder; import com.microsoft.azure.keyvault.KeyVaultClient; -import com.microsoft.azure.keyvault.KeyVaultClientImpl; import com.microsoft.azure.keyvault.models.KeyBundle; import com.microsoft.azure.keyvault.models.KeyOperationResult; +import com.microsoft.azure.keyvault.models.KeyVerifyResult; +import com.microsoft.azure.keyvault.webkey.JsonWebKeyEncryptionAlgorithm; import com.microsoft.azure.keyvault.webkey.JsonWebKeySignatureAlgorithm; /** @@ -66,26 +63,19 @@ public String getName() { } /** - * Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key - * Vault. + * Constructor that authenticates to AAD. This is used by KeyVaultClient at runtime to authenticate to Azure Key * - * @param authenticationCallback - * - Callback function used for authenticating to AAD. - * @param executorService - * - The ExecutorService used to create the keyVaultClient + * @param clientId + * Identifier of the client requesting the token. + * @param clientKey + * Key of the client requesting the token. * @throws SQLServerException * when an error occurs */ - public SQLServerColumnEncryptionAzureKeyVaultProvider(SQLServerKeyVaultAuthenticationCallback authenticationCallback, - ExecutorService executorService) throws SQLServerException { - if (null == authenticationCallback) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); - Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; - throw new SQLServerException(form.format(msgArgs1), null); - } - credential = new KeyVaultCredential(authenticationCallback); - HttpClientBuilder builder = HttpClientBuilder.create(); - keyVaultClient = new KeyVaultClientImpl(builder, executorService, credential); + public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, + String clientKey) throws SQLServerException { + credential = new KeyVaultCredential(clientId, clientKey); + keyVaultClient = new KeyVaultClient(credential); } /** @@ -308,7 +298,7 @@ public byte[] encryptColumnEncryptionKey(String masterKeyPath, byte dataToSign[] = md.digest(); // Sign the hash - byte[] signedHash = AzureKeyVaultSignHashedData(dataToSign, masterKeyPath); + byte[] signedHash = AzureKeyVaultSignHashedData(dataToSign, masterKeyPath); if (signedHash.length != keySizeInBytes) { throw new SQLServerException(SQLServerException.getErrString("R_SignedHashLengthError"), null); @@ -433,14 +423,10 @@ private byte[] AzureKeyVaultWrap(String masterKeyPath, throw new SQLServerException(SQLServerException.getErrString("R_CEKNull"), null); } - KeyOperationResult wrappedKey = null; - try { - wrappedKey = keyVaultClient.wrapKeyAsync(masterKeyPath, encryptionAlgorithm, columnEncryptionKey).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_EncryptCEKError"), e); - } - return wrappedKey.getResult(); + JsonWebKeyEncryptionAlgorithm jsonEncryptionAlgorithm = new JsonWebKeyEncryptionAlgorithm(encryptionAlgorithm); + KeyOperationResult wrappedKey = keyVaultClient.wrapKey(masterKeyPath, jsonEncryptionAlgorithm, columnEncryptionKey); + + return wrappedKey.result(); } /** @@ -466,14 +452,10 @@ private byte[] AzureKeyVaultUnWrap(String masterKeyPath, throw new SQLServerException(SQLServerException.getErrString("R_EmptyEncryptedCEK"), null); } - KeyOperationResult unwrappedKey; - try { - unwrappedKey = keyVaultClient.unwrapKeyAsync(masterKeyPath, encryptionAlgorithm, encryptedColumnEncryptionKey).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_DecryptCEKError"), e); - } - return unwrappedKey.getResult(); + JsonWebKeyEncryptionAlgorithm jsonEncryptionAlgorithm = new JsonWebKeyEncryptionAlgorithm(encryptionAlgorithm); + KeyOperationResult unwrappedKey = keyVaultClient.unwrapKey(masterKeyPath, jsonEncryptionAlgorithm, encryptedColumnEncryptionKey); + + return unwrappedKey.result(); } /** @@ -490,14 +472,9 @@ private byte[] AzureKeyVaultSignHashedData(byte[] dataToSign, String masterKeyPath) throws SQLServerException { assert ((null != dataToSign) && (0 != dataToSign.length)); - KeyOperationResult signedData = null; - try { - signedData = keyVaultClient.signAsync(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToSign).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_GenerateSignature"), e); - } - return signedData.getResult(); + KeyOperationResult signedData = keyVaultClient.sign(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToSign); + + return signedData.result(); } /** @@ -516,15 +493,9 @@ private boolean AzureKeyVaultVerifySignature(byte[] dataToVerify, assert ((null != dataToVerify) && (0 != dataToVerify.length)); assert ((null != signature) && (0 != signature.length)); - boolean valid = false; - try { - valid = keyVaultClient.verifyAsync(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToVerify, signature).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_VerifySignature"), e); - } + KeyVerifyResult valid = keyVaultClient.verify(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToVerify, signature); - return valid; + return valid.value(); } /** @@ -537,21 +508,22 @@ private boolean AzureKeyVaultVerifySignature(byte[] dataToVerify, * when an error occurs */ private int getAKVKeySize(String masterKeyPath) throws SQLServerException { + KeyBundle retrievedKey = keyVaultClient.getKey(masterKeyPath); - KeyBundle retrievedKey = null; - try { - retrievedKey = keyVaultClient.getKeyAsync(masterKeyPath).get(); - } - catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_GetAKVKeySize"), e); + if (null == retrievedKey) { + String[] keyTokens = masterKeyPath.split("/"); + + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AKVKeyNotFound")); + Object[] msgArgs = {keyTokens[keyTokens.length - 1]}; + throw new SQLServerException(null, form.format(msgArgs), null, 0, false); } - if (!"RSA".equalsIgnoreCase(retrievedKey.getKey().getKty()) && !"RSA-HSM".equalsIgnoreCase(retrievedKey.getKey().getKty())) { + if (!"RSA".equalsIgnoreCase(retrievedKey.key().kty().toString()) && !"RSA-HSM".equalsIgnoreCase(retrievedKey.key().kty().toString())) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NonRSAKey")); - Object[] msgArgs = {retrievedKey.getKey().getKty()}; + Object[] msgArgs = {retrievedKey.key().kty().toString()}; throw new SQLServerException(null, form.format(msgArgs), null, 0, false); } - return retrievedKey.getKey().getN().length; + return retrievedKey.key().n().length; } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java deleted file mode 100644 index c940dd8eb..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerKeyVaultAuthenticationCallback.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.jdbc; - -public interface SQLServerKeyVaultAuthenticationCallback { - - /** - * The authentication callback delegate which is to be implemented by the client code - * - * @param authority - * - Identifier of the authority, a URL. - * @param resource - * - Identifier of the target resource that is the recipient of the requested token, a URL. - * @param scope - * - The scope of the authentication request. - * @return access token - */ - public String getAccessToken(String authority, - String resource, - String scope); -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index d4cc1bb7d..a02b087aa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -383,5 +383,6 @@ protected Object[][] getContents() { {"R_kerberosLoginFailed", "Kerberos Login failed: {0} due to {1} ({2})"}, {"R_StoredProcedureNotFound", "Could not find stored procedure ''{0}''."}, {"R_jaasConfigurationNamePropertyDescription", "Login configuration file for Kerberos authentication."}, + {"R_AKVKeyNotFound", "Key not found: {0}"}, }; } From f1ab1f994e8b8658478202cafc8c5355470b6f28 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Fri, 29 Sep 2017 12:49:53 -0700 Subject: [PATCH 2/2] update ADAL4J to 1.2.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d6ad0bb8a..dba42955b 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ com.microsoft.azure adal4j - 1.1.3 + 1.2.0 true