【问题标题】:How to get compatibility between C# and SQL2k8 AES Encryption?如何获得 C# 和 SQL2k8 AES 加密之间的兼容性?
【发布时间】:2010-05-12 20:39:03
【问题描述】:

我对两列进行了 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,我也会得到非常不同的结果,无论是内容还是长度。

【问题讨论】:

  • 文档中是否有任何内容表明这两种加密算法实际上是相同的?
  • 他们说他们都是 AES,对吧?我认为这足以确保一些互操作性..

标签: .net sql-server-2008 encryption aes


【解决方案1】:

有几件事我会看:

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

  2. 确保两种加密算法使用相同的块大小。在 SQL 中,您在调用 CREATE SYMMETRIC KEY 时确定算法。如果不指定算法,则使用 AES256。在使用RijndaelManaged 的.NET 中,我相信默认块大小为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 个字节表示 用于加密数据的对称密钥
  • 接下来的 4 个字节代表版本号,当前是硬编码的 为“01000000”。
  • 接下来的 8 个字节用于 DES 加密(16 个字节用于 AES 加密) 表示随机生成的 初始化向量。
  • 接下来的 8 个字节是代表选项的标头信息 用于加密数据。如果 使用了验证器选项,这个 头信息包括一个 20 字节 验证者的 SHA1 哈希,使得 头信息 28 个字节 长度。
  • 加密数据的最后一部分是实际数据和填充本身。 对于 DES 算法,这个长度 加密数据将是 8 的倍数 字节。对于 AES 算法,长度 将是 16 个字节的倍数。

【讨论】:

  • 从输出长度(base64 编码前 84 个字节)来看,我认为 SQL Server 没有使用 AES256。不过,它也不是 16 的倍数。很奇怪。
  • @Thorarin - 相信 Coles 只是说数据 + 填充将是 16 的倍数,而不是整个密码。因此,84 减去 36 字节的标头是 48 字节,是 16 的倍数。
  • 啊,你的编辑清楚了很多,至少它解释了为什么前 18 个 base64 字符是相同的,其余的一直在变化。但是,现在你应该可以使用 .NET 解密知道标题布局。加密我不确定,取决于我猜的 SHA1 哈希。
  • 谢谢托马斯,你帮了很多忙。我编写了一个程序集,使 SQL Server 使用的 C# 加密,而不是本机 AES ;)
  • 有关格式的权威信息似乎位于blogs.msdn.com/b/sqlsecurity/archive/2009/03/29/…
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-06
    • 2017-05-16
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多