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.
//---------------------------------------------------------------------------------------------------------------------------------
// File: SQLServerSecurityUtility.java
//
//
// Microsoft JDBC Driver for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
package com.microsoft.sqlserver.jdbc;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
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;
return;
}
/*
* 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;
}
}