注意: 已于 2023 年 6 月 22 日修订,以更新解决方法和解决方法
注意: 修订于 2023 年 6 月 15 日,以更新选项 4 和 5 的解决方法
背景
2023 年 6 月 13 日,Microsoft 发布了针对 .NET Framework 和 .NET 的安全更新,该更新会影响运行时导入 X.509 证书的方式。 在更新之前导入成功的情况下,这些更改可能会导致 X.509 证书导入引发 CryptographicException。
本文档介绍适用于受影响应用程序的更改和解决方法。
受影响的软件
-
.NET Framework 2.0
-
.NET Framework 4.6.2、4.7、4.7.1、4.7.2
-
.NET Framework 4.8
-
.NET Framework 4.8.1
-
.NET 6.0
-
.NET 7.0
受影响的 API
更改说明
在 2023 年 6 月 13 日之前,当向 .NET Framework 和 .NET 提供用于导入的二进制证书 Blob 时,.NET Framework 和 .NET 通常会将 blob 的验证和导入委托给基础 OS。 例如,在 Windows 上,.NET Framework和 .NET 通常依赖于 PFXImportCertStore API 进行验证和导入。
从 2023 年 6 月 13 日开始,当 .NET Framework 和 .NET 提供用于导入的二进制证书 Blob 时,.NET Framework 和 .NET 在某些情况下将执行其他验证,然后再将 blob 交给基础 OS。 此附加验证执行一系列启发式检查,以确定传入的证书是否会在导入时恶意耗尽资源。 由于 这是基础 OS 通常执行的其他验证,因此可能会阻止在 2023 年 6 月 13 日更改之前成功导入的证书 Blob。
已知回归
-
如果 X.509 证书已导出为 PFX Blob,并且密码迭代计数异常高,该证书现在可能无法导入。 大多数证书导出设施使用介于 2,000 到 10,000 之间的迭代计数。 应用安全更新后,对于迭代计数大于 600,000 的证书,导入将失败。
-
如果 X.509 证书已使用空密码 [例如,通过X509Certificate.Export(X509ContentType.Pfx, (string)null)或无密码X509Certificate.Export(X509ContentType.Pfx)]导出,则该证书现在可能无法导入。
注意: 上述回归已在 KB5028608 中讨论的 2023 年 6 月 22 日更新中得到解决。
-
如果使用 Windows 保护 SID 私钥的功能将 X.509 证书导出为 PFX Blob,该证书现在可能无法导入。 这将影响以以下方式创建的 PFX Blob:
-
通过 Windows 的 证书导出向导 并在向导中指定应保护域用户的私钥;或
-
通过 PowerShell 的 Export-PfxCertificate cmdlet,其中提供了显式 -ProtectTo 参数;或
-
通过 certutil 实用工具,其中提供了显式 -protectto 参数;或
-
通过 PFXExportCertStoreEx API,其中提供了 PKCS12_PROTECT_TO_DOMAIN_SIDS 标志。
-
解决方法&解决方法
存在各种解决方法,具体取决于是要在代码中的单个调用站点上进行有针对性的更改,还是要更改单个应用程序的行为,或者要进行计算机范围的更改。
选项 1 (首选) - 安装更新的修补程序
注意: 这是首选选项,因为它解决了经常报告的客户回归问题,并且不需要对应用程序进行任何代码更改。
适用性:此选项适用于所有版本的 .NET Framework 和 .NET。
此问题已在 KB5028608 中讨论的 2023 年 6 月 22 日更新中得到解决。
Microsoft 建议遇到 2023 年 6 月 13 日版本引入的回归的客户先尝试安装此更新的修补程序,然后再尝试本文档后面列出的解决方法。
选项 2 - 修改呼叫站点
适用性:此选项适用于所有版本的 .NET Framework 和 .NET。
考虑要导入的 Blob 是否可信。 例如,Blob 是从受信任的位置(如受你控制的数据库或配置文件)检索的,还是通过未经身份验证或未经特权的客户端发出的网络请求提供的?
Microsoft 强烈建议不要导入未经身份验证或未经特权的客户端提供的 PFX Blob,因为这些 Blob 可能包含恶意资源耗尽行为。
如果需要导入不受信任的一方提供给你的公钥证书 Blob,可以使用以下代码安全地导入此类 Blob。 此示例代码使用 GetCertContentType 方法确定证书 Blob 的基础类型,并在只希望导入公钥证书 Blob 的情况下拒绝 PFX Blob。 当给定不受信任的非 PFX Blob 时,X509Certificate2(byte[]) 构造函数是安全的。
using System.Security.Cryptography.X509Certificates;
public static X509Certificate2 ImportPublicCertificateBlob(byte[] blob)
{
if (X509Certificate2.GetCertContentType(blob) == X509ContentType.Pfx)
{
throw new Exception("PFX blobs are disallowed.");
}
else
{
// Import only after we have confirmed it's not a PFX.
return new X509Certificate2(blob);
}
}
如果需要导入无密码私钥证书 Blob,并且已确定该 Blob 是可信的,可以通过调用其他构造函数重载来取消在 2023 年 6 月 13 日安全版本执行的其他验证检查。 例如,可以调用构造函数重载,该重载接受字符串密码参数,并为参数值传递 null。
byte[] blobToImport = GetBlobToImport(); // fetch this from a database, config, etc.
// REGRESSION - byte[] ctor performs additional security checks X509Certificate2 certA = new X509Certificate2(blobToImport);
// RECOMMENDED WORKAROUND - different ctor overload suppresses additional security checks X509Certificate2 certB = new X509Certificate2(blobToImport, (string)null);
选项 3 - 使用环境变量修改或取消附加验证
适用性:此选项仅适用于.NET Framework的所有版本。 它不适用于 .NET 6.0+。
虽然.NET Framework默认将导入操作限制为密码的迭代次数不超过 600,000 次,但可以使用环境变量在应用范围或计算机范围内配置此限制。 此新限制将应用于上面列出的受影响的 API 的所有调用。
若要更改限制,请将环境变量COMPlus_Pkcs12UnspecifiedPasswordIterationLimit设置为新限制的值。 例如,若要将限制设置为 1,000,000 (100 万次) 迭代,请将环境变量设置为如下所示。
-
此数字控制 总 迭代限制,即 MAC 迭代计数、加密的安全内容和掩码包的迭代计数的总和。 如果已使用显式迭代计数<iter_count> (手动导出 PFX,例如,通过 openssl pkcs12 -export -iter <iter_count>) 并希望导入该 PFX Blob,请将此环境变量设置为至少与所有预期迭代的总和相同的值。 实际上,.NET Framework和 .NET 可能允许总迭代计数略高于此处配置的任何显式限制。
COMPlus_Pkcs12UnspecifiedPasswordIterationLimit=1000000
若要完全取消附加检查,请将环境变量设置为特殊 sentinel 值 -1,如下所示。
-
⚠️ 警告:仅当确定目标应用程序未处理不受信任的证书输入时,才会将环境变量值设置为 -1。
COMPlus_Pkcs12UnspecifiedPasswordIterationLimit=-1
选项 4 - 使用 AppContext 修改或取消附加验证
适用性:此选项仅适用于 .NET 6.0+。 它不适用于.NET Framework
虽然 .NET 默认将导入操作限制为密码的迭代次数不超过 600,000 次,但可以使用 AppContext 开关在应用程序范围内配置此限制。 此新限制将应用于上面列出的受影响的 API 的所有调用。
若要更改限制,请将 AppContext 开关 System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit 设置为新限制的值。 例如,若要将限制设置为 1,000,000 (100 万次) 迭代,请将开关设置为如下所示。
-
此数字控制总迭代限制,即 MAC 迭代计数、加密的安全内容和掩码包的迭代计数的总和。 如果已使用显式迭代计数<iter_count> (手动导出 PFX,例如,通过 openssl pkcs12 -export -iter <iter_count>) 并希望导入该 PFX Blob,请将此环境变量设置为至少与所有预期迭代的总和相同的值。 实际上,.NET 可能允许总迭代计数略高于此处配置的任何显式限制。
若要在应用程序的项目文件 (.csproj 或 .vbproj) 中设置开关,请执行以下操作:
<!--
- This switch only works if the current project file represents an application. It has no effect if the current project file represents a shared library.
-->
<ItemGroup>
- <RuntimeHostConfigurationOption Include="System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit" Value="1000000" />
</ItemGroup>
或者,可以将包含以下内容的名为 runtimeconfig.template.json 的文件放在包含应用程序项目文件的同一目录中:
{
"configProperties": {
- "System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit": 1000000
}
}
有关更改 .NET 运行时配置设置的详细信息,请参阅文档页 .NET 运行时配置设置。
若要完全取消其他检查,请将配置开关设置为特殊 sentinel 值 -1,如下所示。
⚠️ 警告:仅当确定目标应用程序未处理不受信任的证书输入时,才会将 AppContext 开关设置为 -1。
在应用程序的项目文件 (.csproj 或 .vbproj) :
<!--
- This switch only works if the current project file represents an application. It has no effect if the current project file represents a shared library.
-->
<ItemGroup>
- <RuntimeHostConfigurationOption Include="System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit" Value="-1" />
</ItemGroup>
或在 runtimeconfig.template.json 文件中:
{
- "configProperties": {
- "System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit": -1
}
}
选项 5 - 通过注册表 (仅限 Windows 来修改或取消计算机范围的附加验证.NET Framework)
适用性:此选项仅适用于.NET Framework的所有版本。 它不适用于 .NET 6.0+。
虽然.NET Framework默认将导入操作限制为密码的迭代次数不超过 600,000 次,但可以使用 HKLM 注册表在计算机范围内配置此限制。 此新限制将应用于上面列出的受影响的 API 的所有调用。
若要更改限制,请在注册表项HKLM\Software\Microsoft\.NETFramework下,将Pkcs12UnspecifiedPasswordIterationLimit的值设置为新的限制。 例如,若要将限制设置为 1,000,000 (100 万次) 迭代,请在提升的命令提示符中运行如下所示的命令。
-
此数字控制 总 迭代限制,即 MAC 迭代计数、加密的安全内容和掩码包的迭代计数的总和。 如果已使用显式迭代计数<iter_count> (手动导出 PFX,例如,通过 openssl pkcs12 -export -iter <iter_count>) 并希望导入该 PFX blob,请将此注册表值设置为至少与所有预期迭代的总和相同的值。 实际上,.NET Framework可能允许总迭代计数略高于此处配置的任何显式限制。
-
注册表设置依赖于体系结构。 若要确保应用程序观察配置的值,而不考虑其目标体系结构,请记住同时修改 32 位和 64 位注册表,如下所示。
reg add "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /t REG_DWORD /d 1000000 /reg:32 reg add "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /t REG_DWORD /d 1000000 /reg:64
若要完全取消其他检查,请在提升的命令提示符下将注册表值设置为 -1,如下所示。
-
⚠️ 警告:仅当确定目标计算机上运行的服务未处理不受信任的证书输入时,才将注册表值设置为 -1。
-
若要设置 -1 sentinel,请使用 REG_SZ 类型,而不是 REG_DWORD 类型。 注册表设置依赖于体系结构。 若要确保应用程序观察配置的值,而不考虑其目标体系结构,请记住同时修改 32 位和 64 位注册表,如下所示。
reg add "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /t REG_SZ /d -1 /reg:32 reg add "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /t REG_SZ /d -1 /reg:64
若要还原注册表更改,请从提升的命令提示符中删除 Pkcs12UnspecifiedPasswordIterationLimit reg 值。
reg delete "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /reg:32 reg delete "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /reg:64
特定于 Windows 的备注
在 Windows 上,.NET Framework通过 PFXImportCertStore 函数导入证书。 此函数执行自己的验证,包括对 PFX Blob 的最大允许迭代计数设置自己的限制。 这些检查仍将在 PFX 导入时进行。 。上述特定于 NET 的环境变量和注册表项不会影响 PFXImportCertStore 执行这些检查的方式。