12

我对两列进行了 AES 加密:其中一列存储在 SQL Server 2000数据库中;另一个存储在 SQL Server 2008数据库中。

由于第一列的数据库 (2000) 没有用于加密/解密的本机功能,因此我们决定在应用程序级别使用 .NET 类对两者进行加密逻辑。

但是由于第二列的数据库(2008)允许这种功能,我们希望使用数据库功能的数据迁移更快,因为SQL 2k中的数据迁移比这一秒要小得多,并且会持续更长的时间超过 50 小时,因为是在应用程序级别制作的。

我的问题从这一点开始:使用相同的密钥,我在加密一个值时没有得到相同的结果,也没有相同的结果大小。

下面我们有双方的完整逻辑。当然我没有显示关键,但其他一切都是一样的:

private byte[] RijndaelEncrypt(byte[] clearData, byte[] Key)
{
    var memoryStream = new MemoryStream();

    Rijndael algorithm = Rijndael.Create();

    algorithm.Key = Key;
    algorithm.IV = InitializationVector;

    var criptoStream = new CryptoStream(memoryStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
    criptoStream.Write(clearData, 0, clearData.Length);
    criptoStream.Close();

    byte[] encryptedData = memoryStream.ToArray();
    return encryptedData;
}

private byte[] RijndaelDecrypt(byte[] cipherData, byte[] Key)
{
    var memoryStream = new MemoryStream();

    Rijndael algorithm = Rijndael.Create();

    algorithm.Key = Key;
    algorithm.IV = InitializationVector;

    var criptoStream = new CryptoStream(memoryStream, algorithm.CreateDecryptor(), CryptoStreamMode.Write);

    criptoStream.Write(cipherData, 0, cipherData.Length);

    criptoStream.Close();

    byte[] decryptedData = memoryStream.ToArray();

    return decryptedData;
}

这是 SQL 代码示例:

open symmetric key columnKey decryption by password = N'{pwd!!i_ll_not_show_it_here}'

declare @enc varchar(max)

set @enc = dbo.VarBinarytoBase64(EncryptByKey(Key_GUID('columnKey'), 'blablabla'))

select LEN(@enc), @enc

这个 varbinaryToBase64 是一个经过测试的 sql 函数,我们用来将 varbinary 转换为我们用来在 .net 应用程序中存储字符串的相同格式。

C# 中的结果是: eg0wgTeR3noWYgvdmpzTKijkdtTsdvnvKzh+uhyN3Lo=

SQL2k8 中的相同结果是: AI0zI7D77EmqgTQrdgMBHAEAAACyACXb+P3HvctA0yBduAuwPS4Ah3AB4Dbdj2KBGC1Dk4b8GEbtXs5fINzvusp8FRBknF15Br2xI1CqP0Qb/M4w

我只是还没有得到我做错了什么。

你有什么想法?

编辑:我认为至关重要的一点:我的 C# 代码中有一个初始化向量,16 个字节。此 IV 未设置为 SQL 对称密钥,我可以这样做吗?

但即使没有用 C# 填充 IV,我也会得到非常不同的结果,无论是内容还是长度。

4

2 回答 2

7

有几件事我会看:

  1. 绝对确保明文的内容和编码相同。IIRC,流默认为 UTF-8,而如果您的VarBinaryToBase64函数采用 nvarchar 参数,它将是 Unicode。

  2. 确保两种加密算法使用相同的块大小。在 SQL 中,您在调用时确定算法CREATE SYMMETRIC KEY。如果不指定算法,则使用 AES256。在 .NET using 中RijndaelManaged,我相信默认块大小为 128,但您可以将其设置为 256(如果您使用Aes该类,则不能)。

  3. 我要寻找的最后一件事是 SQL Server 如何处理您在修改后的帖子中提到的初始化向量。我想说它为此使用了authenticator参数,但这是一个疯狂的猜测。

编辑

我走得很远。鉴于我的发现,您不能使用 .NET 类来解密由 SQL Server 内置加密加密的文本,因为 SQL Server 在加密的内容中添加了一堆粘液,包括随机初始化向量。来自 Michael Cole 的书“Pro T-SQL 2005 Programmer's Guide”(尽管 2008 的做法相同):

当 SQL Server 通过对称密钥加密时,它会将元数据添加到加密结果以及填充中,从而使加密结果比未加密的纯文本更大(有时明显更大)。带有元数据的加密结果的格式遵循以下格式:

  • 加密结果的前 16 个字节表示用于加密数据的对称密钥的 GUID
  • 接下来的 4 个字节代表版本号,当前硬编码为“01000000”。
  • 接下来的 8 个字节用于 DES 加密(16 个字节用于 AES 加密)代表随机生成的初始化向量。
  • 接下来的 8 个字节是标头信息,表示用于加密数据的选项。如果使用了authenticator 选项,这个头信息包括一个20 字节的认证器SHA1 散列,使得头信息的长度为28 字节。
  • 加密数据的最后一部分是实际数据和填充本身。对于 DES 算法,此加密数据的长度将是 8 个字节的倍数。对于 AES 算法,长度将是 16 字节的倍数。
于 2010-05-12T21:41:29.340 回答
1

不是真正的答案,但太大而无法发表评论。也许它会帮助其他人弄清楚:)
SQL Server 似乎做了很多填充。当我尝试您的示例时,我不断得到不同的结果,尽管前 18 个字符似乎每次都相同。然而,这对我来说还没有意义......

您没有展示如何RijndaelEncrypt被调用,而是使用不同的数据长度,我正在考虑 unicode 与 ANSI 代码页的差异。但是,您似乎没有在 SQL Server 中使用 unicode,因此长度差异将是相反的,如果有的话。

于 2010-05-12T21:31:38.803 回答