Skip to content

Commit

Permalink
Fix: Initialization vector cannot be reused (#1858)
Browse files Browse the repository at this point in the history
  • Loading branch information
David-Engel authored Jul 11, 2022
1 parent 7c61330 commit c9fa435
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
17 changes: 11 additions & 6 deletions src/main/java/com/microsoft/sqlserver/jdbc/SecureStringUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ final class SecureStringUtil {
/* authentication tag length in bits */
static final int TAG_LENGTH = 16;

/* initialization vector */
byte[] iv;

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

Expand Down Expand Up @@ -68,7 +65,6 @@ static SecureStringUtil getInstance() throws SQLServerException {
* if error
*/
private SecureStringUtil() throws SQLServerException {
iv = new byte[IV_LENGTH];
try {
// generate key */
KeyGenerator keygen = KeyGenerator.getInstance(KEYGEN_ALGORITHEM);
Expand Down Expand Up @@ -99,6 +95,8 @@ private SecureStringUtil() throws SQLServerException {
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);
Expand All @@ -107,7 +105,10 @@ byte[] getEncryptedBytes(char[] chars) throws SQLServerException {
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParamSpec);

byte[] cipherText = encryptCipher.doFinal(Util.charsToBytes(chars));
return cipherText;
byte[] bytes = new byte[iv.length + cipherText.length];
System.arraycopy(iv, 0, bytes, 0, iv.length);
System.arraycopy(cipherText, 0, bytes, iv.length, cipherText.length);
return bytes;
} catch (Exception e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_EncryptionFailed"));
Object[] msgArgs = {e.getMessage()};
Expand All @@ -127,13 +128,17 @@ byte[] getEncryptedBytes(char[] chars) throws SQLServerException {
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);

byte[] plainText = null;
try {
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParamSpec);

plainText = decryptCipher.doFinal(bytes);
plainText = decryptCipher.doFinal(bytes, IV_LENGTH, bytes.length - IV_LENGTH);
return Util.bytesToChars(plainText);
} catch (Exception e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DecryptionFailed"));
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/com/microsoft/sqlserver/jdbc/UtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public void testparseUrl() throws SQLException {
}

private static String testString = "A ß € 嗨 𝄞 🙂ăѣ𝔠ծềſģȟᎥ𝒋ǩľḿꞑȯ𝘱𝑞𝗋𝘴ȶ𝞄𝜈ψ𝒙𝘆𝚣1234567890!@#$%^&*()-_=+[{]};:'\",<.>/?~𝘈Ḇ𝖢𝕯٤ḞԍНǏ𝙅ƘԸⲘ𝙉০Ρ𝗤Ɍ𝓢ȚЦ𝒱Ѡ𝓧ƳȤѧᖯć𝗱ễ𝑓𝙜Ⴙ𝞲𝑗𝒌ļṃʼnо𝞎𝒒ᵲꜱ𝙩ừ𝗏ŵ𝒙𝒚ź1234567890!@#$%^&*()-_=+[{]};:'\",<.>/?~АḂⲤ𝗗𝖤𝗙ꞠꓧȊ𝐉𝜥ꓡ𝑀𝑵Ǭ𝙿𝑄Ŗ𝑆𝒯𝖴𝘝𝘞ꓫŸ𝜡ả𝘢ƀ𝖼ḋếᵮℊ𝙝Ꭵ𝕛кιṃդⱺ𝓅𝘲𝕣𝖘ŧ𝑢ṽẉ𝘅ყž1234567890!@#$%^&*()-_=+[{]};:'\",<.>/?~Ѧ𝙱ƇᗞΣℱԍҤ١𝔍К𝓛𝓜ƝȎ𝚸𝑄Ṛ𝓢ṮṺƲᏔꓫ𝚈𝚭𝜶Ꮟçძ𝑒𝖿𝗀ḧ𝗂𝐣ҝɭḿ𝕟𝐨𝝔𝕢ṛ𝓼тú𝔳ẃ⤬𝝲𝗓1234567890!@#$%^&*()-_=+[{]};:'\",<.>/?~𝖠Β𝒞𝘋𝙴𝓕ĢȞỈ𝕵ꓗʟ𝙼ℕ০𝚸𝗤ՀꓢṰǓⅤ𝔚Ⲭ𝑌𝙕𝘢𝕤";
private static String testString2 = "ssdfsdflkjh9u0345)*&)(*&%$";
private static String testString3 = "ss345(*&^%oujdf.';lk2345(*&()*$#~!`1\\]wer><.,/?dfsdflkjh9u0345)*&)(*&%$";

@Test
public void testArrayConversions() {
Expand All @@ -79,9 +81,17 @@ public void testArrayConversions() {

@Test
public void testSecureStringUtil() throws SQLException {
// Encrypt/decrypt multiple values in overlapping orders
byte[] bytes = SecureStringUtil.getInstance().getEncryptedBytes(testString.toCharArray());
byte[] bytes2 = SecureStringUtil.getInstance().getEncryptedBytes(testString2.toCharArray());
String end = String.valueOf(SecureStringUtil.getInstance().getDecryptedChars(bytes));
byte[] bytes3 = SecureStringUtil.getInstance().getEncryptedBytes(testString3.toCharArray());
String end3 = String.valueOf(SecureStringUtil.getInstance().getDecryptedChars(bytes3));
String end2 = String.valueOf(SecureStringUtil.getInstance().getDecryptedChars(bytes2));

assertEquals(testString, end);
assertEquals(testString2, end2);
assertEquals(testString3, end3);
}

private void writeAndReadLong(long valueToTest) {
Expand Down

0 comments on commit c9fa435

Please sign in to comment.