Cryptography breaking changes for .NET Core 2.1-3.0

The following breaking changes are documented on this page:

Breaking change Version introduced
BEGIN TRUSTED CERTIFICATE syntax no longer supported on Linux 3.0
EnvelopedCms defaults to AES-256 encryption 3.0
Minimum size for RSAOpenSsl key generation has increased 3.0
.NET Core 3.0 prefers OpenSSL 1.1.x to OpenSSL 1.0.x 3.0
CryptoStream.Dispose transforms final block only when writing 3.0
Boolean parameter of SignedCms.ComputeSignature is respected 2.1

.NET Core 3.0

"BEGIN TRUSTED CERTIFICATE" syntax no longer supported for root certificates on Linux

Root certificates on Linux and other Unix-like systems (but not macOS) can be presented in two forms: the standard BEGIN CERTIFICATE PEM header, and the OpenSSL-specific BEGIN TRUSTED CERTIFICATE PEM header. The latter syntax allows for additional configuration that has caused compatibility issues with .NET Core's System.Security.Cryptography.X509Certificates.X509Chain class. BEGIN TRUSTED CERTIFICATE root certificate contents are no longer loaded by the chain engine starting in .NET Core 3.0.

Change description

Previously, both the BEGIN CERTIFICATE and BEGIN TRUSTED CERTIFICATE syntaxes were used to populate the root trust list. If the BEGIN TRUSTED CERTIFICATE syntax was used and additional options were specified in the file, X509Chain may have reported that the chain trust was explicitly disallowed (X509ChainStatusFlags.ExplicitDistrust). However, if the certificate was also specified with the BEGIN CERTIFICATE syntax in a previously loaded file, the chain trust was allowed.

Starting in .NET Core 3.0, BEGIN TRUSTED CERTIFICATE contents are no longer read. If the certificate is not also specified via a standard BEGIN CERTIFICATE syntax, the X509Chain reports that the root is not trusted (X509ChainStatusFlags.UntrustedRoot).

Version introduced

3.0

Most applications are unaffected by this change, but applications that cannot see both root certificate sources because of permissions problems may experience unexpected UntrustedRoot errors after upgrading.

Many Linux distributions (or distros) write root certificates into two locations: a one-certificate-per-file directory, and a one-file concatenation. On some distros, the one-certificate-per-file directory uses the BEGIN TRUSTED CERTIFICATE syntax while the file concatenation uses the standard BEGIN CERTIFICATE syntax. Ensure that any custom root certificates are added as BEGIN CERTIFICATE in at least one of these locations, and that both locations can be read by your application.

The typical directory is /etc/ssl/certs/ and the typical concatenated file is /etc/ssl/cert.pem. Use the command openssl version -d to determine the platform-specific root, which may differ from /etc/ssl/. For example, on Ubuntu 18.04, the directory is /usr/lib/ssl/certs/ and the file is /usr/lib/ssl/cert.pem. However, /usr/lib/ssl/certs/ is a symlink to /etc/ssl/certs/ and /usr/lib/ssl/cert.pem does not exist.

$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"
$ ls -al /usr/lib/ssl
total 12
drwxr-xr-x  3 root root 4096 Dec 12 17:10 .
drwxr-xr-x 73 root root 4096 Feb 20 15:18 ..
lrwxrwxrwx  1 root root   14 Mar 27  2018 certs -> /etc/ssl/certs
drwxr-xr-x  2 root root 4096 Dec 12 17:10 misc
lrwxrwxrwx  1 root root   20 Nov 12 16:58 openssl.cnf -> /etc/ssl/openssl.cnf
lrwxrwxrwx  1 root root   16 Mar 27  2018 private -> /etc/ssl/private

Category

Cryptography

Affected APIs


EnvelopedCms defaults to AES-256 encryption

The default symmetric encryption algorithm used by EnvelopedCms has changed from TripleDES to AES-256.

Change description

In previous versions, when EnvelopedCms is used to encrypt data without specifying a symmetric encryption algorithm via a constructor overload, the data is encrypted with the TripleDES/3DES/3DEA/DES3-EDE algorithm.

Starting with .NET Core 3.0 (via version 4.6.0 of the System.Security.Cryptography.Pkcs NuGet package), the default algorithm has been changed to AES-256 for algorithm modernization and to improve the security of default options. If a message recipient certificate has a (non-EC) Diffie-Hellman public key, the encryption operation may fail with a CryptographicException due to limitations in the underlying platform.

In the following sample code, the data is encrypted with TripleDES if running on .NET Core 2.2 or earlier. If running on .NET Core 3.0 or later, it's encrypted with AES-256.

EnvelopedCms cms = new EnvelopedCms(content);
cms.Encrypt(recipient);
return cms.Encode();

Version introduced

3.0

If you are negatively impacted by the change, you can restore TripleDES encryption by explicitly specifying the encryption algorithm identifier in an EnvelopedCms constructor that includes a parameter of type AlgorithmIdentifier, such as:

Oid tripleDesOid = new Oid("1.2.840.113549.3.7", null);
AlgorithmIdentifier tripleDesIdentifier = new AlgorithmIdentifier(tripleDesOid);
EnvelopedCms cms = new EnvelopedCms(content, tripleDesIdentifier);

cms.Encrypt(recipient);
return cms.Encode();

Category

Cryptography

Affected APIs


Minimum size for RSAOpenSsl key generation has increased

The minimum size for generating new RSA keys on Linux has increased from 384-bit to 512-bit.

Change description

Starting with .NET Core 3.0, the minimum legal key size reported by the LegalKeySizes property on RSA instances from RSA.Create, RSAOpenSsl, and RSACryptoServiceProvider on Linux has increased from 384 to 512.

As a result, in .NET Core 2.2 and earlier versions, a method call such as RSA.Create(384) succeeds. In .NET Core 3.0 and later versions, the method call RSA.Create(384) throws an exception indicating the size is too small.

This change was made because OpenSSL, which performs the cryptographic operations on Linux, raised its minimum between versions 1.0.2 and 1.1.0. .NET Core 3.0 prefers OpenSSL 1.1.x to 1.0.x, and the minimum reported version was raised to reflect this new higher dependency limitation.

Version introduced

3.0

If you call any of the affected APIs, ensure that the size of any generated keys is not less than the provider minimum.

Note

384-bit RSA is already considered insecure (as is 512-bit RSA). Modern recommendations, such as NIST Special Publication 800-57 Part 1 Revision 4, suggest 2048-bit as the minimum size for newly generated keys.

Category

Cryptography

Affected APIs


.NET Core 3.0 prefers OpenSSL 1.1.x to OpenSSL 1.0.x

.NET Core for Linux, which works across multiple Linux distributions, can support both OpenSSL 1.0.x and OpenSSL 1.1.x. .NET Core 2.1 and .NET Core 2.2 look for 1.0.x first, then fall back to 1.1.x; .NET Core 3.0 looks for 1.1.x first. This change was made to add support for new cryptographic standards.

This change may impact libraries or applications that do platform interop with the OpenSSL-specific interop types in .NET Core.

Change description

In .NET Core 2.2 and earlier versions, the runtime prefers loading OpenSSL 1.0.x over 1.1.x. This means that the IntPtr and SafeHandle types for interop with OpenSSL are used with libcrypto.so.1.0.0 / libcrypto.so.1.0 / libcrypto.so.10 by preference.

Starting with .NET Core 3.0, the runtime prefers loading OpenSSL 1.1.x over OpenSSL 1.0.x, so the IntPtr and SafeHandle types for interop with OpenSSL are used with libcrypto.so.1.1 / libcrypto.so.11 / libcrypto.so.1.1.0 / libcrypto.so.1.1.1 by preference. As a result, libraries and applications that interoperate with OpenSSL directly may have incompatible pointers with the .NET Core-exposed values when upgrading from .NET Core 2.1 or .NET Core 2.2.

Version introduced

3.0

Libraries and applications that do direct operations with OpenSSL need to be careful to ensure they are using the same version of OpenSSL as the .NET Core runtime.

All libraries or applications that use IntPtr or SafeHandle values from the .NET Core cryptographic types directly with OpenSSL should compare the version of the library they use with the new SafeEvpPKeyHandle.OpenSslVersion property to ensure the pointers are compatible.

Category

Cryptography

Affected APIs


CryptoStream.Dispose transforms final block only when writing

The CryptoStream.Dispose method, which is used to finish CryptoStream operations, no longer attempts to transform the final block when reading.

Change description

In previous .NET versions, if a user performed an incomplete read when using CryptoStream in Read mode, the Dispose method could throw an exception (for example, when using AES with padding). The exception was thrown because the final block was attempted to be transformed but the data was incomplete.

In .NET Core 3.0 and later versions, Dispose no longer tries to transform the final block when reading, which allows for incomplete reads.

Reason for change

This change enables incomplete reads from the crypto stream when a network operation is canceled, without the need to catch an exception.

Version introduced

3.0

Most apps should not be affected by this change.

If your application previously caught an exception in case of an incomplete read, you can delete that catch block. If your app used transforming of the final block in hashing scenarios, you might need to ensure that the entire stream is read before it's disposed.

Category

Cryptography

Affected APIs


.NET Core 2.1

Boolean parameter of SignedCms.ComputeSignature is respected

In .NET Core, the Boolean silent parameter of the SignedCms.ComputeSignature(CmsSigner, Boolean) method is respected. A PIN prompt is not shown if this parameter is set to true.

Change description

In .NET Framework, the silent parameter of the SignedCms.ComputeSignature(CmsSigner, Boolean) method is ignored, and a PIN prompt is always shown if required by the provider. In .NET Core, the silent parameter is respected, and if set to true, a PIN prompt is never shown, even if it's required by the provider.

Support for CMS/PKCS #7 messages was introduced into .NET Core in version 2.1.

Version introduced

2.1

To ensure a PIN prompt appears if required, desktop applications should call SignedCms.ComputeSignature(CmsSigner, Boolean) and set the Boolean parameter to false. The resulting behavior is the same as on .NET Framework regardless of whether the silent context is disabled there.

Category

Cryptography

Affected APIs