diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs
index f3ea843576..8c6c34f9e0 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs
+++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs
@@ -24,7 +24,11 @@ internal static class Constants
@"vault.azure.net", // default
@"vault.azure.cn", // Azure China
@"vault.usgovcloudapi.net", // US Government
- @"vault.microsoftazure.de" // Azure Germany
+ @"vault.microsoftazure.de", // Azure Germany
+ @"managedhsm.azure.net", // public HSM vault
+ @"managedhsm.azure.cn", // Azure China HSM vault
+ @"managedhsm.usgovcloudapi.net", // US Government HSM vault
+ @"managedhsm.microsoftazure.de" // Azure Germany HSM vault
};
///
diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs
index 4c3c56cee2..2d01efb26d 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs
+++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs
@@ -101,16 +101,16 @@ public SqlColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential,
/// and an array of trusted endpoints.
///
/// Instance of an implementation of Token Credential that is capable of providing an OAuth Token
- /// TrustedEndpoints are used to validate the master key path
- public SqlColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential, string[] trustedEndPoints)
+ /// TrustedEndpoints are used to validate the master key path
+ public SqlColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential, string[] trustedEndpoints)
{
ValidateNotNull(tokenCredential, nameof(tokenCredential));
- ValidateNotNull(trustedEndPoints, nameof(trustedEndPoints));
- ValidateNotEmpty(trustedEndPoints, nameof(trustedEndPoints));
- ValidateNotNullOrWhitespaceForEach(trustedEndPoints, nameof(trustedEndPoints));
+ ValidateNotNull(trustedEndpoints, nameof(trustedEndpoints));
+ ValidateNotEmpty(trustedEndpoints, nameof(trustedEndpoints));
+ ValidateNotNullOrWhitespaceForEach(trustedEndpoints, nameof(trustedEndpoints));
KeyCryptographer = new AzureSqlKeyCryptographer(tokenCredential);
- TrustedEndPoints = trustedEndPoints;
+ TrustedEndPoints = trustedEndpoints;
}
#endregion
@@ -242,7 +242,6 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e
{
// Validate the input parameters
ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true);
- ValidateNotNullOrWhitespace(encryptionAlgorithm, nameof(encryptionAlgorithm));
ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true);
ValidateNotNull(columnEncryptionKey, nameof(columnEncryptionKey));
ValidateNotEmpty(columnEncryptionKey, nameof(columnEncryptionKey));
diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs
index 97e15ebd56..29378fdac8 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs
+++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs
@@ -214,11 +214,11 @@ internal static string InvalidSignatureTemplate
///
/// Looks up a localized string similar to Invalid trusted endpoint specified: '{0}'; a trusted endpoint must have a value..
///
- internal static string InvalidTrustedEndpointTemplate
+ internal static string NullOrWhitespaceForEach
{
get
{
- return ResourceManager.GetString("InvalidTrustedEndpointTemplate", resourceCulture);
+ return ResourceManager.GetString("NullOrWhitespaceForEach", resourceCulture);
}
}
diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx
index 7446876a52..5752720fb0 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx
+++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx
@@ -117,8 +117,8 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- Invalid trusted endpoint specified: '{0}'; a trusted endpoint must have a value.
+
+ One or more of the elements in {0} are null or empty or consist of only whitespace.
CipherText length does not match the RSA key size.
@@ -174,4 +174,4 @@
Internal error. Key encryption algorithm cannot be null.
-
+
\ No newline at end of file
diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Validator.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Validator.cs
index cca06fdea6..f0611dd551 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Validator.cs
+++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Validator.cs
@@ -24,7 +24,7 @@ internal static void ValidateNotNullOrWhitespace(string parameter, string name)
{
if (string.IsNullOrWhiteSpace(parameter))
{
- throw new ArgumentException(name, Strings.NullOrWhitespaceArgument);
+ throw new ArgumentException(string.Format(Strings.NullOrWhitespaceArgument, name));
}
}
@@ -32,18 +32,15 @@ internal static void ValidateNotEmpty(IList parameter, string name)
{
if (parameter.Count == 0)
{
- throw new ArgumentException(name, Strings.EmptyArgumentInternal);
+ throw new ArgumentException(string.Format(Strings.EmptyArgumentInternal, name));
}
}
internal static void ValidateNotNullOrWhitespaceForEach(string[] parameters, string name)
{
- foreach (var parameter in parameters)
+ if (parameters.Any(s => string.IsNullOrWhiteSpace(s)))
{
- if (null == parameter)
- {
- throw new ArgumentException(parameter, Strings.InvalidTrustedEndpointTemplate);
- }
+ throw new ArgumentException(string.Format(Strings.NullOrWhitespaceForEach, name));
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs
index a9503d69de..80c087ee77 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs
@@ -4,6 +4,7 @@
using System;
using System.Security.Cryptography;
+using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup;
using Xunit;
@@ -45,7 +46,7 @@ public void NullEncryptionAlgorithm()
Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, null, cek));
Assert.Matches($@"Internal error. Key encryption algorithm cannot be null.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex1.Message);
Exception ex2 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, null, cek));
- Assert.Matches($@"Key encryption algorithm cannot be null.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex2.Message);
+ Assert.Matches($@"Internal error. Key encryption algorithm cannot be null.\s+\(?Parameter (name: )?'?encryptionAlgorithm('\))?", ex2.Message);
}
@@ -53,28 +54,28 @@ public void NullEncryptionAlgorithm()
public void EmptyColumnEncryptionKey()
{
Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, new byte[] { }));
- Assert.Matches($@"Empty column encryption key specified.\s+\(?Parameter (name: )?'?columnEncryptionKey('\))?", ex1.Message);
+ Assert.Matches($@"Internal error. Empty columnEncryptionKey specified.", ex1.Message);
}
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
public void NullColumnEncryptionKey()
{
Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, null));
- Assert.Matches($@"Column encryption key cannot be null.\s+\(?Parameter (name: )?'?columnEncryptionKey('\))?", ex1.Message);
+ Assert.Matches($@"Value cannot be null..\s+\(?Parameter (name: )?'?columnEncryptionKey('\))?", ex1.Message);
}
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
public void EmptyEncryptedColumnEncryptionKey()
{
Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, new byte[] { }));
- Assert.Matches($@"Internal error. Empty encrypted column encryption key specified.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?", ex1.Message);
+ Assert.Matches($@"Internal error. Empty encryptedColumnEncryptionKey specified", ex1.Message);
}
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
public void NullEncryptedColumnEncryptionKey()
{
Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(DataTestUtility.AKVUrl, MasterKeyEncAlgo, null));
- Assert.Matches($@"Internal error. Encrypted column encryption key cannot be null.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?", ex1.Message);
+ Assert.Matches($@"Value cannot be null.\s+\(?Parameter (name: )?'?encryptedColumnEncryptionKey('\))?", ex1.Message);
}
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
@@ -152,18 +153,31 @@ public void NullAKVKeyPath()
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
public void InvalidCertificatePath()
{
- string dummyPath = @"https://www.microsoft.com";
- string errorMessage = $@"Invalid Azure Key Vault key path specified: '{dummyPath}'. Valid trusted endpoints: vault.azure.net, vault.azure.cn, vault.usgovcloudapi.net, vault.microsoftazure.de.\s+\(?Parameter (name: )?'?masterKeyPath('\))?";
+ string dummyPathWithOnlyHost = @"https://www.microsoft.com";
+ string invalidUrlErrorMessage = $@"Invalid url specified: '{dummyPathWithOnlyHost}'";
+ string dummyPathWithInvalidKey = @"https://www.microsoft.vault.azure.com/keys/dummykey/dummykeyid";
+ string invalidTrustedEndpointErrorMessage = $@"Invalid Azure Key Vault key path specified: '{dummyPathWithInvalidKey}'.
+Valid trusted endpoints: vault.azure.net, vault.azure.cn, vault.usgovcloudapi.net, vault.microsoftazure.de, managedhsm.azure.net,
+managedhsm.azure.cn, managedhsm.usgovcloudapi.net, managedhsm.microsoftazure.de.\s+\(?Parameter (name: )?'?masterKeyPath('\))?";
+
+ Exception ex = Assert.Throws(
+ () => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, cek));
+ Assert.Matches(invalidUrlErrorMessage, ex.Message);
+
+ ex = Assert.Throws(
+ () => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, cek));
+ Assert.Matches(invalidTrustedEndpointErrorMessage, ex.Message);
+
+ ex = Assert.Throws(
+ () => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithOnlyHost, MasterKeyEncAlgo, encryptedCek));
+ Assert.Matches(invalidUrlErrorMessage, ex.Message);
- Exception ex1 = Assert.Throws(() => fixture.AkvStoreProvider.EncryptColumnEncryptionKey(dummyPath, MasterKeyEncAlgo, cek));
- Assert.Matches(errorMessage, ex1.Message);
-
- Exception ex2 = Assert.Throws(
- () => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPath, MasterKeyEncAlgo, encryptedCek));
- Assert.Matches(errorMessage, ex2.Message);
+ ex = Assert.Throws(
+ () => fixture.AkvStoreProvider.DecryptColumnEncryptionKey(dummyPathWithInvalidKey, MasterKeyEncAlgo, encryptedCek));
+ Assert.Matches(invalidTrustedEndpointErrorMessage, ex.Message);
}
- [InlineData(true)]
+ [InlineData(true)]
[InlineData(false)]
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnabled)
@@ -206,5 +220,19 @@ public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnab
tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = cmkSignature[startingByteIndex + randomIndexInCipherText[0]];
}
}
+
+ [InlineData(new object[] { new string[] { null } })]
+ [InlineData(new object[] { new string[] { "" } })]
+ [InlineData(new object[] { new string[] { " " } })]
+ [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsAKVSetupAvailable))]
+ public void InvalidTrustedEndpoints(string[] trustedEndpoints)
+ {
+ Exception ex = Assert.Throws(() =>
+ {
+ SqlColumnEncryptionAzureKeyVaultProvider azureKeyProvider = new SqlColumnEncryptionAzureKeyVaultProvider(
+ new SqlClientCustomTokenCredential(), trustedEndpoints);
+ });
+ Assert.Matches("One or more of the elements in trustedEndpoints are null or empty or consist of only whitespace.", ex.Message);
+ }
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
index 617f905ff7..06c1b2794e 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
@@ -107,7 +107,7 @@ static DataTestUtility()
}
AKVOriginalUrl = c.AzureKeyVaultURL;
- if (!string.IsNullOrEmpty(AKVOriginalUrl) && Uri.TryCreate(AKVOriginalUrl, UriKind.Absolute, out Uri AKVBaseUri))
+ if (!string.IsNullOrEmpty(AKVOriginalUrl) && Uri.TryCreate(AKVOriginalUrl, UriKind.Absolute, out AKVBaseUri))
{
AKVBaseUri = new Uri(AKVBaseUri, "/");
AKVBaseUrl = AKVBaseUri.AbsoluteUri;