加密算法如何知道他们是否拥有正确的密钥

时间:2013-07-03 16:48:21

标签: security encryption encryption-symmetric

我知道尝试实现自己的加密算法是个坏主意。这是我认为不是的一个原因:

例如,假设我想将plainText = new byte[]{2,5,1,6,7}发送给bob。

如果我使用AES加密或其他熟知的算法,那么我将:

cipherText = Aes.Encrypt(plainText, key, iv); // now I have some cipher text that is not readable to anyone if they do not have the key and iv vector.

如果有人想要解密该消息,那么他们必须执行以下操作:

newPlainText = Aes.Decrypt(cipherText, key, iv);

现在我的问题是AES如何知道你是否输入了正确的密钥?我认为拥有一个不会提示暴力攻击的算法会更安全。换句话说,如果黑客知道您使用了AES,它可以继续尝试大量密码,直到method Aes.Decrypt Thorws也不例外。现在考虑这个算法:

假设我想将相同的字节数组{2,5,1,6,7}发送给bob。我的加密算法可能如下所示:

密码是=“securePassword”;

我将迭代我的纯文本上的每个字节,并对密码上每个字符的ASCII值执行Xor运算符。例如,第一个字节将是2 Xor (ASCII value of 's'),那么下一个值将是5 Xor (ASCII value of 'e'),最后我会以{2 Xor 's', 5 Xor 'e', 1 Xor 'c', 6 Xor 'u', 7 Xor 'r'} 这个算法的优点是你永远不会现在,如果你有正确的钥匙!

使用这种算法,不可能知道你是否有写密钥,这使我无法破译它。如果您使用众所周知的算法,如果您的密码不长,则会提示您进行暴力攻击。

* 所以我的问题是众所周知的AES等对称加密算法如何知道你是否输入了正确的密钥?有一个算法,你不知道你是否提供正确的密钥将不会更安全? *

3 个答案:

答案 0 :(得分:9)

加密算法知道输入密钥是否正确!该算法可以很好地解密任何密钥;如果您使用错误的密钥,它最终会被垃圾邮寄。使用加密的应用程序一旦完成就可以检测到这一点,因为解密的消息可能具有无效的填充或者在其他方面会出现格式错误,但AES本身永远不会“抛出异常”。

您描述的算法是one-time pad。它的致命缺陷是它的密钥必须至少与消息一样长,并且永远不能重复使用 - 如果相同的密钥与两条消息一起使用,则两条消息A ⊕ KB ⊕ K可以被异和一起产生A ⊕ K ⊕ B ⊕ K = A ⊕ B - 现在已经从消息中删除了密钥,并且可以从中猜出消息是什么。

答案 1 :(得分:3)

首先,AES算法就是这样。这只是一个数学密码。它无法知道您是否使用了正确的密钥。提供这种能力的差异通常是密码原语与密钥,IV和明文/密文的一个或多个“块”组合以产生结果的预定义方式;这被称为密码模式。

最常见的模式之一是CBC或Cipher Block Chaining。在这种模式下,先前加密的密文块在其通过密码之前与下一个明文块进行异或(第一个块与IV进行异或)。此模式要求明文消息长度是块大小的精确倍数,这可以通过填充来完成。

填充与“链接”加密模式相结合,提供了各种类型的校验和。最后一个块必须是有效填充的块(对于恰好是块大小的精确长度倍数的消息,最后一个块仍然是填充;它只是全零)。如果不是,则使用错误的密钥,或者传输中的消息被破坏(加密的链接效应意味着在明文块和后面的每个块中都会看到引入密文的任何错误)。当使用错误的密钥时,这很可能是您的异常来源。但是,CBC已被证明易受选择密文攻击,允许攻击者在不知道密钥的情况下恢复明文,如果允许他将特殊修改的密文块提供给解密算法并查看结果。

其他模式具有内置消息身份验证,可防止此类攻击。例如,有CCM,它是CBC-MAC的计数器。首先,通过CBC加密运行消息进行哈希处理,但只保留最后一块数据(请记住级联行为?原来这是计算键控哈希的好方法)。然后,使用计数器模式对消息和散列摘要进行加密(IV与顺序值进行异或,产生与每个明文块进行异或的随机数)。如果使用了错误的密钥,或者传输中的消息已更改,则重新散列时生成的消息将与散列摘要不匹配。除非你确定你有正确的密钥,否则真的不可能知道是哪种情况,这意味着更改密文或强制密钥将产生相同的故障并且在大致相同的时间内,因此这些模式是安全通信渠道的黄金标准。

答案 2 :(得分:1)

但是要回答你的问题,.NET AES提供商确实知道你是否使用了错误的密钥 - 但这只是因为你问过它。

您尝试加密字节:

2,5,1,6,7

使用类似的代码:

var plainText = new byte[] {2,5,1,6,7};
byte[] cipherText;

var key = new Byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
var iv = new Byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

//encrypt plaintext using one key
using (var aes = Aes.Create())
{
   var crypt = aes.CreateEncryptor(key, iv);
   cipherText = crypt.TransformFinalBlock(plainText, 0, plainText.Length);
}

实际上,AES仅在16字节块上运行。为了将输入数据转换为足以实际加密的输入数据,输入字节首先填充。在.NET中构造Aes类时:

using (var aes = Aes.Create())
{
}

default padding mode in Aes is PKCS#7 padding。这会导致Aes加密以将 plainText 填充到:

2, 5, 1, 6, 7, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11

然后将那些 byes加密为:

210, 49, 160, 164, 2, 53, 121, 254, 79, 249, 91, 111, 104, 173, 50, 207

解密那些密码字节时:

byte[] recoveredPlainText;
using (var aes = Aes.Create())
{
   var key = new Byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   var iv = new Byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   var crypt = aes.CreateDecryptor(key, iv);
   recoveredPlainText = crypt.TransformFinalBlock(cipherText, 0, cipherText.Length);
}

Aes知道确保存在正确的PKCS#7填充:

  

2,5,1,6,7 ,11,11,11,11,11,11,11,11,11,11,1

如果您尝试使用无效密钥解密(例如{1,0,0, ..., 0}),则最终将使用不包含有效PKCS#7填充的plainText:

  

154,203,183,159,52,162,186,41,127,162,152,75,114,109,107,74

所以Aes解密器会抛出异常。

您仍然可以使用无效密钥解密字节,您只需通过设置Padding = PaddingMode.None告诉Aes不要寻找任何填充:

byte[] recoveredPlainText;
using (var aes = Aes.Create())
{
   aes.Padding = PaddingMode.None;
   var key = new Byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   var iv = new Byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   var crypt = aes.CreateDecryptor(key, iv);
   recoveredPlainText = crypt.TransformFinalBlock(cipherText, 0, cipherText.Length);
}

tl; dr :它知道因为您使用了填充

  

注意:任何代码都会发布到公共域中。无需归属。