Applies To.NET

Note: Revised June 22, 2023 to update resolution and workarounds

Note: Revised June 15, 2023 to update work around options 4 and 5 

Background

On June 13, 2023, Microsoft released a security update to .NET Framework and .NET which impacts how the runtime imports X.509 certificates. These changes may cause X.509 certificate import to throw CryptographicException in scenarios where import would have succeeded prior to the update.

This document describes the change and workarounds available for impacted applications.

Affected software

  • .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

Affected APIs

Description of change

Prior to the June 13, 2023, change, when .NET Framework and .NET is presented with a binary certificate blob for import, .NET Framework and .NET would typically delegate validation and import of the blob to the underlying OS. For example, on Windows, .NET Framework and .NET would typically rely on the PFXImportCertStore API for validation and import.

As of the June 13, 2023, change, when .NET Framework and .NET is presented with a binary certificate blob for import, .NET Framework and .NET will in some circumstances perform additional validation before handing the blob to the underlying OS. This additional validation performs a series of heuristic checks to determine if the incoming certificate would maliciously exhaust resources upon import. Since this is additional validation beyond what the underlying OS would normally perform, it may block certificate blobs which would have successfully imported prior to the June 13, 2023, change.

Known regressions

  1. If an X.509 certificate has been exported as a PFX blob using an uncommonly high password iteration count, that certificate may now fail to import. Most certificate export facilities use an iteration count somewhere between 2,000 - 10,000. After the security update is applied, import will fail for certificates containing an iteration count greater than 600,000.

  2. If an X.509 certificate has been exported using a null password [e.g., via X509Certificate.Export(X509ContentType.Pfx, (string)null) or the passwordless X509Certificate.Export(X509ContentType.Pfx)], that certificate may now fail to import.  

    Note: The above regression has been addressed in the June 22, 2023 Update discussed in KB5028608.

  3. If an X.509 certificate has been exported as a PFX blob using Windows's capability to protect the private key to a SID, that certificate may now fail to import. This will impact PFX blobs created in the following manners:

    • Via Windows's Certificate Export Wizard and specifying in the wizard that the private key should be protected to a domain user; or

    • Via PowerShell's Export-PfxCertificate cmdlet where an explicit -ProtectTo argument is provided; or

    • Via the certutil utility where an explicit -protectto argument is provided; or

    • Via the PFXExportCertStoreEx API where the PKCS12_PROTECT_TO_DOMAIN_SIDS flag is provided.

Resolution & Workarounds

Various workarounds exist, depending on whether you want to make targeted changes at individual call sites within your code, or you want to change the behavior of a single application, or you want to make machine-wide changes.

Option 1 (preferred) - Install an updated patch

Note: This is the preferred option since it addresses commonly reported customer regressions and does not require any code changes to the application.

Applicability: This option applies to all versions of .NET Framework and .NET.

This issue has been addressed in the June 22, 2023 Update discussed in KB5028608.

Microsoft recommends that customers who are experiencing regressions introduced by the June 13, 2023, release try installing this updated patch before attempting the workarounds listed later in this document.

Option 2 - Modifying the call site

Applicability: This option applies to all versions of .NET Framework and .NET.

Consider whether the blob you're importing is trustworthy. For example, was the blob retrieved from a trusted location, like a database or config file under your control, or was it provided via a network request made by an unauthenticated or unprivileged client?

Microsoft strongly recommends that you do not import PFX blobs provided to you by unauthenticated or unprivileged clients, as these blobs could contain malicious resource exhaustion behaviors.

If you need to import a public key certificate blob given to you by an untrusted party, you can use the following code to safely import such a blob. This sample code uses the GetCertContentType method to determine what the underlying type of the certificate blob is, and it rejects PFX blobs in cases where you only expect to import a public key certificate blob. The X509Certificate2(byte[]) constructor is safe for use when given untrusted non-PFX blobs.

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);
    }
} 

If you need to import a passwordless private key certificate blob and you have determined that the blob is trustworthy, you can suppress the additional validation checks performed by the June 13, 2023, security release by calling a different constructor overload. For example, you can call the constructor overload which accepts a string password argument and pass null for the argument value. 

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);

Option 3 - Modifying or suppressing the additional validation using an environment variable

Applicability: This option applies to all versions of .NET Framework only.  It does not apply to .NET 6.0+.

While .NET Framework by default limit import operations to take no more than 600,000 iterations of a password, this limit can be configured on an app-wide or machine-wide basis by using an environment variable. This new limit will apply to all invocations of the Affected APIs listed above.

To change the limit, set the environment variable COMPlus_Pkcs12UnspecifiedPasswordIterationLimit to the value of what the new limit should be. For example, to set the limit to 1,000,000 (one million) iterations, set the environment variable as shown below.

  • This number controls the total iteration limit, which is the sum of the MAC iteration count, encrypted safe contents, and shrouded bag's iteration count. If you have manually exported a PFX using an explicit iteration count <iter_count> (e.g., via openssl pkcs12 -export -iter <iter_count>) and wish to import that PFX blob, set this environment variable to a value at least as large as the sum of all expected iterations. In practice, .NET Framework and .NET may allow the total iteration count to slightly exceed any explicit limit configured here.

COMPlus_Pkcs12UnspecifiedPasswordIterationLimit=1000000

To suppress the additional checks entirely, set the environment variable to the special sentinel value -1, as shown below.

  • ⚠️ Warning: Only set the environment variable value to -1 if you're certain that the target application isn't handling untrusted certificate input.

COMPlus_Pkcs12UnspecifiedPasswordIterationLimit=-1

Option 4 - Modifying or suppressing the additional validation using AppContext

Applicability: This option applies to .NET 6.0+only.  It does not apply to .NET Framework

While .NET by default limit import operations to take no more than 600,000 iterations of a password, this limit can be configured application-wide by using the AppContext switch. This new limit will apply to all invocations of the Affected APIs listed above.

To change the limit, set the AppContext switch System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit to the value of what the new limit should be. For example, to set the limit to 1,000,000 (one million) iterations, set the switch as shown below.

  • This number controls the total iteration limit, which is the sum of the MAC iteration count, encrypted safe contents, and shrouded bag's iteration count. If you have manually exported a PFX using an explicit iteration count <iter_count> (e.g., via openssl pkcs12 -export -iter <iter_count>) and wish to import that PFX blob, set this environment variable to a value at least as large as the sum of all expected iterations. In practice, .NET may allow the total iteration count to slightly exceed any explicit limit configured here.

To set the switch within your application's project file (.csproj or .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>

Alternatively, you can place a file named runtimeconfig.template.json with the following contents in the same directory which contains your application's project file:

{

     "configProperties": {

  • "System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit": 1000000

      }

}

For more information on changing .NET runtime configuration settings, see the documentation page .NET Runtime configuration settings.

To suppress the additional checks entirely, set the configuration switch the special sentinel value -1, as shown below.

⚠️ Warning: Only set the AppContext switch to -1 if you're certain that the target application isn't handling untrusted certificate input.

Within the application's project file (.csproj or .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>

Or within the runtimeconfig.template.json file:

{

  • "configProperties": { 
  •     "System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit": -1

     }

}

Option 5 - Modifying or suppressing the additional validation machine-wide via the registry (Windows-only for .NET Framework)

Applicability: This option applies to all versions of .NET Framework only.  It does not apply to .NET 6.0+.

While .NET Framework by default limit import operations to take no more than 600,000 iterations of a password, this limit can be configured machine-wide by using the HKLM registry. This new limit will apply to all invocations of the Affected APIs listed above.

To change the limit, under the registry key HKLM\Software\Microsoft\.NETFramework, set the value Pkcs12UnspecifiedPasswordIterationLimit to what the new limit should be. For example, to set the limit to 1,000,000 (one million) iterations, run the commands as shown below from an elevated command prompt.

  • This number controls the total iteration limit, which is the sum of the MAC iteration count, encrypted safe contents, and shrouded bag's iteration count. If you have manually exported a PFX using an explicit iteration count <iter_count> (e.g., via openssl pkcs12 -export -iter <iter_count>) and wish to import that PFX blob, set this registry value to a value at least as large as the sum of all expected iterations. In practice, .NET Framework may allow the total iteration count to slightly exceed any explicit limit configured here.

  • The registry setting is architecture-dependent. To ensure that applications observe your configured value regardless of their target architecture, remember to modify both the 32-bit and the 64-bit registries, as shown below.

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

To suppress the additional checks entirely, set the registry value to -1 from an elevated command prompt, as shown below.

  • ⚠️ Warning: Only set the registry value to -1 if you're certain that the services running on the target machine aren't handling untrusted certificate input.

  • To set the -1 sentinel, use the REG_SZ type instead of the REG_DWORD type. The registry setting is architecture-dependent. To ensure that applications observe your configured value regardless of their target architecture, remember to modify both the 32-bit and the 64-bit registries, as shown below.

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

To revert the registry changes, delete the Pkcs12UnspecifiedPasswordIterationLimit reg value from an elevated command prompt.

reg delete "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /reg:32 reg delete "HKLM\Software\Microsoft\.NETFramework" /v Pkcs12UnspecifiedPasswordIterationLimit /reg:64

Windows-specific notes

On Windows, .NET Framework import certificates through the PFXImportCertStore function. This function performs its own validation, including placing its own limits on a PFX blob's maximum allowable iteration count. These checks will still take place upon PFX import. The .NET-specific environment variables and registry keys described above do not impact how PFXImportCertStore performs these checks.

Need more help?

Want more options?

Explore subscription benefits, browse training courses, learn how to secure your device, and more.

Communities help you ask and answer questions, give feedback, and hear from experts with rich knowledge.