Skip to content

Commit

Permalink
Add encrypt utility to obfuscate password strings (#1780)
Browse files Browse the repository at this point in the history
  • Loading branch information
lilgreenbird committed Mar 31, 2022
1 parent 4337c57 commit 17b4799
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 5 deletions.
8 changes: 6 additions & 2 deletions src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1582,8 +1582,12 @@ void enableSSL(String host, int port, String clientCertificate, String clientKey

String trustStoreFileName = con.activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.TRUST_STORE.toString());
String trustStorePassword = con.activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString());

String trustStorePassword = null;
if (con.encryptedTrustStorePassword != null) {
trustStorePassword = SecureStringUtil.getInstance().getDecryptedString(con.encryptedTrustStorePassword);
}

String hostNameInCertificate = con.activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
/** flag indicating whether prelogin TLS handshake is required */
private boolean isTDSS = false;

String encryptedTrustStorePassword = null;

/**
* Return an existing cached SharedTimer associated with this Connection or create a new one.
*
Expand Down Expand Up @@ -1826,6 +1828,13 @@ Connection connectInternal(Properties propsIn,

pooledConnectionParent = pooledConnection;

String trustStorePassword = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString());
if (trustStorePassword != null) {
encryptedTrustStorePassword = SecureStringUtil.getInstance().getEncryptedString(trustStorePassword);
activeConnectionProperties.remove(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString());
}

String hostNameInCertificate = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ protected Object[][] getContents() {
{"R_InvalidCipherTextSize", "Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption."},
{"R_InvalidAlgorithmVersion", "The specified ciphertext''s encryption algorithm version {0} does not match the expected encryption algorithm version {1} ."},
{"R_InvalidAuthenticationTag", "Specified ciphertext has an invalid authentication tag. "},
{"R_EncryptionFailed", "Internal error while encryption: {0} "},
{"R_DecryptionFailed", "Internal error while decryption: {0} "},
{"R_EncryptionFailed", "Internal error during encryption: {0} "},
{"R_DecryptionFailed", "Internal error during decryption: {0} "},
{"R_InvalidKeySize", "The column encryption key has been successfully decrypted but it''s length: {0} does not match the length: {1} for algorithm \"{2}\". Verify the encrypted value of the column encryption key in the database."},
{"R_InvalidEncryptionType", "Encryption type {0} specified for the column in the database is either invalid or corrupted. Valid encryption types for algorithm {1} are: {2}."},
{"R_UnknownColumnEncryptionAlgorithm", "The Algorithm {0} does not exist. Algorithms registered in the factory are {1}."},
Expand Down Expand Up @@ -507,6 +507,7 @@ protected Object[][] getContents() {
{"R_UnableLoadAuthDll", "Unable to load authentication DLL {0}"},
{"R_illegalArgumentTrustManager", "Interal error. Peer certificate chain or key exchange algorithem can not be null or empty."},
{"R_serverCertError", "Error validating Server Certificate: {0}."},
{"R_SecureStringInitFailed", "Failed to initialize SecureStringUtil to store secure strings"},
};
}
// @formatter:on
139 changes: 139 additions & 0 deletions src/main/java/com/microsoft/sqlserver/jdbc/SecureStringUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.microsoft.sqlserver.jdbc;

import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;


/**
*
* This is an utility class to encrypt/decrypt strings. This is used to obfuscate passwords so they won't be visible as
* plaintext.
*/
final class SecureStringUtil {
/* cipher transformation in the form of algorithm/mode/padding */
static final String CIPHER_TRANSFORMATION = "AES/GCM/NoPadding";

/* key generator algorithm */
static final String KEYGEN_ALGORITHEM = "AES";

/* length of initialization vector buffer */
static final int IV_LENGTH = 12;

/* key size */
static final int KEY_SIZE = 256;

/* authentication tag length in bits */
static final int TAG_LENGTH = 16;

/* initialization vector */
byte[] iv;

/** secret key for encryption/decryption */
SecretKeySpec secretKey;

/* cryptographic cipher for encryption */
private Cipher encryptCipher;

/* cryptographic cipher for decryption */
private Cipher decryptCipher;

/* singleton instance */
private static SecureStringUtil instance;

/**
* Get reference to SecureStringUtil instance
*
* @return the SecureStringUtil instance
*
* @throws SQLServerException
* if error
*/
static SecureStringUtil getInstance() throws SQLServerException {
if (instance == null) {
instance = new SecureStringUtil();
}
return instance;
}

/**
* Creates an instance of the SecureStringUtil object and initialize values to encrypt/decrypt strings
*
* @throws SQLServerException
* if error
*/
private SecureStringUtil() throws SQLServerException {
iv = new byte[IV_LENGTH];
try {
// generate key */
KeyGenerator keygen = KeyGenerator.getInstance(KEYGEN_ALGORITHEM);
keygen.init(KEY_SIZE);
secretKey = new SecretKeySpec(keygen.generateKey().getEncoded(), "AES");

// get ciphers for encryption/decryption
encryptCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
decryptCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
} catch (Exception e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_SecureStringInitFailed"));
Object[] msgArgs = {e.getMessage()};
throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
}
}

/**
* Get encrypted value of given string
*
* @param str
* string to encrypt
*
* @return encrypted string
*
* @throws SQLServerException
* if error
*/
String getEncryptedString(String str) throws SQLServerException {
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
GCMParameterSpec ivParamSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv);

try {
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParamSpec);

byte[] cipherText = encryptCipher.doFinal(str.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
} catch (Exception e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_EncryptionFailed"));
Object[] msgArgs = {e.getMessage()};
throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
}
}

/**
* Get decrypted value of an encrypted string
*
* @param str
*
* @return decrypted string
*
* @throws SQLServerException
*/
String getDecryptedString(String str) throws SQLServerException {
GCMParameterSpec ivParamSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv);

try {
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParamSpec);

byte[] plainText = decryptCipher.doFinal(Base64.getDecoder().decode(str));
return new String(plainText);
} catch (Exception e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DecryptionFailed"));
Object[] msgArgs = {e.getMessage()};
throw new SQLServerException(this, form.format(msgArgs), null, 0, false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ public void testIncorrectDatabase() throws SQLException {
assertTrue(timeDiff <= milsecs, form.format(msgArgs));
}
} catch (Exception e) {
assertTrue(e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")));
assertTrue(e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")), e.getMessage());
timerEnd = System.currentTimeMillis();
}
}
Expand Down

0 comments on commit 17b4799

Please sign in to comment.