com.microsoft.sqlserver.jdbc.SQLServerSecurityUtility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mssql-jdbc Show documentation
Show all versions of mssql-jdbc Show documentation
Microsoft JDBC Driver for SQL Server.
/*
* 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;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* Various SQLServer security utilities.
*
*/
class SQLServerSecurityUtility {
/**
* Give the hash of given plain text
*
* @param plainText
* @param key
* @param length
* @return hash of the plain text using provided key
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
static byte[] getHMACWithSHA256(byte[] plainText, byte[] key,
int length) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] computedHash;
byte[] hash = new byte[length];
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec ivkeySpec = new SecretKeySpec(key, "HmacSHA256");
mac.init(ivkeySpec);
computedHash = mac.doFinal(plainText);
// truncating hash if needed
System.arraycopy(computedHash, 0, hash, 0, hash.length);
return hash;
}
/**
* Compare two arrays
*
* @param buffer1
* first array
* @param buffer2
* second array
* @param buffer2Index
* @param lengthToCompare
* @return true if array contains same bytes otherwise false
*/
static boolean compareBytes(byte[] buffer1, byte[] buffer2, int buffer2Index, int lengthToCompare) {
if (null == buffer1 || null == buffer2) {
return false;
}
if ((buffer2.length - buffer2Index) < lengthToCompare) {
return false;
}
for (int index = 0; index < buffer1.length && index < lengthToCompare; ++index) {
if (buffer1[index] != buffer2[buffer2Index + index]) {
return false;
}
}
return true;
}
/*
* Encrypts the ciphertext.
*/
static byte[] encryptWithKey(byte[] plainText, CryptoMetadata md,
SQLServerConnection connection) throws SQLServerException {
String serverName = connection.getTrustedServerNameAE();
assert serverName != null : "Server name should npt be null in EncryptWithKey";
// Initialize cipherAlgo if not already done.
if (!md.IsAlgorithmInitialized()) {
SQLServerSecurityUtility.decryptSymmetricKey(md, connection);
}
assert md.IsAlgorithmInitialized();
byte[] cipherText = md.cipherAlgorithm.encryptData(plainText); // this call succeeds or throws.
if (null == cipherText || 0 == cipherText.length) {
throw new SQLServerException(null, SQLServerException.getErrString("R_NullCipherTextAE"), null, 0, false);
}
return cipherText;
}
/**
* Return the algorithm name mapped to an Id
*
* @param cipherAlgorithmId
* The cipher algorithm Id
* @param cipherAlgorithmName
* The cipher algorithm name
* @return The cipher algorithm name
*/
private static String ValidateAndGetEncryptionAlgorithmName(byte cipherAlgorithmId,
String cipherAlgorithmName) throws SQLServerException {
// Custom cipher algorithm not supported for CTP.
if (TDS.AEAD_AES_256_CBC_HMAC_SHA256 != cipherAlgorithmId) {
throw new SQLServerException(null, SQLServerException.getErrString("R_CustomCipherAlgorithmNotSupportedAE"),
null, 0, false);
}
return SQLServerAeadAes256CbcHmac256Algorithm.algorithmName;
}
/**
* Decrypts the symmetric key and saves it in metadata. In addition, initializes the SqlClientEncryptionAlgorithm
* for rapid decryption.
*
* @param md
* The cipher metadata
* @param connection
* The connection
*/
static void decryptSymmetricKey(CryptoMetadata md, SQLServerConnection connection) throws SQLServerException {
assert null != md : "md should not be null in DecryptSymmetricKey.";
assert null != md.cekTableEntry : "md.EncryptionInfo should not be null in DecryptSymmetricKey.";
assert null != md.cekTableEntry.columnEncryptionKeyValues : "md.EncryptionInfo.ColumnEncryptionKeyValues should not be null in DecryptSymmetricKey.";
SQLServerSymmetricKey symKey = null;
EncryptionKeyInfo encryptionkeyInfoChosen = null;
SQLServerSymmetricKeyCache cache = SQLServerSymmetricKeyCache.getInstance();
Iterator it = md.cekTableEntry.columnEncryptionKeyValues.iterator();
SQLServerException lastException = null;
while (it.hasNext()) {
EncryptionKeyInfo keyInfo = it.next();
try {
symKey = cache.getKey(keyInfo, connection);
if (null != symKey) {
encryptionkeyInfoChosen = keyInfo;
break;
}
} catch (SQLServerException e) {
lastException = e;
}
}
if (null == symKey) {
if (null != lastException) {
throw lastException;
} else {
throw new SQLServerException(null, SQLServerException.getErrString("R_CEKDecryptionFailed"), null, 0,
false);
}
}
// Given the symmetric key instantiate a SqlClientEncryptionAlgorithm object and cache it in metadata.
md.cipherAlgorithm = null;
SQLServerEncryptionAlgorithm cipherAlgorithm = null;
String algorithmName = ValidateAndGetEncryptionAlgorithmName(md.cipherAlgorithmId, md.cipherAlgorithmName); // may
// throw
cipherAlgorithm = SQLServerEncryptionAlgorithmFactoryList.getInstance().getAlgorithm(symKey, md.encryptionType,
algorithmName); // will
// validate
// algorithm
// name and
// type
assert null != cipherAlgorithm : "Cipher algorithm cannot be null in DecryptSymmetricKey";
md.cipherAlgorithm = cipherAlgorithm;
md.encryptionKeyInfo = encryptionkeyInfoChosen;
}
/*
* Decrypts the ciphertext.
*/
static byte[] decryptWithKey(byte[] cipherText, CryptoMetadata md,
SQLServerConnection connection) throws SQLServerException {
String serverName = connection.getTrustedServerNameAE();
assert null != serverName : "serverName should not be null in DecryptWithKey.";
// Initialize cipherAlgo if not already done.
if (!md.IsAlgorithmInitialized()) {
SQLServerSecurityUtility.decryptSymmetricKey(md, connection);
}
assert md.IsAlgorithmInitialized() : "Decryption Algorithm is not initialized";
byte[] plainText = md.cipherAlgorithm.decryptData(cipherText); // this call succeeds or throws.
if (null == plainText) {
throw new SQLServerException(null, SQLServerException.getErrString("R_PlainTextNullAE"), null, 0, false);
}
return plainText;
}
/*
* Verify the signature for the CMK
*/
static void verifyColumnMasterKeyMetadata(SQLServerConnection connection, String keyStoreName, String keyPath,
String serverName, boolean isEnclaveEnabled, byte[] CMKSignature) throws SQLServerException {
// check trusted key paths
Boolean[] hasEntry = new Boolean[1];
List trustedKeyPaths = SQLServerConnection.getColumnEncryptionTrustedMasterKeyPaths(serverName,
hasEntry);
if (hasEntry[0]) {
if ((null == trustedKeyPaths) || (0 == trustedKeyPaths.size()) || (!trustedKeyPaths.contains(keyPath))) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UntrustedKeyPath"));
Object[] msgArgs = {keyPath, serverName};
throw new SQLServerException(form.format(msgArgs), null);
}
}
if (!connection.getColumnEncryptionKeyStoreProvider(keyStoreName).verifyColumnMasterKeyMetadata(keyPath,
isEnclaveEnabled, CMKSignature)) {
throw new SQLServerException(SQLServerException.getErrString("R_VerifySignature"), null);
}
}
}