安全简报
加密的灵活性
Bryan Sullivan
内容
规划将来利用
潜在的问题
可选的用法和语法
加密灵活性的另一个好处
上的换行
整个历史记录,人员有用于各种形式的密码隐藏从其敌人的信息。 Julius 恺撒使用三位移位密码 (的字母 A 将转换为 D,B 转换为电子,等) 进行通信战斗的行列计划。 在世界是 II,德语的 navy 使用一个明显更高级的系统 — Enigma 计算机 — 加密邮件发送到其 U 船。 目前,我们甚至更复杂的加密机制用作公钥基础结构,可帮助我们在 Internet 上执行安全的事务的一部分。
但对于只要已做 cryptographers 机密代码,cryptanalysts 有已尝试断开它们并窃取信息,和有时断路成功的代码。 中断一次被认为是安全的加密算法,将变得不可用。 在的算法中找到有时细微缺陷,而且有时它是只需访问更多的计算能力进行强力攻击的攻击者。
最近,安全研究人员有冲突的结果说明 MD5 哈希算法中的漏洞 ; 也就是它们具有所示的两个消息可以具有相同计算的 MD5 哈希值。 它们已经创建了一个概念证明攻击此弱点,针对该公用密钥的基础结构保护电子商务网站上的事务。 通过购买特制的 Web 站点证书从证书颁发机构 (CA) 用于 MD5 签署的证书,研究人员能够创建可以有效地使用模拟可能在 Internet 上的任何网站的恶意 CA 证书。 它们结束 MD5 不适用于签名的数字证书应使用如该 SHA 2 算法的一个更强备选方法。 (是否您有兴趣了解更多有关此信息检索,可以读取该 白皮书.)
这些结果是肯定会导致的问题,但它们不是一个巨大的意外。 年中, 所示理论 MD5 弱点,MD5 使用 Microsoft 产品中的已被禁止通过 Microsoft SDL 的加密标准 2005年以来。 有同样被禁止如 SHA-1 和 RC2 的其他常用一次的算法。 图 1显示了加密算法禁止或经 SDL 的完整列表。 SDL 的批准算法的列表是当前在撰写本文但该列表检查,并且每年更新 SDL 更新过程的一部分。
图 1 批准 SDL 的加密算法 |
算法类型 | 禁止 (要替换现有代码中或只用于解密的算法) | 接受 (除敏感数据的现有代码可接受的算法) | 建议 (算法的新的代码) |
对称块 | DES,DESX,rc2 SKIPJACK | 3DES (2 或 3 个键) | AES (> = 128 位) |
对称流 | 密封,CYLINK_MEK,RC4 (< 128 位) | RC4 (> = 128 位) | 无,块密码是首选 |
不对称 | RSA (< 2048 位),Diffie-大 (< 2048 位) | RSA (> = 2048bit),Diffie-大 (> = 2048bit) | RSA (> = 2048bit),Diffie-大 (> = 2048bit),ECC (> = 256 位) |
哈希 (包括 HMAC 使用) | SHA 0 (SHA),SHA-1,md2,md4 md5 | SHA-2 | SHA-2 (包括: SHA-256 SHA-384、 SHA-512) |
HMAC 的密钥长度 | <112 位 | >= 112 位 | >= 128 位 |
如果即使在您自己的代码中执行这些标准,使用最安全的算法和最长的密钥长度的不保证您今天编写的代码将保持安全。 实际上,则将可能不保留任何指南历史记录是否安全。
规划将来利用
可以通过将旧应用程序的基本代码、 挑选出的易受攻击的算法实例化、 它们替换为新的算法,重新生成应用程序,通过回归的测试运行它们,然后颁发修补程序通过立即解决此方案中不或到您的用户的服务包。 这不是只用于您,大量但仍然留下您的用户的风险直到您可以提供修补程序。
一个更好地替代方法是计划从一开始在此方案。 硬编码特定加密算法在代码中,而不是是使用的一个加密的灵活性 Microsoft.NET 框架中内置的功能。 让我们看一看几个 C# 代码段,启动与最灵活的示例:
private static byte[] computeHash(byte[] buffer)
{
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
return md5.ComputeHash(buffer);
}
}
此代码是完全 nonagile。与它相关一个特定的算法 (MD5),以及该算法,MD5CryptoServiceProvider 类的特定实现。修改此应用程序使用安全哈希算法需要更改代码,并发出一个修补程序。下面是一个小的更好示例:
private static byte[] computeHash(byte[] buffer)
{
string md5Impl = ConfigurationManager.AppSettings["md5Impl"];
if (md5Impl == null)
md5Impl = String.Empty;
using (MD5 md5 = MD5.Create(md5Impl))
{
return md5.ComputeHash(buffer);
}
}
此函数使用 System.Configuration.Configuration 管理器类来检索应用程序的配置文件的自定义应用程序设置 ("md5Impl"设置)。 在这种情况下,设置用于存储要使用的算法实现类的强名称。 该代码将检索到此设置的值传递给静态函数 MD5.Create 创建所需的类的实例中。 (System.Security.Cryptography.MD5 是所有的实现,MD5 算法必须派生的抽象基类。 例如,应用程序 md5Impl 被设置为字符串"System.Security.Cryptography.MD5Cng,System.Core,版本 = 3.5.0.0,区域性 = 非特定于,PublicKeyToken = b77a5c561934e089",MD5.Create 会创建 MD5Cng 类的实例。
这种方法解决只有一半我们加密的灵活性的问题实际上根本是没有解决方案。 现在,我们可以指定 MD5 算法的实现而不必更改任何源代码如果缺陷发现类似于 MD5Cng,等特定实现,但我们正在仍绑定到的 MD5 使用一般情况下可能会证明非常有用的。 若要解决此问题,我们保留向上项目抽象化:
private static byte[] computeHash(byte[] buffer)
{
using (HashAlgorithm hash = HashAlgorithm.Create("MD5"))
{
return hash.ComputeHash(buffer);
}
}
第一次看到此代码段不查找第一个示例差别很大。看起来我们已经再一次的硬编码 MD5 算法到通过 HashAlgorithm.Create("MD5") 调用应用程序。令人惊讶的是尽管此代码是显著比其他示例的两个密码更灵活。该方法的默认行为时调用 HashAlgorithm.Create (MD5) —.NET 3.5 的 — 是创建该 MD5CryptoServiceProvider 类的实例可以自定义运行库行为 machine.config 文件进行更改。
让我们来更改此代码创建的而不是 MD5 SHA512algorithm 实例的行为。涓烘 我们需要 machine.config 文件中添加两个元素: 一个 <cryptoclass> 元素映射为友好算法名称以算法实现类我们需要; 和 <nameentry> 元素旧的、 已被否决算法的友好名称映射到新的友好名称。
<configuration>
<mscorlib>
<cryptographySettings>
<cryptoNameMapping>
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512CryptoServiceProvider, System.Core, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="MD5" class="MyPreferredHash"/>
</cryptoNameMapping>
</cryptographySettings>
</mscorlib>
</configuration>
现在,我们的代码发出其调用 HashAlgorithm.Create("MD5") 时, CLR machine.config 文件中查找,并可以看到字符串"MD5"应映射到友好算法名称"MyPreferredHash"。然后它会看到"MyPreferredHash"(如程序集中 System.Core,使用指定的版本、 区域性和公钥标记中定义) 映射到类 SHA512CryptoServiceProvider,创建该类的实例。
值得注意的算法的重新映射会发生运行但不是在编译时: 它是用户的 machine.config,控制重新在映射,不在开发人员的。鍥犳 这种技术解决了我们的被绑定到一个特定的算法可能在将来某个时间中断的问题。避免使用硬编码在加密算法类到应用程序 — — 而编码只加密算法,HashAlgorithm 的抽象类型 — 我们创建的最终用户 (更具体地说就的是具有管理权限的人编辑应用程序的位置安装在计算机上 machine.config 文件) 可以确定精确的算法和实现应用程序将使用的应用程序。管理员可能选择替换最近已断开与仍被认为是安全的算法 (渚嬪的方式 替换 MD5 SHA-256) 或主动安全的算法替换为长的位长度 (替换 SHA-256 SHA 到 512 之间的) 的替代方法。
潜在的问题
修改 machine.config 文件以重新映射默认算法类型字符串 (如"MD5"和"SHA1") 可能会解决加密的灵活性的问题,但它可以同时创建兼容性问题。对 machine.config 的更改会影响计算机上的每个.NET 应用程序。在计算机上安装其他应用程序可能依赖 MD5 专门,并更改这些应用程序使用的算法可能破坏它们难以诊断的意外的方式。作为替代强制总更改到整个计算机的更好在代码中使用自定义、 特定于应用程序的友好名称,并在 machine.config 中的首选类映射这些名称条目。例如,我们可以在我们的示例将"MD5"更改为"MyApplicationHash":
private static byte[] computeHash(byte[] buffer)
{
using (HashAlgorithm hash = HashAlgorithm.Create("MyApplicationHash"))
{
return hash.ComputeHash(buffer);
}
}
我们再添加 machine.config 文件映射到"MyPreferredHash"类的"MyApplicationHash"一项:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512CryptoServiceProvider,
System.Core, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="MyApplicationHash" class="MyPreferredHash"/>
此外可以将多个友好名称映射到同一个类,渚嬪的方式 您可能有一个友好的名称为您的应用程序的每个这种方式更改而不影响计算机上的每个其他应用程序特定的应用程序的行为:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512CryptoServiceProvider,
System.Core, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="MyApplicationHash" class="MyPreferredHash"/>
<nameEntry name="MyApplication2Hash" class="MyPreferredHash"/>
<nameEntry name="MyApplication3Hash" class="MyPreferredHash"/>
但是,我们仍不超出了与兼容性问题方面,乌兹我们自己的应用程序中。 您需要有关的本地变量 (暂时存储) 和数据库和 XML 架构 (永久存储) 的存储大小,提前计划。 渚嬪的方式 MD5 哈希值始终 128 长度为位。 如果您的预算完全的 128 位代码或架构以存储哈希输出中,您不能升级到 SHA-256 (256 位长度输出) 或 SHA 到 512 之间 (512 位长度输出)。
这不会 beg 问题的多少存储就足够了。 足够,512 位还是应使用 1,024,2,048,或多个吗? 我不能提供一个硬规则,因为每个应用程序具有不同的要求,但为一条经验法则建议您预算的两倍的空间散列您当前使用。 为对称和非对称加密的数据,您可能最多保留空间的一个额外的 10 %。 是不可能与输出大小明显大于现有算法的新算法将被广泛接受。
但是,在持久状态 (渚嬪的方式 数据库或文件) 中存储加密的数据或哈希值的应用程序具有更大的问题比保留足够的空间。 如果您保持使用一个算法的数据,然后尝试在以后使用不同的算法的数据上操作,则不将得到预期的结果。 渚嬪的方式 它最好存储哈希密码而不是完整的纯文本版本。 当用户试图登录时,代码可以将与在数据库中存储哈希为用户提供密码的哈希进行比较。 如果哈希匹配,用户是可信的。 但是,如果哈希值存储在一种格式 (假设,MD5),并且应用程序升级到使用其他算法 (例如,SHA-256),用户永远不会将能够登录,因为 SHA-256 密码的哈希值始终将不同于这些相同的密码的 MD5 哈希值。
您可以通过解决此问题,在某些情况下为元数据与实际数据一起存储在原始的算法。 然后,存储的数据时,使用灵活性的方法 (或反射) 实例化该算法最初而不是当前的算法:
private static bool checkPassword(string password, byte[] storedHash,
string storedHashAlgorithm)
{
using (HashAlgorithm hash = HashAlgorithm.Create(storedHashAlgorithm))
{
byte[] newHash =
hash.ComputeHash(System.Text.Encoding.Default.GetBytes(password));
if (newHash.Length != storedHash.Length)
return false;
for (int i = 0; i < newHash.Length; i++)
if (newHash[i] != storedHash[i])
return false;
return true;
}
}
遗憾的是,如果您需要比较两个存储的哈希,它们必须使用相同的算法已创建。 没有只比较 SHA-256 哈希以 MD5 哈希,并确定是否它们都创建来自同一原始数据方法。 姝 ら 棶棰樼殑不好的灵活性加密的解决方案,并且我可以提供最好的建议是您应选择目前最安全的算法,然后制定升级计划,以防以后被中断的算法。 一般情况下,加密灵活性往往会暂时比的数据持久性数据的好得多。
可选的用法和语法
假定您的应用程序设计允许加密灵活性使用,让我们继续看一些替代使用此方法的语法。 我们已经重点几乎完全到相应的文章中的该点的加密哈希算法,但加密灵活性还适用于其他加密算法的类型。 只需调用适当的抽象基类的静态 Create 方法: (机密密钥) 的对称加密算法,AES ; 如 SymmetricAlgorithm AsymmetricAlgorithm 对非对称 (公钥) 例如 RSA 加密算法,KeyedHashAlgorithm 的键控哈希 ; HMAC 基于哈希的消息身份验证代码。
您还可用于加密的灵活性替换标准.NET 加密算法类之一的一个自定义算法类如 该算法,在由 CLR 安全小组开发和上载到 CodePlex 之一:. 但是,建议高度编写您自己的自定义加密库。 您自己的算法,包含一个 ROT13 后跟一个按位左移位并只猫的名称对的 XOR 似乎安全,但它将会带来一些难题的专家代码断字符。 除非您是在加密方面的专家,将算法设计专业人士。
此外抵御开发您自己的算法诱惑) 或要恢复长时间的死,模糊的类似 Vigenère 密码 — 甚至在不需要加密型强的保护的情况下。 问题不是那么多什么您但什么的开发人员有后您将使用它执行的您密码的。 新开发人员在您的自定义算法类中查找代码基年以后可能决定只是他需要的内容的新产品激活的密钥生成逻辑。
到目前为止我们见过用于实现灵活的加密代码,AlgorithmType.Create(algorithmName),语法之一,但.NET 框架中内置其他两种方法。 第一个是使用 System.Security.Cryptography.CryptoConfig 类:
private static byte[] computeHash(byte[] buffer)
{
using (HashAlgorithm hash = (HashAlgorithm)CryptoConfig.CreateFromName("MyApplicationHash"))
{
return hash.ComputeHash(buffer);
}
}
此代码执行相同的操作与使用 HashAlgorithm.Create("MyApplicationHash") 我们前一个示例: CLR 的字符串"MyApplicationHash"的一个重新映射 machine.config 文件中查找和使用 remapped 的算法类,如果它找到一个。 请注意我们需转换 CryptoConfig.CreateFromName 的结果,因为它具有 System.Object 的返回类型,并可用于创建 SymmetricAlgorithms、 AsymmetricAlgorithms 或任何其他类型的对象。
第二个替代语法是调用静态算法创建方法,我们的原始示例但不带参数如下:
private static byte[] computeHash(byte[] buffer)
{
using (HashAlgorithm hash = HashAlgorithm.Create())
{
return hash.ComputeHash(buffer);
}
}
在此代码,我们只需询问框架提供的任何默认哈希算法实现的实例。 可以在 图 2 中 System.Security.Cryptography 抽象基类 (的.NET 3.5) 的每个找到的默认值的列表。
图 2 的默认算法和实现在.NET Framework 3.5 中 |
抽象基类 | 默认算法 | 默认实现 |
HashAlgorithm | SHA-1 | SHA1CryptoServiceProvider |
SymmetricAlgorithm | AES (Rijndael) | RijndaelManaged |
AsymmetricAlgorithm | RSA | RSACryptoServiceProvider |
KeyedHashAlgorithm | SHA-1 | hmacsha1 |
HMAC | SHA-1 | hmacsha1 |
为 HashAlgorithm,您可以看到默认的算法是 SHA-1,默认实现类 SHA1CryptoServiceProvider。 但是,我们知道 SHA-1 禁止 SDL 的加密标准。 目前,为我们忽略这一事实潜在的兼容性问题使其重新映射固有的算法名称类似于"SHA1"并更改以重新"SHA1"映射到 SHA512CryptoServiceProvider 我们 machine.config 通常 unwise:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512CryptoServiceProvider, System.Core, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="SHA1" class="MyPreferredHash"/>
现在让我们来插入 computeHash 函数以确认的算法已正确地重新映射,然后运行应用程序中的调试行:
private static byte[] computeHash(byte[] buffer)
{
using (HashAlgorithm hash = HashAlgorithm.Create())
{
Debug.WriteLine(hash.GetType());
return hash.ComputeHash(buffer);
}
}
调试输出从该方法为:
System.Security.Cryptography.SHA1CryptoServiceProvider
发生了什么变化? 我们不能重新映射 SHA1 SHA 到 512 之间吗? 实际,不,我们没有。 我们重新映射只能字符串"SHA1"到类 SHA512CryptoServiceProvider,并将我们确实不传递字符串"SHA1"作为参数给 HashAlgorithm.Create 调用。
即使创建似乎没有字符串参数,以重新映射,就仍可以更改已创建的对象的类型。 可以只是快捷方式语法 HashAlgorithm.Create("System.Security.Cryptography.HashAlgorithm") HashAlgorithm.Create() 是执行此操作。 现在让我们将添加到重新映射到 SHA512CryptoServiceProvider"System.Security.Cryptography.HashAlgorithm"并再次运行该应用程序 machine.config 文件中其他行:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512CryptoServiceProvider, System.Core, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="SHA1" class="MyPreferredHash"/>
<nameEntry name="System.Security.Cryptography.HashAlgorithm" class="MyPreferredHash"/>
调试输出从 computeHash 正是现在我们的预期:
System.Security.Cryptography.SHA512CryptoServiceProvider
但是,请记住重新映射的类,这种方式可以创建意外和难以调试的兼容性问题。是特定于应用程序的友好名称可以被重新映射使用较少导致问题的机会更可取。
加密灵活性的另一个好处
除了让您替换而无需重新编译的动态中断的算法,加密灵活性可用于提高性能。如果以前已经看 System.Security.Cryptography 命名空间,您可能已经注意到通常几个不同的实现类存在一个给定的算法。例如,有 SHA 到 512 之间的三个不同的实现: SHA512Cng,SHA512CryptoServiceProvider,和 SHA512Managed。
这些类,SHA512Cng 通常提供了最佳的性能。在我的便携式计算机 (运行 Windows 7 候选发布) 快速测试所显示 –Cng 类通常 10 % 更快-CryptoServiceProvider 和托管类。我的同事核心体系结构组中通知我在某些情况下 –Cng 类可以实际运行的 10 倍比其他更快 !
清楚地,使用 –Cng 类最好,并将我们可以设置我们 machine.config 文件重新映射算法使用这些的类的实现,但-Cng 类上不支持每个操作系统。只有 Windows Vista,Windows Server 2008 和 Windows 7 (及更高版本,大概) 支持 –Cng。尝试实例化 –Cng 类在任何其他操作系统上的,则将引发异常。
同样,–Managed 系列的加密类 (AesManaged、 RijndaelManaged、 SHA256Managed,等) 并不总是可用,但完全不同的原因。在联邦信息处理标准 140 (FIPS) 指定标准的加密算法和实现。在撰写本文 –Cng 和 –CryptoServiceProvider 实现类 FIPS 认证但 –Managed 类不。此外,您可以配置允许仅符合 FIPS 的算法使用的组策略设置。某些美国和加拿大的政府机构要求此策略设置。如果您想要检查您的计算机,打开本地组策略编辑器 (gpedit.msc),定位到该计算机配置 /Windows 设置/安全设置/本地策略/安全选项节点并检查设置的值"系统加密: 使用 FIPS 兼容的算法来加密,哈希,和签名"。如果此策略设置为启用,但试图实例化 –Managed 类在该计算机上的将引发异常。
这为能保证在所有的平台上的工作最小公分离开 –CryptoServiceProvider 系列类的但这些类也通常最差的性能。您可以通过实现前面所述这三个加密的灵活性语法之一来解决此问题文章和自定义重新在 machine.config 文件映射为部署基于其鎿嶄綔绯荤粺和设置的计算机。对于运行 Windows Vista 的计算机或更高版本,我们可以重新映射到喜欢 –Cng 实现类 machine.config:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512Cng, System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="MyApplicationHash" class="MyPreferredHash"/>
鎿嶄綔绯荤粺运行早于 Windows Vista 禁用 FIPS 兼容的计算机的我们可以重新映射为首选 –Managed 类 machine.config:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512Managed"/>
</cryptoClasses>
<nameEntry name="MyApplicationHash" class="MyPreferredHash"/>
对于所有其他计算机我们重新映射到 –CryptoServiceProvider 类:
<cryptoClasses>
<cryptoClass MyPreferredHash="SHA512CryptoServiceProvider, System.Core, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</cryptoClasses>
<nameEntry name="MyApplicationHash" class="MyPreferredHash"/>
任何调用 HashAlgorithm.Create("MyApplicationHash") 现在可创建最高执行实现类可用于该计算机。此外,由于在算法相同,您不必担心兼容性或互操作性问题。即使实现类不同,为给定的输入值一台计算机上创建哈希值将是为创建另一个上的相同输入值的哈希值相同。这适用于其他算法类型以及: 您可以使用 AesManaged 加密的一台计算机上输入并解密成功地在不同计算机上使用 AesCryptoServiceProvider。
上的换行
给定的时间和费用的记录应用程序以响应中断的加密算法不提到危险给您的用户,直到您可以获得新的版本部署,最好本次规划和加密灵活的方式编写应用程序。这一事实也可以从编码这种方式获得一个性能优点糖衣在蛋糕上。
永远不会硬编码特定算法或到您的应用程序的这些算法的实现。始终将加密算法声明为下列的抽象算法类型类之一: HashAlgorithm,SymmetricAlgorithm,AsymmetricAlgorithm,KeyedHashAlgorithm,或 HMAC。
我认为会非常有用的 FxCop 规则,将验证加密的灵活性。如果有人编写这样一个规则 Codeplex 或另一个公共代码库发送它我将使其完全贷方该空间中和在多个快乐,SDL 博客.
最后,我想确认 Shawn Hernan SQL Server 安全团队和 Shawn Farkas 从 CLR 安全小组的专家反馈和帮助生成这篇文章。
将您的问题和提出发送到briefs@microsoft.com.
王志 Sullivan 是管理一个安全程序器,Microsoft 安全开发生命周期团队特殊化的 Web 应用程序安全问题。他是 Ajax Security (Addison-Wesley,2007年) 的作者。