密码学是一个如此广泛的学科,即使经验丰富的编码人员也几乎总是会在前几次出错。但是加密是一个非常重要的主题,通常我们无法承担这些错误。
这个问题的目的是识别并列出与给定算法或API无关的内容。这样,我们可以从他人的经验中学习并防止不良作法的传播。
为使该问题具有建设性,请
包括一个“错误的”示例
说明该示例出了什么问题
提供正确的实现(如果适用)。
尽您所能,提供有关上面#2和#3的参考。
#1 楼
不要发明自己的加密算法或协议;那是极容易出错的。正如Bruce Schneier所说,
“任何人都可以发明一种自己无法破解的加密算法;要发明一种别人都无法破解的加密算法要困难得多”。
加密算法非常复杂,需要进行严格审查才能确保其安全性;如果您发明了自己的产品,那么您将一事无成,并且很容易在没有意识到的情况下得到一些不安全的东西。相反,请使用标准的加密算法和协议。可能是其他人之前曾遇到您的问题,并为此目的设计了合适的算法。
最好的情况是使用经过严格审查的高级方案:为实现通信安全,请使用TLS(或SSL);对于静态数据,请使用GPG(或PGP)。如果您不能做到这一点,请使用诸如cryptlib,GPGME,Keyczar或NaCL之类的高级加密库,而不要使用诸如OpenSSL,CryptoAPI,JCE等之类的低级库。感谢Nate Lawson的帮助建议。
评论
实际上,这应该是规则编号1-这会使我们所有其他规则无效。世界上可能只有数百个人应该设计或实现加密货币。我们其余的人应该只使用他们(合理的)API。
– Alex Holst
2011年2月20日在17:15
.NET开发人员的最佳选择是什么?有教程或示例吗?对于非加密专家而言,很难根据有效信息确定错误信息。
–半比特
2011年2月21日在17:28
对于过去解决难题的创意工程师而言,将加密技术留给其他人尤其具有诱惑力。在加密货币之外,还有许多难题需要解决。看一些在加密方面犯了严重错误并解决其他问题的人的例子。
–this.josh
2011年6月17日下午4:26
+1 Crypto API提供了太多的灵活性,可能使外行开发人员陷入困境。互联网上(和MSFT的支持站点)上有许多样本至少违反了此页面上所吸取的教训之一。一些开发人员忘记考虑诸如如何交换,验证和撤销密钥之类的事情。这就是棘手的地方。钥匙在哪里?密钥如何发布?如何验证密钥?钥匙轮换如何进行?即使开发人员很幸运地掌握了正确的数学方法或功能组合(CBC,块,流等),该协议也可能被破坏。
–半比特
2012年4月18日在16:07
对于.net的高级选项,我已将keyczar移植到C#。
– jbtule
13年2月20日在16:33
#2 楼
在没有消息身份验证的情况下不要使用加密在不对数据进行身份验证的情况下加密数据是一个非常常见的错误。
示例:开发人员希望对消息保密,因此使用AES-CBC模式加密消息。错误:在存在主动攻击,重播攻击,响应攻击等情况下,这不足以确保安全。存在已知的未经消息身份验证的加密攻击,这些攻击可能非常严重。解决方法是添加消息身份验证。
此错误已导致在使用未经身份验证的加密的已部署系统中出现严重漏洞,包括ASP.NET,XML加密,Amazon EC2,JavaServer Faces,Ruby on Rails, OWASP ESAPI,IPSEC,WEP,再次为ASP.NET和SSH2。您不想成为此列表中的下一个。
为了避免这些问题,每次应用加密时都需要使用消息身份验证。您有两种选择方法:
最简单的解决方案可能是使用提供经过身份验证的加密的加密方案,例如GCM,CWC,EAX,CCM,OCB。 (另请参见:1.)经过身份验证的加密方案可以为您解决此问题,因此您不必考虑它。
或者,您可以按照以下方式应用自己的消息身份验证。首先,使用适当的对称密钥加密方案(例如AES-CBC)对消息进行加密。然后,获取整个密文(包括解密所需的任何IV,随机数或其他值),应用消息身份验证代码(例如AES-CMAC,SHA1-HMAC,SHA256-HMAC),并将生成的MAC摘要附加到传输前的密文。在接收端,解密之前请检查MAC摘要是否有效。这称为加密然后认证结构。 (另请参见:1、2。)这也可以正常工作,但需要您多加注意。
评论
C#和Java用户应查看BouncyCastle
–半比特
11年5月13日在16:37
@ makerofthings7:GCM加密包含在Java 7的Oracle提供程序中。它也建议用于TLS(在RFC内)和XML加密v1.1。有弹性的实现与Sun提供程序中的实现兼容(除了有关经过身份验证的数据和引发的确切异常方面的差异)。
–马滕·博德威斯(Maarten Bodewes)
2012年3月2日在20:48
对于那些不了解密码的人(因此请阅读这篇文章),“身份验证”与“登录”或使用您的凭据无关。有点像校验和。实际上,数学和过程的结合最终所要做的远不仅仅是对数据进行校验和。 (@ D.W。您如何看待这个外行的解释?)
–半比特
2012年4月18日15:50
@ makerofthings7,很棒的解释!如果本文提到的是“消息身份验证”,而不仅仅是泛型身份验证,可能会更加清楚。我现在进行更改。
– D.W.
2012年4月18日在17:40
#3 楼
在哈希之前连接多个字符串时要小心。我有时会看到一个错误:人们想要对字符串S和T进行哈希处理。他们将它们连接起来以获得单个字符串S || T,然后进行哈希处理它得到H(S || T)。这是有缺陷的。
问题:串联使两个字符串之间的边界不明确。例如:
builtin
|| securely
= built
|| insecurely
。换句话说,哈希H(S || T)不能唯一地标识字符串S和T。因此,攻击者可以在不更改哈希的情况下更改两个字符串之间的边界。例如,如果爱丽丝想发送两个字符串builtin
和securely
,则攻击者可以将它们更改为两个字符串built
和insecurely
而不会使哈希无效。应用数字签名或消息时也会遇到类似的问题。修正代码:将验证码转换为字符串的串联。
解决方法:使用简单可解码的编码,而不是简单的串联。例如,代替计算H(S || T),您可以计算H(length(S)||| S || T),其中length(S)是32位值,表示S的长度(以字节为单位)。或者,另一种可能性是使用H(H(S)|| H(T)),甚至使用H(H(S)|| T)。
关于此示例的真实示例缺陷,请参阅Amazon Web Services中的此缺陷或Flickr [pdf]中的此缺陷。
评论
我通常会向其扔HMAC。稍微贵一点,但是至少我不需要自己实现它。
– CodesInChaos
2012年4月18日在21:26
@CodeInChaos,这些问题同样适用于HMAC。如果在将多个字符串提供给HMAC之前将它们串联起来,HMAC不会有任何帮助。
– D.W.
2012年4月19日在18:07
@CodeInChaos,好吧,如果您对加密技能有足够的信心来避免缺陷,请使用任何适合您的方法。就个人而言,我不会向其他人推荐这种方法。 (1)这不是HMAC设计的目的,因此,如果碰巧是安全的,您会“很幸运”。 (2)仅限于两个字段的情况。如果您有三个字段,则必须做一些更复杂的事情。因此最好一开始就使用适当的防御措施,例如使用明确可解码的编码(例如,在要连接的每个字段之前加长)。
– D.W.
2012年4月19日在18:49
@Marcin“哈希应该很慢”不,它们不应该很慢,而且也不慢
–好奇
2012年6月25日19:48
@Matt,是的,可以工作!但是,您必须在这些字符串中转义定界符的任何实例(否则,不可避免地,有人会输入包含定界符的字符串,然后这种方法会崩溃)。这就增加了替代方案所没有的复杂性。如果您向其他人推荐此方法,那么不可避免地会有一个观众忘记转义分隔符。如果他们忘记转义分隔符,则该方案会悄悄地失败:这是不安全的,但在正常操作中他们可能不会注意到这一点。因此,是的,它可以工作-但这可能不是我的首选。
– D.W.
2012年8月27日15:52
#4 楼
确保您为具有足够熵的随机数生成器提供种子。确保使用加密强度的伪随机数生成器进行诸如生成密钥,选择IV /随机数等操作。请勿使用
rand()
,random()
, drand48()
等。确保您为伪随机数生成器提供了足够的熵。不要在一天中的任何时候播种。
示例:
srand(time(NULL))
非常糟糕。播种PRNG的一个好方法是从例如/dev/urandom
,CryptGenRandom或类似的地址中获取128位或真随机数。在Java中,请使用SecureRandom,而不要使用Random。在.NET中,请使用System.Security.Cryptography.RandomNumberGenerator,而不要使用System.Random。在Python中,请使用random.SystemRandom,而不是random。感谢Nate Lawson的一些示例。真实示例:在早期版本的Netscape浏览器中看到此漏洞,攻击者可以利用它破坏SSL。
评论
我记得在Apple上学习Basic [e。我正在编写游戏,需要一些随机输入,所以我使用了RND(1)。我必须不断重新启动才能调试游戏,我注意到随机元素在启动后总是按照相同的顺序进行。那时我了解了伪随机数生成器。如果需要一些随机种子,Random.org可提供基于大气噪声的免费随机数生成。
–this.josh
2011年6月17日4:45
Random.org最适合用于模拟和其他非安全性目的。 Random.org并不是加密PRNG种子的良好基础,因为您不能相信其他人不知道它。
– D.W.
2011年6月19日下午6:24
#5 楼
不要重用随机数或IV许多操作模式都需要IV(初始化向量)。您绝不能为IV重复使用相同的值。这样做会取消所有安全保证,并导致灾难性的安全破坏。
对于流密码操作模式(例如CTR模式或OFB模式),重新使用IV是安全的灾害。
对于其他操作模式,例如CBC模式,重新使用IV在某些情况下还可以促进明文恢复攻击。
否无论使用哪种操作模式,都不应重复使用IV。如果您想知道如何正确执行操作,NIST规范提供了有关如何正确使用分组密码操作模式的详细文档。
Tarsnap项目提供了一个很好的例子来说明这种陷阱。 Tarsnap通过将备份数据分成多个块来加密,然后在CTR模式下使用AES加密每个块。在Tarsnap的1.0.22至1.0.27版本中,无意中重复使用了相同的IV,从而实现了纯文本恢复。
这是怎么发生的?为了简化Tarsnap代码-并希望减少错误的可能性-Colin Percival借此机会将AES-CTR代码“重构”为一个新文件(Tarsnap源代码中的lib / crypto / crypto_aesctr.c) ),并修改了使用AES-CTR来利用这些例程的现有位置。新代码如下所示:
/* Encrypt the data. */ - aes_ctr(&encr_aes->key, encr_aes->nonce++, buf, len, - filebuf + CRYPTO_FILE_HLEN); + if ((stream = + crypto_aesctr_init(&encr_aes->key, encr_aes->nonce)) == NULL) + goto err0; + crypto_aesctr_stream(stream, buf, filebuf + CRYPTO_FILE_HLEN, len); + crypto_aesctr_free(stream);
在重构期间,
encr_aes->nonce++
意外地变成了encr_aes->nonce
,结果重复使用了相同的现时值。特别是,在加密每个块之后,CTR随机数值不会增加。 (在处理完每16个字节的数据后,CTR计数器会正确递增,但是对于每个新块,该计数器都会重置为零。)Colin Percival在以下网站中描述了完整的详细信息:http://www.daemonology.net/blog/ 2011-01-18-tarsnap-critical-security-bug.html 评论
由于问题集中在一个随机数上,因此请尝试将该结果的标题设为(+1);问:随机数通常是++,还是随机的?
–半比特
2011-2-19在21:04
@makerofthings,取决于算法。某些算法和操作模式需要随机随机数(例如CBC模式);其他的仅要求随机数是不同的,因此计数器就足够了(例如CTR模式)。希望算法/操作模式的规范可以描述所需的内容。
– D.W.
11年2月20日在9:28
我建议对答案进行编辑以包括IV。正确的IV /随机数用法非常相似。
– B-Con
2012年8月13日在20:42
一次是N。是的,仅使用变量N一次。单数。不要重复。重复N会损害相关算法的强度。
–this.josh
2012年8月30日,下午5:41
这是一个错误的示例:WEP实现的RC4具有24位随机数,每条消息后该数值都会增加。这带来了两个问题:(1)发送2 ^ 24个数据包后,重用了随机数。 (2)RC4并非设计为具有“紧密相关”的随机数,因为已知每个后续密码都是前一个密码的++。
–半比特
13年5月20日在14:47
#6 楼
不要对加密和身份验证使用相同的密钥。请勿对加密和签名使用相同的密钥。密钥不应用于多种用途;
例如,如果您具有RSA私钥/公钥对,则不应同时将其用于加密(使用公钥加密,使用私钥解密) )以及用于签名(使用私钥签名,使用公钥验证):选择一个目的并将其仅用于该目的。如果您同时需要这两种能力,请生成两个密钥对,一个用于签名,一个用于加密/解密。
类似地,对于对称加密,您应该使用一个密钥进行加密,并使用单独的独立密钥进行消息身份验证。请勿将相同的密钥用于这两个目的。
评论
s / MIME是否违反此建议? AFAIK我只有一把钥匙,并且可以签名和加密。
–半比特
2011-2-20在16:43
恕我直言,在两种用法中使用相同的密钥的最大问题来自执法问题。现在,在许多管辖区中,您都被要求交出加密密钥,这实际上意味着它们可以登录您的名字。
–布鲁诺·罗埃(BrunoRohée)
2011年6月30日在8:54
是的,很多算法使用相同的密钥对进行签名和加密,PGP和S / MIME是明显的示例。这不一定是数学问题。
– ewanm89
2012年4月18日在21:40
PGP不使用相同的密钥对进行签名和加密。而是,PGP私钥由用于签名的主密钥和用于加密的一个或多个子密钥组成。子键对用户是隐藏的,因此很混乱,但是您可以使用gpg --list-secret-keys来查看它们。
– Flimm
2012年12月8日在20:42
#7 楼
不要将带有ECB的分组密码用于对称加密(适用于AES,3DES等)
这是一篇帖子,与Microsoft KB文章非常相似关于ECB模式如何导致未加密的代码。
也可以从Rook看到类似的帖子
纯文本消息:
用ECB加密的相同消息模式(使用什么密码都没有关系):
使用CBC模式的完全相同的消息(同样,使用什么密码也没关系):
错误的方式
public static string Encrypt(string toEncrypt, string key, bool useHashing)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
if (useHashing)
keyArray = new MD5CryptoServiceProvider().ComputeHash(keyArray);
var tdes = new TripleDESCryptoServiceProvider()
{ Key = keyArray, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(
toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
错误在下一行
{Key = keyArray,Mode = CipherMode.ECB,填充= PaddingMode.PKCS7};
正确的方法
Microsoft的好伙伴向我发送了以下代码来更正该知识库文章上面链接。在案例号111021973179005
中引用了此示例代码,该示例代码使用AES加密数据,而AES加密的密钥是SHA256生成的哈希码。 AES是高级加密标准(AES)算法。 AES算法基于排列和替换。排列是数据的重新排列,而替换则将一个数据单元替换为另一个数据单元。 AES使用几种不同的技术执行置换和替换。有关AES的更多详细信息,请参阅MSDN杂志上的文章“使用新的高级加密标准保护您的数据安全”,位于http://msdn.microsoft.com/zh-cn/magazine/cc164055.aspx。 >
SHA是安全哈希算法。现在建议使用SHA-2(SHA-224,SHA-256,SHA-384,SHA-512)。有关.NET Framework中哈希值的更多详细信息,请参考http://msdn.microsoft.com/zh-cn/library/92f9ye3s.aspx#hash_values。
AesCryptoServiceProvider
的对称算法操作模式的默认值为CBC。 CBC是密码块链接模式。它介绍了反馈。在对每个纯文本块进行加密之前,通过按位异或运算将其与前一个块的密文组合在一起。这样可以确保即使纯文本包含许多相同的块,它们也将各自加密为不同的密文块。在对该块进行加密之前,通过按位异或运算将初始化向量与第一个纯文本块组合在一起。如果密文块的单个位被整齐,则相应的明文块也将被整齐。此外,后续块中与原始错位相同的位也将被错位。有关CipherMode
的更多详细信息,请参考http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.ciphermode.aspx。以下是示例代码。// This function is used for encrypting the data with key and iv.
byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
// Create an AESCryptoProvider.
using (var aesCryptoProvider = new AesCryptoServiceProvider())
{
// Initialize the AESCryptoProvider with key and iv.
aesCryptoProvider.KeySize = key.Length * 8;
aesCryptoProvider.IV = iv;
aesCryptoProvider.Key = key;
// Create encryptor from the AESCryptoProvider.
using (ICryptoTransform encryptor = aesCryptoProvider.CreateEncryptor())
{
// Create memory stream to store the encrypted data.
using (MemoryStream stream = new MemoryStream())
{
// Create a CryptoStream to encrypt the data.
using (CryptoStream cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
// Encrypt the data.
cryptoStream.Write(data, 0, data.Length);
// return the encrypted data.
return stream.ToArray();
}
}
}
}
// This function is used for decrypting the data with key and iv.
byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
{
// Create an AESCryptoServiceProvider.
using (var aesCryptoProvider = new AesCryptoServiceProvider())
{
// Initialize the AESCryptoServiceProvier with key and iv.
aesCryptoProvider.KeySize = key.Length * 8;
aesCryptoProvider.IV = iv;
aesCryptoProvider.Key = key;
// Create decryptor from the AESCryptoServiceProvider.
using (ICryptoTransform decryptor = aesCryptoProvider.CreateDecryptor())
{
// Create a memory stream including the encrypted data.
using (MemoryStream stream = new MemoryStream(data))
{
// Create a CryptoStream to decrypt the encrypted data.
using (CryptoStream cryptoStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
{
// Create a byte buffer array.
byte[] readData = new byte[1024];
int readDataCount = 0;
// Create a memory stream to store the decrypted data.
using (MemoryStream resultStream = new MemoryStream())
{
do
{
// Decrypt the data and write the data into readData buffer array.
readDataCount = cryptoStream.Read(readData, 0, readData.Length);
// Write the decrypted data to resultStream.
resultStream.Write(readData, 0, readDataCount);
}
// Check whether there is any more encrypted data in stream.
while (readDataCount > 0);
// Return the decrypted data.
return resultStream.ToArray();
}
}
}
}
}
}
// This function is used for generating a valid key binary with UTF8 encoding and SHA256 hash algorithm.
byte[] GetKey(string key)
{
// Create SHA256 hash algorithm class.
using (SHA256Managed sha256 = new SHA256Managed())
// Decode the string key to binary and compute the hash binary of the key.
return sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
}
有关示例代码中类的更多详细信息,请参阅以下链接:
·AesCryptoServiceProvider类
·SHA256Managed类
·CryptoStream类
此外,还有几篇文章可能有助于更好地理解.NET Framework中的加密,请参考链接。下面的内容:
·加密服务
·.NET Framework密码学模型
·密码学的简单指南
·没有秘密加密
评论
我建议删除“正确方法”之后的所有内容。 Coda Hale的建议有许多缺点。它会产生一些我在其他答案中记录的错误:它使用没有消息身份验证的加密(严重的缺陷),将密钥创建为密码的哈希(严重的缺陷),没有尝试减慢详尽的密钥搜索(另一个严重缺陷)。我对答案的正确处理建议在答案的最后一段中描述,标题为“不要自行加密”。
– D.W.
2011-02-20 9:34
我建议删除“免责声明”和“一些要点”之后的所有内容。我认为其中大多数都与避免ECB的高层观点无关,并且会分散您的注意力。简明扼要。相反,我建议您以正确的方式提出建议:使用安全的操作模式,例如CBC模式或CTR模式。不要忘记遵循此页面上的其他建议,包括使用消息身份验证,适当地生成密钥等。如果要将其设为社区Wiki,我很乐意相应地编辑此答案。
– D.W.
2011年2月20日在9:36
@ D.W。是的,可以随时将任何或所有答案编辑为CW。我不想让CW整个问题都想激励海报,但我会把这个决定留给大家。我只想学习正确的东西,而不学习不良做法
–半比特
2011-02-20 15:30
请注意,在Java中,使用
–马滕·博德威斯(Maarten Bodewes)
2012年3月2日20:53
@Matt,该图像是成功攻击的结果。给定一个名为TUX.BMP的文件,假定攻击者将尝试查看它,但发现它已加密。然后,他查看加密的字节,并在看到非随机模式后怀疑CBC。然后,他用已知良好的BMP文件头替换前几个块,并对其进行调整,直到行和列对齐为止。几年前,我在Blackhat看到一位研究人员使用一种工具来执行此操作,我认为该工具称为rumint。
–约翰·迪特斯
2012年9月8日14:51
#8 楼
Kerckhoffs的原理:即使系统中除密钥之外的所有内容都是公共知识,密码系统也应是安全的错误的例子:LANMAN哈希
LANMAN哈希将很难弄清楚是否有人不知道该算法,但是一旦知道了该算法,现在破解起来就很容易了。
算法如下(来自维基百科):
用户的ASCII密码转换为大写。 14个字节
“固定长度”密码分为两个七个字节。
这些值用于创建两个DES密钥,每个7字节一半都一个。
每个两个密钥用于对恒定的ASCII字符串“ KGS!@#$%”进行DES加密,从而产生两个8字节密文值。
这两个密文值连接在一起形成一个16字节的值,即LM哈希值。
因为您现在知道了这些事实的密文,所以现在可以很容易地将密文分成两个您知道的密文是大写的,导致密码可能是一组有限的字符。
正确的例子:AES加密
已知算法
随技术扩展。需要更多加密功能时增加密钥大小
#9 楼
尝试避免将密码用作加密密钥。在许多系统中,一个常见的缺点是使用密码或密码短语或密码或密码短语的哈希作为加密/解密密钥。问题是,这往往很容易受到脱机密钥搜索攻击的影响。大多数用户选择的密码没有足够的熵来抵抗这种攻击。
最好的解决方法是使用真正随机的加密/解密密钥,而不是确定性地从密码/口令生成的密钥。
但是,如果必须使用基于密码/密码的密码,请使用适当的方案来减慢详尽的按键搜索。我推荐PBKDF2,它使用迭代哈希(沿H(H(H(.... H(password)...)))行)来减慢字典搜索的速度。安排使用足够多的迭代来使此过程在用户的计算机上花费例如100ms的时间来生成密钥。
评论
作为一个初学者,我承认我已经做到了。如果密钥是随机的,因此无法记住,您是否建议将密钥以物理形式存储在某处?这是我看到的使用纯随机密钥实现系统的唯一方法。
–亚当·克罗斯(Adam Cross)
2012年11月23日20:09
@AdamCross,“如果密钥是随机的,因此无法记住,您是否建议将密钥以物理形式存储在某个地方?” -好的,可以以电子形式或物理形式存储在某个地方。非电子形式不一定在所有情况下都比电子形式具有特权。举个例子...您可以使用SSL安全地连接到网站。您使用的SSL会话密钥不会以非电子形式存储在任何地方,也不是从密码短语派生的。
– D.W.
2012年11月23日在22:28
哈哈,用“身体形态”来澄清,我的意思是除了我的头以外的地方-但这真的没有意义,因为我的头也是身体的。
–亚当·克罗斯(Adam Cross)
2012年11月24日9:41
#10 楼
在加密协议中:使每条经过身份验证的消息都可识别:两条消息看起来都不应相同以下内容的概括/变体:
连接多个字符串时要小心,在散列之前。
不要重用密钥。
请不要重用随机数。
在运行加密协议期间,许多没有密钥(密钥或密匙)就无法伪造的消息。随机数)可以交换。接收者可以验证这些消息,因为他知道一些公用(签名)密钥,或者因为只有他和发送者知道一些对称密钥或随机数。这样可以确保这些消息没有被修改。
但是,这不能确保在协议的同一运行期间已经发出了这些消息:攻击者可能在之前或期间捕获了这些消息。协议的并发运行。攻击者可能会启动多个并发运行的加密协议,以捕获有效消息并未经修改地重用它们。
通过巧妙地重放消息,可能可以在不破坏任何主键的情况下攻击协议,而无需攻击任何主键。 RNG,任何密码等。
通过使该协议的每条经过身份验证的消息对于接收方来说都明显不同,可以减少(未消除)重播未修改消息的机会。
评论
实际上,随机数不必是秘密,它只需要使用一次(在一定时间范围内,例如,相应秘密密钥的有效性)。
–PaŭloEbermann
2011-09-28 15:09
@PaŭloEbermann随机数的许多用法不需要保密,但是某些协议形式主义将用于认证消息的秘密称为“随机数”,而不是密钥,因为它不用作加密密钥。
–好奇
2011-09-28 22:39
#11 楼
不要在两个方向上都使用相同的密钥。在网络通信中,常见的错误是在A-> B方向和B-> A方向上使用相同的密钥。这是一个坏主意,因为它通常会启用重播攻击,从而将发送给B的东西重播回B,然后再重发给A。最安全的方法是协商两个独立的密钥,每个方向协商一个密钥。或者,您可以协商单个密钥K,然后将K1 = AES(K,00..0)用于一个方向,将K2 = AES(K,11..1)用于另一个方向。
评论
或者,您可以为每个加密(在半双工通信中)增加一个SSC,一个安全会话计数器。我什至看过一个例子,其中另一方的密文的最后一个块用作下一个块的IV,但这可能会导致某些特殊的攻击。
–马滕·博德威斯(Maarten Bodewes)
2012年3月2日在20:57
@owlstead,是的,使用密文的最后一个块作为下一个块的IV以及CBC模式,导致对SSL的BEAST攻击。附言SSC可以将两个通道分开,但是您必须谨慎使用。您必须为发送和接收都增加它(使用两个SSC,每个方向使用一个SSC会达到目的)。此外,SSC将要求双方同步并且不会容忍丢包,这在某些设置中可能会出现问题。仅使用两个独立的键可能会更容易。
– D.W.
2012年3月3日,0:17
有趣的是,我知道一些使用该方案的存储卡,其中密码块的最后一部分作为下一个的IV。我会调查一下。谢谢D.W,猜想我的预感是对的。
–马滕·博德威斯(Maarten Bodewes)
2012年3月3日,0:42
这也为Microsoft的PPTP双重攻击打开了大门。 PPTP的第一个版本在客户端和服务器中使用相同的密钥
–半比特
13年5月20日在15:43
#12 楼
不要使用不安全的密钥长度。请确保您使用的密钥长度足够长的算法。
对于对称密钥加密,我建议至少使用80位密钥,如果可能的话,最好使用128位密钥。不要使用40位加密;它很不安全,很容易被业余爱好者破坏,只需彻底尝试所有可能的键即可。不要使用56位DES;破解并不容易,但是专门的攻击者可以破解DES。 128位算法(例如AES)不会比40位加密慢很多,因此您没有借口使用伪造的加密。
对于公共密钥加密,密钥长度建议取决于密钥长度。算法和所需的安全级别。同样,增加密钥大小会损害性能,因此过大的杀伤力是不经济的。因此,这比选择对称密钥密钥大小需要更多的思考。对于RSA,El Gamal或Diffie-Hellman,我建议密钥至少为1024位,并且绝对最小值。但是,1024位密钥处于短期内会变得容易破解的边缘,通常不建议用于现代用途,因此,如果有可能,我建议使用1536位甚至2048位密钥。对于椭圆曲线密码学,160位密钥显得足够,而224位密钥则更好。您还可以参考已发布的准则,以建立对称密钥和公共密钥大小之间的大致等效性。
评论
NIST推荐截至2010年底的1024位以上的密钥:securitymusings.com/article/1587/…
–nealmcb
2011-02-20 5:10
我唯一不同意的一句话是:“如今这是一种不太常见的错误” ...仍然是我看到的最常见的加密错误之一,在没有加密和滚动自己的加密之后。
–AVID♦
2011年2月20日在8:19
@ AviD,@ nealmcb,感谢您的反馈。我已进行编辑以反映@AviD的评论。请注意,我已将此社区设为Wiki,请随时对其进行编辑以改进建议并更正所有错误。
– D.W.
2011年2月20日在9:29
#13 楼
使用正确的模式等效地,不要依赖库的默认设置来保证安全。具体来说,许多实现AES的库都实现了FIPS 197中描述的算法,即所谓的ECB(电子代码簿)模式,该模式本质上是以下各项的直接映射:
AES(plaintext [32]byte, key [32]byte) -> ciphertext [32]byte
非常不安全。推理很简单,尽管密钥空间中可能的密钥数量很大,但这里的薄弱环节是消息中的熵量。与往常一样,xkcd.com所描述的要比我更好http://xkcd.com/257/
使用像CBC(密码块链接)之类的东西基本上使密文[i]映射:
ciphertext[i] = SomeFunction(ciphertext[i-1], message[i], key)
只需要指出一些容易出错的语言库即可:http://golang.org/pkg/crypto/aes /提供一个AES实现,如果天真地使用它会导致ECB模式。
pycrypto库在创建新的AES对象时默认为ECB模式。
OpenSSL,可以这个权利。每个AES调用都明确说明操作模式。 IMO真正最安全的事情就是只是不要自己做这种低级加密。如果您被迫这样做,请像小心地在碎玻璃上行走一样继续操作,并尝试确保用户有理由相信您,以保护他们的数据。
评论
感谢您的回答,Shane!一个问题:这已经被另一个答案覆盖了吗?不要将带有ECB的分组密码用于对称加密吗?
– D.W.
2012年9月8日于20:33
#14 楼
不要在许多设备上重复使用相同的密钥。共享密钥越广泛,就越不可能将其保密。某些已部署的系统已将相同的对称密钥重用于系统上的每个设备。问题在于,迟早有人会从单个设备中提取密钥,然后他们便能够攻击所有其他设备。因此,请不要这样做。
另请参阅此博客文章中的“对称加密不要#6:不要在多个设备之间共享单个密钥”。感谢Matthew Green。
#15 楼
如果密钥是通过算法拉伸的,则一次性垫不是一次性垫。标识符“一次性垫”(也称为Vernam密码)经常误用于各种密码试图声称牢不可破的安全性的解决方案。但是根据定义,只有并且满足以下所有三个条件时,Vernam密码才是安全的:
关键材料确实是不可预测的。 AND
密钥材料的长度与明文相同; AND
关键材料永远不会重复使用。
任何违反这些条件的行为都意味着它不再是一次性填充密码。
常见的错误是使用算法扩展了短密钥。此操作违反了不可预测性规则(不必担心密钥长度规则)。完成此操作后,一次性填充将在数学上转换为密钥扩展算法。将短键和随机字节组合在一起只会改变暴力破解键拉伸算法所需的搜索空间。类似地,使用“随机生成”字节会将随机数生成器算法转换为安全性算法。这里是一个简单的示例。我收到一条消息,我将使用“一次性密码”加密,该密码使用加密安全功能作为密钥生成器。我选择了一个秘密密钥,然后在其中添加了一个随机数以确保它不会被重用。由于我没有重用密钥,因此无法通过从另一消息中减去一条消息来攻击密文。
plaintext : 1234567890123456789012345678901234567890
key material : 757578fbf23ffa4d748e0800dd7c424a46feb0cc
OTP function (xor) : ----------
ciphertext : 67412E83622DCE1B0C1E1A348B04D25872A8C85C
密钥材料是使用SHA-1安全生成的,用于对我的秘密密码(加上随机数)进行哈希处理以对其进行扩展。但是,任何知道所使用的扩展算法*为SHA-1的攻击者都可以通过尝试将各种输入SHA-1并将输出与密文进行XOR来对其进行攻击。现在,猜测“ OTP”密钥比猜测密码算法的组合输入更困难。无论选择哪种基本密码算法,采用何种度量复杂度,如何实现或设定种子,此属性均成立。
您可能有一个很好的密钥拉伸算法。您可能还具有非常安全的随机数生成器。但是,根据定义,您的算法不是一次性填充,因此不具有一次性填充的不可破坏的属性。
*应用Kerckhoff原理意味着您必须假设攻击者可以始终确定使用的算法。
评论
您是否将编辑“由于我不重用密钥,因此无法通过从另一消息中减去一条消息来攻击密文。”并添加文字说其他攻击是可能的?示例:两个时间垫,错误的协议或其他偏差(分别为PPTP,WEP和RC4)。一个无知的外行可能会误读您所写的内容,并认为OTP可以从另一个意义上说“完全保密”。另外,由于您正在研究该主题,因此介绍一些有效的PNG / PRG密钥扩展器是有帮助的。
–半比特
13年5月20日在13:49
注意:OTP中没有消息身份验证。不会检测到对OTP的修改。
–半比特
13年5月20日在16:58
注意:安全的PRG类似于OTP。它是所有有效的统计检验,其结果可忽略不计,而且PRG不可能满足所有理论统计检验。安全性的这种“放松”是效率所必需的,因为“完美保密”要求安全传输足够大的OTP以匹配消息的大小。示例:所有OTP传输都要求安全地传输秘密(这是不确定的方式)。首先,使用该安全方法发送数据会更有效。
–半比特
13年5月20日在17:10
正是这种“相似性”使人们对不可破解性提出了怪异的主张,而正是这种“效率”打破了维南密码的不可预测性。没有人说过使用OTP进行密钥生成,密钥管理或密钥分发是简单或实用的-并非以上所有。尽管在数学上可以做到完美保密,但人们仍然很难使用其他密码。任何“键拉伸”都不能改变这个事实。
–约翰·迪特斯
13年5月24日在16:54
可以使用“安全PRNG”来生成这些位,但是,如果它确实是安全的,那么您仍然会遇到所有分发问题,因为您无法在接收方的计算机上复制它们的生成-如果可以的话,该状态将是密钥,而不是密钥。位。
–约翰·迪特斯
13年5月24日在16:56
#16 楼
不要相信标准。密码术中存在许多标准,有时您必须使用它们。但是,请不要以为编写标准的人充分理解了他们所需的加密技术。例如,EAX在网络标准中进行了重新设计。 EAX有安全证明。重做的版本没有。
MD5是标准。现在坏了。由于具有丰富的危险功能,芯片和PIN屡屡被破坏。 GPG仍然支持DSA密钥,这些密钥太短而无法舒适。 SSL具有不应使用的选项,并且需要避免使用这些选项。
该怎么办?谨慎行事,了解已知风险,并跟上新风险的研究。
评论
MD5是一个标准,是真的。但是它已被更新的最新标准SHA取代。在大多数情况下,出于多种原因应遵循标准。互操作性是一个很大的因素。
–user10211
2012年9月22日在16:05
这是一个非常误导性的陈述。该措辞暗示读者应“信任非标准”,这显然是不正确的。大多数安全标准只有在经过广泛的实际测试之后才能出现。测试远比任何单个组织用来“证明”其非标准系统的安全性都更为彻底。
–约翰·迪特斯
13年5月24日在17:02
#17 楼
请勿在磁盘加密中使用OTP或流密码。示例1
假设使用流密码/ OTP保存了两个文件。如果在进行较小的编辑后重新保存文件,则攻击者可以看到仅某些位被更改,并推断出有关文档的信息。 (想象一下将称呼“亲爱的鲍勃”更改为“亲爱的爱丽丝”)。
示例2
输出中没有完整性:攻击者可以修改密文并修改只需对数据进行XOR即可处理数据的内容。
要点:密文的修改未被发现,并且对明文具有可预测的影响。
解决方案
对于以下情况,请使用分组密码:邮件完整性检查
#18 楼
永远不要一次使用一个Time Pad(OTP)或流密码密钥一次应用一次OTP意味着用“完全保密”加密的数据将被解密并且是明文。发生这种情况是因为数据被异或两次。
示例
假设正在重用具有相同密钥的OTP /或流。
攻击者收集了从客户端发送的大量数据然后将一组两个数据包进行XOR运算,直到两个数据包互相解密(或其中的子集)。
ASCII编码具有足够的冗余性,这意味着给定足够的密文,就可以对原始消息进行解码(以及秘密的OTP密钥)。
现实世界的例子
项目维罗纳(1941-46)是俄罗斯人使用的OTP的例子,随后被美国情报机构解密
/> Microsoft的PPTPv1客户端和服务器都使用相同的密钥加密数据。
一旦发送2 ^ 24个数据包,或者重置了NIC卡,WEP就会重复使用相同的密钥。第一个问题是由于IV的长度为24位,导致在发送1600万帧后创建了两个时间间隔。第二个问题发生在硬件实现中,在重新上电之后,IV重置为零,导致两个时间间隔。因为IV是明文发送的,所以这个问题很容易看到。
建议
应该为每个会话创建一个新密钥(例如TLS)。
客户端应与服务器一起使用一个OTP(或带有PRG的流密码),并且服务器在向客户端加密数据时服务器应使用其他密钥
而不是生成许多密钥,可以使用PRG(假设您信任PRG)将单个键扩展为长数据流,并使用该扩展的每个段作为键。
请注意,并非所有PRG都可以在增量模式下工作,并且随机输入可能需要。 (RC4在增量模式下存在此问题)
#19 楼
使用可以在硬件或软件中正常工作的现代流处理器并非所有流密码都设计为在硬件或软件中实现。线性反馈移位寄存器(LFSR)是易于破解的广泛使用的硬件密码的一个示例。
LFSR用于:DVD加密(也用于被称为CSS)2 LFSR
GSM加密(A5 / 1.2)3 LSFR
蓝牙(E0):4 LFSR
上述硬件广泛部署,因此很难更新或提出现代标准。以上所有内容均已严重破坏,因此不值得用于安全通信。
攻击:
由于在加密过程中密钥被分为两部分(17位和25位) ),而这些位用于加密相同的密文,则可以使用MPEG格式的知识,并强行使用17位密钥来推断25位密钥是什么。
这并不是什么新鲜事物,但是FOSS很容易找到,证明了这个问题。
解决方案:
eStream项目(在2008年)限定了应使用的5个流密码。显着的区别在于,密码使用的是密钥,随机数和计数器,而不是使用带有IV的密钥。 Salsa20以这种方式操作,并且设计为可轻松在硬件和软件中使用。具体来说,它包含在x86 SSE2指令集中。
在旁边
现代密码不仅更安全,而且速度更快:
PRG Speed (MB/sec)
RC4 126 (obsolete)
Salsa20/12 643 (modern)
Sosemaunk 727 (modern)
#20 楼
不要使用RC4RC4于1987年设计,用作流密码。它在HTTPS和WEP中使用。
存在弱点
初始输出中存在偏差:Pr [2nd byte = 0] = 2/256
等于零的16位的概率是1/256 ^ 2 + 1/256 ^ 3。在加密了几Gig数据之后,就会发生这种情况。
容易受到相关的密钥攻击,在这种情况下,仅IV发生了变化,但密钥保持不变。
如果必须使用RC4,请忽略前256个字节(有偏差)。如果将RC4用于数据的Gig,则RC4中的偏差将允许攻击所有以前的加密数据。
#21 楼
仅使用不易受到邮件扩展攻击的MACMAC是确保给定纯文本的邮件完整性(无修改等)的哈希码。许多实现和已发布的标准未能保护MAC免受攻击者的攻击,因为攻击者无法向MAC添加其他数据。
解决此问题的方法是MAC实现使用第二个(不同的)密钥并重新加密最终输出。
ECBC和NMAC是正确加密的示例防止邮件扩展攻击。
解决方案:
使用
Encrypted CBC (ECBC)
代替raw CBC
使用
NMAC
代替cascade
评论
最常见的错误不是代码中的错误,而是关于如何使用加密技术的误解。换句话说,开发人员可能会以任何语言犯同样的错误。因此,我建议扩大问题范围,以使它不要过于关注代码。大多数错误是概念性错误,而不是编码缺陷。即使答案被接受,也要继续增加经验教训。至少将具有教育意义。
另外,来自Colin Percival的简短演示(tarsnap):bsdcan.org/2010/schedule/attachments/135_crypto1hr.pdf
如果您需要阅读“不做”列表,请不要做加密。请专门研究密码学的安全工程师来帮助您。 :)
相关元讨论:阐明“滚动自己的密码”与“实施标准”之间的关系