diff --git a/build.gradle b/build.gradle index 57db8095e..912e0b0fb 100644 --- a/build.gradle +++ b/build.gradle @@ -85,7 +85,7 @@ if(hasProperty('buildProfile') && buildProfile == "jre8") { } } -jar.archiveFileName = "${archivesBaseName}-${version}.${jreVersion}-preview.jar" +jar.archiveFileName = "${archivesBaseName}-${version}.${jreVersion}.jar" jar { manifest { attributes 'Title': "Microsoft JDBC Driver ${archiveVersion} for SQL Server", @@ -130,7 +130,7 @@ dependencies { implementation 'org.osgi:org.osgi.core:6.0.0', 'org.osgi:org.osgi.compendium:5.0.0' compileOnly 'com.azure:azure-security-keyvault-keys:4.2.8', - 'com.azure:azure-identity:1.3.3', + 'com.azure:azure-identity:1.7.0-beta.2', 'org.antlr:antlr4-runtime:4.9.2', 'com.google.code.gson:gson:2.8.7', 'org.bouncycastle:bcprov-jdk15on:1.69', @@ -152,7 +152,7 @@ dependencies { 'com.google.code.gson:gson:2.8.7', 'org.bouncycastle:bcprov-jdk15on:1.69', 'com.azure:azure-security-keyvault-keys:4.2.8', - 'com.azure:azure-identity:1.3.3', + 'com.azure:azure-identity:1.7.0-beta.2', 'com.microsoft.azure:adal4j:1.6.7', 'com.h2database:h2:1.4.200' } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SecureStringUtil.java b/src/main/java/com/microsoft/sqlserver/jdbc/SecureStringUtil.java index 2ed97efbf..a90f7e8e9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SecureStringUtil.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SecureStringUtil.java @@ -3,6 +3,8 @@ import java.security.SecureRandom; import java.text.MessageFormat; import java.util.Arrays; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -41,7 +43,11 @@ final class SecureStringUtil { private Cipher decryptCipher; /* singleton instance */ - private static SecureStringUtil instance; + private static volatile SecureStringUtil instance; + + private static final Lock INSTANCE_LOCK = new ReentrantLock(); + private static final Lock ENCRYPT_LOCK = new ReentrantLock(); + private static final Lock DECRYPT_LOCK = new ReentrantLock(); /** * Get reference to SecureStringUtil instance @@ -53,7 +59,14 @@ final class SecureStringUtil { */ static SecureStringUtil getInstance() throws SQLServerException { if (instance == null) { - instance = new SecureStringUtil(); + INSTANCE_LOCK.lock(); + try { + if (instance == null) { + instance = new SecureStringUtil(); + } + } finally { + INSTANCE_LOCK.unlock(); + } } return instance; } @@ -90,18 +103,19 @@ private SecureStringUtil() throws SQLServerException { * @return encrypted string * * @throws SQLServerException - * if error + * Throws an exception if the method fails to encrypt the character array */ byte[] getEncryptedBytes(char[] chars) throws SQLServerException { - if (chars == null) - return null; - - byte[] iv = new byte[IV_LENGTH]; - SecureRandom random = new SecureRandom(); - random.nextBytes(iv); - GCMParameterSpec ivParamSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); - + ENCRYPT_LOCK.lock(); try { + if (chars == null) { + return null; + } + + byte[] iv = new byte[IV_LENGTH]; + SecureRandom random = new SecureRandom(); + random.nextBytes(iv); + GCMParameterSpec ivParamSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParamSpec); byte[] cipherText = encryptCipher.doFinal(Util.charsToBytes(chars)); @@ -113,6 +127,8 @@ byte[] getEncryptedBytes(char[] chars) throws SQLServerException { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_EncryptionFailed")); Object[] msgArgs = {e.getMessage()}; throw new SQLServerException(this, form.format(msgArgs), null, 0, false); + } finally { + ENCRYPT_LOCK.unlock(); } } @@ -120,22 +136,25 @@ byte[] getEncryptedBytes(char[] chars) throws SQLServerException { * Get decrypted value of an encrypted string * * @param bytes + * The byte array to decrypt into a character array * * @return decrypted string * * @throws SQLServerException + * Throws an exception if the method fails to decrypt the byte array */ char[] getDecryptedChars(byte[] bytes) throws SQLServerException { - if (bytes == null) - return null; - - byte[] iv = new byte[IV_LENGTH]; - System.arraycopy(bytes, 0, iv, 0, IV_LENGTH); - - GCMParameterSpec ivParamSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); - + DECRYPT_LOCK.lock(); byte[] plainText = null; try { + if (bytes == null) { + return null; + } + + byte[] iv = new byte[IV_LENGTH]; + System.arraycopy(bytes, 0, iv, 0, IV_LENGTH); + + GCMParameterSpec ivParamSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParamSpec); plainText = decryptCipher.doFinal(bytes, IV_LENGTH, bytes.length - IV_LENGTH); @@ -148,6 +167,7 @@ char[] getDecryptedChars(byte[] bytes) throws SQLServerException { if (plainText != null) { Arrays.fill(plainText, (byte) 0); } + DECRYPT_LOCK.unlock(); } } }