runtime: "The certificate data cannot be read with the provided password, the password may be incorrect." in `X509Certificate2` after updating to .NET 5.0
Description
I upgraded my application earlier today to .NET 5.0 and everything worked well apart from one thing - I’m getting the error specified in the title of this issue as soon I start the project up (through dotnet run) and get to the point of instantiating an X509 certificate for use with IdentityServer4. The certificate file is the same one I had before the upgrade and hasn’t been changed for weeks.
The key file was originally generated using the following commands by accepting all defaults (just Enter, Enter, Enter…):
openssl req \
-x509 \
-newkey rsa:4096 \
-sha256 \
-nodes \
-days 3650 \
-keyout .MyCertificate.key \
-out .MyCertificate.crt
openssl pkcs12 \
-export \
-in .MyCertificate.crt \
-inkey .MyCertificate.key \
-certfile .MyCertificate.crt \
-out .MyCertificate.pfx
rm .MyCertificate.crt .MyCertificate.key
…which is then added to the Identity Server in the Startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
IIdentityServerBuilder builder = services.AddIdentityServer(options => ...);
string certPath = Path.Combine(Environment.ContentRootPath, "Certificates", "MyCertificate.pfx");
// This is the line that fails
builder.AddSigningCredential(new X509Certificate2(certPath));
// Changing it to just the instantiation (without calling `AddSigningCredential`) results in the same exception:
// new X509Certificate2(certPath)
}
Configuration
Running:
- .NET 5.0
- Ubuntu 20.04 (
5.4.0-52-generic #57-Ubuntu SMP Thu Oct 15 10:57:00 UTC 2020 x86_64 x86_64 x86_64)
Regression?
This worked in 3.1 with no issues even earlier today before the upgrade. Tested a moment ago by reverting to 3.1 just to confirm.
Other information
Here’s the console output of the exception:
Host terminated unexpectedly
System.Security.Cryptography.CryptographicException: The certificate data cannot be read with the provided password, the password may be incorrect.
---> System.Security.Cryptography.CryptographicException: A certificate referenced a private key which was already referenced, or could not be loaded.
at Internal.Cryptography.Pal.UnixPkcs12Reader.BuildCertsWithKeys(CertBagAsn[] certBags, AttributeAsn[][] certBagAttrs, CertAndKey[] certs, Int32 certBagIdx, SafeBagAsn[] keyBags, RentedSubjectPublicKeyInfo[] publicKeyInfos, AsymmetricAlgorithm[] keys, Int32 keyBagIdx)
at Internal.Cryptography.Pal.UnixPkcs12Reader.Decrypt(ReadOnlySpan`1 password, ReadOnlyMemory`1 authSafeContents)
at Internal.Cryptography.Pal.UnixPkcs12Reader.VerifyAndDecrypt(ReadOnlySpan`1 password, ReadOnlyMemory`1 authSafeContents)
at Internal.Cryptography.Pal.UnixPkcs12Reader.Decrypt(SafePasswordHandle password)
--- End of inner exception stack trace ---
at Internal.Cryptography.Pal.UnixPkcs12Reader.Decrypt(SafePasswordHandle password)
at Internal.Cryptography.Pal.PkcsFormatReader.TryReadPkcs12(OpenSslPkcs12Reader pfx, SafePasswordHandle password, Boolean single, ICertificatePal& readPal, List`1& readCerts)
at Internal.Cryptography.Pal.PkcsFormatReader.TryReadPkcs12(ReadOnlySpan`1 rawData, SafePasswordHandle password, Boolean single, ICertificatePal& readPal, List`1& readCerts, Exception& openSslException)
at Internal.Cryptography.Pal.OpenSslX509CertificateReader.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
at MyProject.Startup.ConfigureIdentityServer(IServiceCollection services) in /var/www/MyProject/Startup.cs:line 197
at MyProject.Startup.ConfigureServices(IServiceCollection services) in /var/www/MyProject/Startup.cs:line 53
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services, Object instance)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass13_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at MyProject.Program.Main(String[] args) in /var/www/MyProject/Program.cs:line 23
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 10
- Comments: 24 (16 by maintainers)
This is because there are two (identical) certificates in the PKCS12 bag that are referencing the same key. If you change the OpenSSL command to not include the certificate twice, then the issue no longer occurs:
(Note the removal of
-certfileparameter.) This will work in .NET Core 3.1 and .NET 5.As for why this changed / regressed in .NET 5 I am not sure yet, but I don’t think it was an intentional change, though @bartonjs is probably the right person to say for certain.
https://source.dot.net/#System.Security.Cryptography.X509Certificates/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs,314
The implementation processes all keys into AsymmetricAlgorithm objects, then later matches them up onto CertAndKey.Key. Later https://source.dot.net/#System.Security.Cryptography.X509Certificates/Internal/Cryptography/Pal.Unix/PkcsFormatReader.cs,327 does the cert+key binding for an X509Certificate2 instance (and macOS does macOS things).
Currently that binding … I think … keeps the same
RSA*value but in different EVP_PKEY wrappers. So, theoretically, allowing dual-bind could result inMaybe that’s so far off the beaten path it doesn’t matter. I’m pretty sure that
doesn’t corrupt the value for the next call, because the Import routines end up building a new
RSA*(orEC_KEY*, etc) then changing the pointer… rather than updating the existingRSA*value. So it … might … be far enough away that it doesn’t matter.None of this came up on previous .NET (Core) on Linux, because the “KISS” loader routine for PKCS#12 emitted a cert, a key, and a list of “other certs” – that routine didn’t need to handle this case because it fell outside its representable range. Supporting multiple private key load is one of the reasons that we stopped using that function (uh… pkcs12_decode?) and rolled our own loader.
@vcsjones It does! I don’t have too many dependencies in my project but they all upgraded without any issues apart from
Pomelo.EntityFrameworkCore.MySql, for which I need to use an alpha version until they release the final one.Thanks all for the great work you’re doing! Really appreciate that. Makes using everything .NET a joy, especially coming from other languages.
I am hitting this error as well. This one-line program:
successfully loads the certificate on Windows, but on Linux (specifically,
dotnet/sdk:5.0-focalrunning in WSL2) I get the exception this issue is about. I have made a minimal reproduction here: https://github.com/mark-raymond/dotnet-runtime-issue44535 using a throwaway cert+key.Note: In general, I will not be in control of the certificate, so the workaround in certificate generation won’t work for me.