C# Decrypt bytes from SQL Server EncryptByPassPhrase?

生来就可爱ヽ(ⅴ<●) 提交于 2019-11-29 11:22:49

SQL Server 2017 uses SHA256 hashing of password + AES-256 encryption

Older versions use SHA1 hashing of password + 3DES-128 encryption

IV size is the same as block size: AES = 128 bits, 3DES = 64 bits

Padding mode: PKCS #7 Cipher mode: CBC

Data encrypted by server 2017 starts with "0x02", older versions start with "0x01".

// Example decrypt:
// UInt32 - "magic" (0xbaadf00d): 0d f0 ad ba
// UInt16 - unknown (always zero): 00 00
// UInt16 - decrypted data length (16): 10 00
// byte[] - decrypted data: 4c 65 74 54 68 65 53 75 6e 53 68 69 6e 69 6e 67

DecryptCombined("0x02000000266AD4F387FA9474E825B013B0232E73A398A5F72B79BC90D63BD1E45AE3AA5518828D187125BECC285D55FA7CAFED61", "Radames");
DecryptCombined("0x010000007854E155CEE338D5E34808BA95367D506B97C63FB5114DD4CE687FE457C1B5D5", "banana");


void DecryptCombined(string FromSql, string Password)
{
    // Encode password as UTF16-LE
    byte[] passwordBytes = Encoding.Unicode.GetBytes(Password);

    // Remove leading "0x"
    FromSql = FromSql.Substring(2);

    int version = BitConverter.ToInt32(StringToByteArray(FromSql.Substring(0, 8)), 0);
    byte[] encrypted = null;

    HashAlgorithm hashAlgo = null;
    SymmetricAlgorithm cryptoAlgo = null;
    int keySize = (version == 1 ? 16 : 32);

    if (version == 1)
    {
        hashAlgo = SHA1.Create();
        cryptoAlgo = TripleDES.Create();
        cryptoAlgo.IV = StringToByteArray(FromSql.Substring(8, 16));
        encrypted = StringToByteArray(FromSql.Substring(24));
    }
    else if (version == 2)
    {
        hashAlgo = SHA256.Create();
        cryptoAlgo = Aes.Create();
        cryptoAlgo.IV = StringToByteArray(FromSql.Substring(8, 32));
        encrypted = StringToByteArray(FromSql.Substring(40));
    }
    else
    {
        throw new Exception("Unsupported encryption");
    }

    cryptoAlgo.Padding = PaddingMode.PKCS7;
    cryptoAlgo.Mode = CipherMode.CBC;

    hashAlgo.TransformFinalBlock(passwordBytes, 0, passwordBytes.Length);
    cryptoAlgo.Key = hashAlgo.Hash.Take(keySize).ToArray();

    byte[] decrypted = cryptoAlgo.CreateDecryptor().TransformFinalBlock(encrypted, 0, encrypted.Length);
    int decryptLength = BitConverter.ToInt16(decrypted, 6);
    UInt32 magic = BitConverter.ToUInt32(decrypted, 0);
    if (magic != 0xbaadf00d)
    {
        throw new Exception("Decrypt failed");
    }

    byte[] decryptedData = decrypted.Skip(8).ToArray();
    bool isUtf16 = (Array.IndexOf(decryptedData, (byte)0) != -1);
    string decryptText = (isUtf16 ? Encoding.Unicode.GetString(decryptedData) : Encoding.UTF8.GetString(decryptedData));

    Console.WriteLine("Result: {0}", decryptText);
}

// Method taken from https://stackoverflow.com/questions/321370/how-can-i-convert-a-hex-string-to-a-byte-array?answertab=votes#tab-top
public static byte[] StringToByteArray(string hex)
{
    return Enumerable.Range(0, hex.Length)
                     .Where(x => x % 2 == 0)
                     .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                     .ToArray();
}
kishang

I believe the link you are following is suggesting a new way to encrypt and decrypt mimicking that of how the SQL EncryptByPassPhrase is made. So, you could only use the decrypt in C#, if you encrypted in C# as well.

Since, you already used EncryptByPassPhrase in SQL then I would suggest just using DecryptByPassPhrase in SQL before passing to C# code.

Example of your hello world encrypt and decrypt:

Declare @lEncryptedText VARBINARY(256) = (select ENCRYPTBYPASSPHRASE('hello','world'))
SELECT @lEncryptedText --Encrypted VALUE for world

SELECT CONVERT(VARCHAR(100),DECRYPTBYPASSPHRASE('hello',@lEncryptedText)) --Decrypted Value
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!