New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OpenSslCryptographicException: error:03000098:digital envelope routines::invalid digest on CentOS Stream 9 #65874
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsDescriptionWhile building .NET 6 using .NET 6 in in CentOS Stream 9 (aka RHEL 9 in-development), I am running into
Full log is here: https://centos.softwarefactory-project.io/zuul/t/centos/build/b00c0fe1895c4e1487350108a41214da Could this be caused by Disable SHA1 signature creation and verification by default? Reproduction StepsIt seems like building runtime in a CentOS Stream 9 container should be enough to trigger the bug. Will test and update this step later. Expected behaviorI can build .NET itself. Actual behaviorI can't build .NET, because the compiler needs signing (via OpenSSL) to work. Regression?It's a regression somewhere. Most likely it's not .NET itself that's to blame, because this same source code (no changes) built a few days ago. Known WorkaroundsI am testing if an Configuration
Other informationNo response
|
Seems likely. It's trying to produce a strong name signature and that uses RSA-SHA1: |
Thanks for the pointer. I tried a build with a workaround to enable SHA1 and it seems to be working. It does seem to be down to SHA1 signature creation being disabled. How difficult would be be to switch to something slightly better for a future release of .NET? I have no knowledge of Windows, but I found a blog post that suggests SHA256 and SHA512 are also supported for signing? |
My understanding is that this RSA-SHA1 is being used for .NET's strong naming functionality, not Windows Authenticode. The Authenticode signatures are likely already SHA256. I don't know what it would take to move the ecosystem past RSA-SHA1 for the purpose that it is being used. Perhaps @bartonjs or @GrabYourPitchforks know, directly or indirectly. |
For what it's worth, this breaks any .NET project that uses strong name signing, not just building .NET itself. To reproduce, create a new C# project. Add this somewhere:
Grab a strong name key. You can use It will fail to build with the same error. |
That is correct for this scenario / stack trace |
ECMA-335 only describes RSA(SSA)-SHA1(-PKCS1v1_5):
There's a concept of "extended strong name", which is a SHA256 countersignature on the SHA-1 signature (similar to how Authenticode bootstrapped SHA256); but I don't think that there's a Strong Name scenario that avoids SHA-1. While Strong Name isn't important for .NET Core / .NET 5+, I can imagine RHEL users building components (such as the Roslyn C# compiler) that need to support Strong Name for .NET Framework-based customers (either a .NET Standard library or even building directly for a .NET Framework TFM). I don't know how |
Additionally, I did a quick search to see if there are other things in the |
I reported this to the roslyn folks directly as well, at dotnet/roslyn#59880, particularly because I am not sure if roslyn's usage of SHA1 qualifies as "security" or not. |
@omajid I responded to the issue in dotnet/roslyn. In summary though this isn't a decision the roslyn compiler is making. The .NET platform requires SHA-1 be used for strong naming. The roslyn compiler, and every other .NET compiler that supports strong naming, is simply implementing the specification here. There are no other options besides SHA-1 unless the underlying format is changed. |
Thanks, @jaredpar
What should the platform do when (RSA+)SHA1 is not available on the OS (because of policies like FIPS, or general system hardening)? Should it hard-fail like it does now? Should it have a separate this-is-not-security implementation of RSA+SHA1? Is there a way to tell OpenSSL "we want to use RSA+SHA1, but not for security, we promise"?
Given how SHA1 is being phased out everywhere, what would it take to update the underlying format? |
I slept over this one, and I wonder if this will allow using RSA+SHA1 even in contexts where the OS really doesn't want it to be used, eg for TLS when downloading nuget packages (that happens as part of dotnet build, right?). And it wont help when FIPS enablement permanently disables RSA+SHA1. |
Correct me if I am wrong, but this is a patch on top of OpenSSL developed by RedHat, not something that is in OpenSSL upstream, correct? It would then be up to RedHat to implement a work around, if one needs to be provided. Based on the changeset, it looks like developers can:
Even if RedHat provided an option for "pinky swear it's not for security", it would require the runtime detect this custom API from RedHat, and somehow expose this option to roslyn and fsharp compilers. Alternatively, the roslyn and fsharp compilers could consider using a private implementation, say one written in managed code, of RSA and not use OpenSSL at all. I don't know if that is at all feasible or not. |
I don't think having a private implementation in Roslyn / F# is the right answer. That means every compiler team has to own their own copy of SHA-1, we all have to individually justify it to the security bugs that will come every 6 months (even justifying the enum value of SHA1 is painful). It also means 3rd party .NET compilers have to do the same. If we go the private implementation route I think the best approach is put into the
@jkotas and @davidwrighton are the best to answer that question authoritatively. But my understanding is that the answer is a lot. Consider that it's not just updating for .NET Core. In order for the update to solve this problem we also need to update it for .NET Framework. Further it probably needs to be updated for all versions of .NET Framework that |
Personally, I think it's one of those "there's not enough money on the planet" problems. My understanding is that the main reason that Roslyn still has Strong Name support is for .NET Framework compatibility. .NET Framework can't support anything other than RSASSA-SHA1-PKCS1-v1_5 without first making the change (which seems obvious... but seems against our current level of support for it) and then waiting for it to be deployed pretty much everywhere in the world (beautiful in-place upgrade SxS protocol delays)... and then could finally start using it. Oh, and it may or may not require first adding a revision to ECMA-335 (not sure if that spec lags or precedes implementation).
It's certainly... possible. But, personally, I think that the answer is "operating environments that do not allow RSA+SHA1 can not be used with scenarios that need to produce assemblies with strong names". Further information over time could, of course, change our position here.
In Windows, at least, FIPSyness has never blocked algorithms. The OS team's view is that the application can make the decision as to whether their attempt to use a non-FIPS-approved algorithm is valid. As far as I understand things, FIPS only applies when cryptography is used for a security decision... e.g. MD5 is not FIPS-Approved, but you don't lose government contracts for using the Content-MD5 HTTP header, since it's not security. .NET's position is that strong name is not a security feature... ergo, no FIPS implication. I'm personally not sure what OpenSSL 3's FIPS module is going to do here (fall back to default/legacy if you ask for non-FIPS modes of operation, or fail).
Actually, it'd be their own copy of RSA (specifically, RSASSA-PKCS1-v1_5 signing)... the SHA-1 by itself works fine. |
Prefer we stop referring to this as a Roslyn problem because it keeps leading to Roslyn based solutions, Roslyn issues, etc ... This is a .NET Compiler problem. It impacts F# just as much as Roslyn.
Gotcha |
I agree. If you need to produce assembly that is fully compatible with .NET Framework, you need to have RSA1+SHA1. If you do not need it to be fully compatible with .NET Framework, use public signing. Note that the public signed binaries still work on .NET Framework in most scenarios. Maybe the resolution for this is to change the defaults somewhere? Public sign by default, require opt-in into full signing? |
I think changing the default for .NET Core target frameworks is reasonable. It should not have any impact. For So say for example in .NET 7 we made the call to do public sign by default and force people who want real sign to opt into it. That seems like a reasonable stance to take. Problem is how do we communicate this to customers? Worry we could quickly end up in a place where people upgrade to .NET 7, everything passes tests but fails in deployment because that is the place which was most likely to rely on strong named binaries. |
Agree. I do not think it is worth the pain to be changing default for these. If you need to target legacy systems, you need to have legacy build machine configuration. |
Thanks, everyone for the discussion. For now, I see two paths forward for users, and no action needed on the .NET side:
Is there anything else that needs to be done? Shall I close this issue? Is this worth documenting somewhere? |
I'm not sure where/what we'd document. From the RSA perspective, it only applies when on Fedora/CentOS/RHEL when using RSAOpenSsl or RSA.Create() (a custom RSA implementation, such as RSAKeyVault, wouldn't necessarily have the problem), and at best we'd just link to the RedHat crypto docs for it. (From an API perspective, it's covered under the blanket docs of "if something goes wrong: CryptographicException"... and this is just "the underlying provider doesn't support the hash algorithm". I think RSACryptoServiceProvider -might- still support RSA+MD4, but RSAOpenSsl doesn't). From the assembly strong name perspective, we already recommend public sign for .NET Core projects, and don't know where in that doc stream anyone would find "if building for .NET Framework or .NET Standard (for .NET Framework) from [a RedHat-influenced distro], things get complicated". So, I'm not opposed to documentation, just can't think of where it'd go and how to explain the problem or solution in an approachable manner 😄. |
I'm curious which part of the .NET 6 build requires real signing (the log file linked in the original post isn't available anymore). Presumably we should still fix that one at least to use public sign? |
Any library that can encounter a strongname check on .NET Framework probably needs to be strong name signed, so that's potentially anything in the project tree that compiles for .NET Framework or .NET Standard (and excluding anything that compiles against a .NET Core/5+ TFM). But that's really only true for an official distribution channel that can realistically end up on .NET Framework where it might encounter an SN check. I don't think there's anything that builds out of The main SN-relevant scenario on .NET Framework is installing an assembly in the GAC. Apps using local copies of DLLs from NuGet or whatever aren't SN-relevant (OK, I think there's a regkey to make .NET Framework SN-check all assembly loads, but it's almost definitely not the default). So "we" (Microsoft builds) have to do real-signing (particularly for assemblies that have in the past), but it shouldn't come up much for anyone else. |
I am hitting that in source-build-reference-packages:
|
We almost certainly have tests that would hit RSA+PKCS1+SHA1 signature condition, such as this one. Line 508 in 91da4ac
Can you confirm if those tests ran? |
In the xml results, I see: <test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"MD5\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.0071895" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"SHA1\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.0068557" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"SHA256\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.0072911" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"MD5\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.0436004" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"SHA1\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.0420614" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"SHA256\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.042372" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"SHA384\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.043085" result="Pass" />
<test name="System.Security.Cryptography.Rsa.Tests.SignVerify_Array.SignAndVerify_Roundtrip(hashAlgorithm: \"SHA512\", rsaParameters: System.Security.Cryptography.RSAParameters)" type="System.Security.Cryptography.Rsa.Tests.SignVerify_Array" method="SignAndVerify_Roundtrip" time="0.0424903" result="Pass" /> 😕 |
What happens when you use the url with If they are happy with the certificate, .NET should be made less strict, and accept it certificate too. |
They do not appear to, no. We explicitly enable it here:
I think the reason why this was added is because this is what Windows does. Windows checks the signature on self-signed certificates. So this was done so that X509 chain building is more consistent across platforms, as I understand it. @bartonjs may want to weigh in on this one when he returns. |
@bartonjs can we drop this strict mode? It causes .NET to reject certificates on RHEL9 which are accepted by system tools like |
No. It was added specifically because .NET on Windows was rejecting things that .NET on Linux wasn't. This is one of those places where I feel like ".NET is .NET" is the better compat answer. |
That breaks .NET on RHEL9. |
To be brutally honest: no, it doesn't. It reveals that RHEL9's change to OpenSSL is based on incorrect assumptions. RHEL9 broke experiences .NET on RHEL9, not this line of code. (RHEL made a change to this area, .NET didn't, ergo the active verb "break" can only be attributed to RHEL).
That's a reasonable statement, and I think I'm willing to accept a PR that makes it true. However, that same PR cannot let corrupted self-signed certificates that were provided via
|
Personally, I'd like .NET to be as close to validating certificates as the other tools on the system. Not more strict, and not less strict. @bartonjs or @vcsjones, can one of you look into making this change? I can take a shot at it, but being largely unfamiliar with the subject, it will take me more time to understand the code, and figure out what changes are needed to match these requirements. |
Fedora is also planning to disable SHA1 soon: https://fedoraproject.org/wiki/Changes/StrongCryptoSettings3Forewarning1 |
Right now I don't have much availability... I'm focused on promised feature work before our "feature complete" preview (my portions are already a bit at-risk). What's the urgency here / is it terrible if we don't fix it up before .NET 8? Off the top of my head, what the PR needs to do, aside from make sure we have tests that cover the truth table above, is at/around Line 693 in 9c34a58
That way we're describing under what situations we don't report the error, rather than trying to figure out the conditions where we need to circle back and decide if we need to perform the check. (It may make more sense in BuildElements, since there the difference between "last" and "last error" stand out) |
We'd like this fixed before users are adopting .NET 6 on RHEL 9. Based on @vcsjones's input, I understand that as a workaround: we can source-build with We prefer an upstream fix over a workaround. |
I will make an attempt but time box it. If it turns out to be more work than expected I may need to defer (my time spent in this repo is purely voluntary). |
Thanks. Even if you can't finish it, your effort will give me a head start. |
@vcsjones I assume you didn't find time for this. I'll give it a shot next week. |
There are still two on-going tasks related to this:
|
Description
While building .NET 6 using .NET 6 in in CentOS Stream 9 (aka RHEL 9 in-development), I am running into
error : Unhandled exception. Interop+Crypto+OpenSslCryptographicException: error:03000098:digital envelope routines::invalid digest
Full log is here: https://centos.softwarefactory-project.io/zuul/t/centos/build/b00c0fe1895c4e1487350108a41214da
Could this be caused by Disable SHA1 signature creation and verification by default?
Reproduction Steps
It seems like building runtime in a CentOS Stream 9 container should be enough to trigger the bug. Will test and update this step later.
Expected behavior
I can build .NET itself.
Actual behavior
I can't build .NET, because the compiler needs signing (via OpenSSL) to work.
Regression?
It's a regression somewhere. Most likely it's not .NET itself that's to blame, because this same source code (no changes) built a few days ago.
Known Workarounds
I am testing if an
export OPENSSL_ENABLE_SHA1_SIGNATURES=1
will work around the issue.Edit: Confirmed. It does make the build move past this particular error.
Configuration
Other information
No response
The text was updated successfully, but these errors were encountered: