Applies To.NET

注意: 已于 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。

已知回归

  1. 如果 X.509 证书已导出为 PFX Blob,并且密码迭代计数异常高,该证书现在可能无法导入。 大多数证书导出设施使用介于 2,000 到 10,000 之间的迭代计数。 应用安全更新后,对于迭代计数大于 600,000 的证书,导入将失败。

  2. 如果 X.509 证书已使用密码 [例如,通过X509Certificate.Export(X509ContentType.Pfx, (string)null)或无密码X509Certificate.Export(X509ContentType.Pfx)]导出,则该证书现在可能无法导入。  

    注意: 上述回归已在 KB5028608 中讨论的 2023 年 6 月 22 日更新中得到解决。

  3. 如果使用 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 执行这些检查的方式。

需要更多帮助?

需要更多选项?

了解订阅权益、浏览培训课程、了解如何保护设备等。

社区可帮助你提出和回答问题、提供反馈,并听取经验丰富专家的意见。