Roslyn fails to work when RSA+SHA1 is not available
See original GitHub issueTLDR: .NET 6 + strong naming is now broken on RHEL 9
The SHA-1 cryptographic algorithm has had a number of attacks against it. Some researchers have claimed to broken it completely. Microsoft itself has stopped using it in a number of scenarios.
Now, a number of Linux distributions are starting to disable support for SHA1.
RHEL 9 has disabled it for a number of use cases: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9-beta/html/considerations_in_adopting_rhel_9/assembly_security_considerations-in-adopting-rhel-9#ref_considerations-security-crypto_changes-to-security and will probably disable them for more use-cases:
In RHEL 9, SHA-1 usage is restricted in the DEFAULT system-wide cryptographic policy. With the exception of HMAC and DNSSec usage, SHA-1 is no longer allowed in TLS, DTLS, SSH, IKEv2 and Kerberos protocols. Individual applications not controlled by crypto policies are also moving away from using SHA-1 hashes in RHEL 9.
Unfortunately, Rolsyn still uses SHA1 (and only SHA1) for strong name signing: https://github.com/dotnet/roslyn/blob/315c2e149ba7889b0937d872274c33fcbfe9af5f/src/Compilers/Core/Portable/PEWriter/SigningUtilities.cs#L22
That means .NET 6 is now broken on RHEL 9.
Building on RHEL 9 leads to a Unhandled exception. Interop+Crypto+OpenSslCryptographicException: error:03000098:digital envelope routines::invalid digest exception when trying to use SHA1.
I am looking into some workarounds to re-enable SHA1 this, but it does look like in certain security configuration (eg, FIPS 140) SHA1 is simply banned and the underlying crypto library on Linux (OpenSSL) will not allow SHA1 to be used. And no matter what’s done, it does make out of the box default user-experience for .NET on RHEL 9 pretty bad if anything involves strong-naming.
I have a couple of questions:
-
Is strong-name signing use of SHA1 in a security context or not?
https://docs.microsoft.com/en-us/dotnet/standard/assembly/create-use-strong-named says:
Do not rely on strong names for security. They provide a unique identity only.
But isn’t proving identity part of security (Authentication)?
-
If strong naming is not security relevant, would it be possible to add a fallback implementation if
System.Security.Cryptography.IncrementalHashdoesn’t support SHA1? -
Are there any plans to move away from SHA1?
Even projects that were completely tied to SHA1, such as git, are migrating off of SHA1. .NET/Roslyn is still tied to it, as far as I can tell. Are there any plans (short term or long term) to remove the use of SHA1 here?
Version Used:
.NET 6
Steps to Reproduce:
- On a CentOS Stream 9 container image (
quay.io/centos/centos:stream9) with a recent enough version of OpenSSL 3.0 (openssl-libs-3.0.1-8or newer) - Using .NET 6, use any feature that requires strong-naming (eg, signing or verifying assemblies)
Expected Behavior:
Things work
Actual Behavior:
Unhandled exception. Interop+Crypto+OpenSslCryptographicException: error:03000098:digital envelope routines::invalid digest
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (5 by maintainers)
Top Related StackOverflow Question
Just so to be clear, the problem isn’t SHA1.
IncrementalHashoverHashAlgorithmName.SHA1and theSHA1classes continue to work in CentOS Stream 9. The issue lies in RSA over SHA1.The
IncrementalHashstep is working fine, what is failing is the RSASignHashwhen given SHA1:https://github.com/dotnet/roslyn/blob/315c2e149ba7889b0937d872274c33fcbfe9af5f/src/Compilers/Core/Portable/PEWriter/SigningUtilities.cs#L27
This is why git is unaffected by this change. Git does not RSA sign anything, it just uses the hashing algorithm, so it won’t be broken by this change in CentOS Stream 9.
@tmds
That is correct.
There are a number of different ways you can ask the compiler to fully sign a binary. Any of those would hit this bug. Hard to enumerate them all because some involve code. It’s much simpler to say that if you want identity but don’t want full signing then use the
<PublicSign>true</PublicSign>. That will override the other ones.