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)
}
Running:
5.4.0-52-generic #57-Ubuntu SMP Thu Oct 15 10:57:00 UTC 2020 x86_64 x86_64 x86_64)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.
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
Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @jeffhandley 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 The key file was originally generated using the following commands by accepting all defaults (just Enter, Enter, Enter...): ...which is then added to the Identity Server in the Running: 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. Here's the console output of the exception:
See info in area-owners.md if you want to be subscribed.
Issue meta data
Issue content:
### Description
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.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
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
5.4.0-52-generic #57-Ubuntu SMP Thu Oct 15 10:57:00 UTC 2020 x86_64 x86_64 x86_64)Regression?
Other information
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
Issue author:
codeaid
Assignees:
-
Milestone:
-
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:
openssl pkcs12 \
-export \
-inkey .MyCertificate.key \
-in .MyCertificate.crt \
-out .MyCertificate.pfx
(Note the removal of -certfile parameter.) 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.
As for why this changed / regressed in .NET 5 I am not sure yet
Ah, this is because UnixPkcs12Reader didn't exist in .NET Core 3.1, it looks like the PKCS12 handling was still being done by native OpenSSL. I confused myself by thinking the native to managed change happened between 2.1 and 3.1.
@bartonjs it looks like the managed implemented explicitly only allows a key to be used once. Once it's used, it's marked as null to make the key ineligible for use again. The behavior seems intentional then, but it diverges from what OpenSSL appears to allow.
https://github.com/dotnet/runtime/blob/1923bba394c31bc2d4f62a948eac8dbe64339ab2/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs#L553-L559
Part of the purpose of that change was to be consistent across the three OS families. Presumably that means that Windows would fail to read a PFX in that state.
(Note the removal of
-certfileparameter.) This will work in .NET Core 3.1 and .NET 5.
Can confirm that this does indeed fix the issue. Regenerated the certificate file using the same commands minus the -certfile parameter and it now loads the certificate without any problems.
@bartonjs
Presumably that means that Windows would fail to read a PFX in that state.
Hm, this particular example appears to work in Windows, at least on Windows 10 2004.
This test seems to support that Windows doesn't like two certificates and one key:
But something more nuanced seems to be going on, I shall dig in.
@codeaid
Can confirm that this does indeed fix the issue.
Great! Does this mean this particular issue is no longer blocking you from moving to .NET 5?
@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.
We have same issue with password protected ceritificate on our linux azure pipeline but it works on windows dev environment.
@mano-cz Are you getting the exact same error? In particular, is this the exception message of the inner exception?
System.Security.Cryptography.CryptographicException: A certificate referenced a private key which was already referenced, or could not be loaded.
Does the work around work for you in the meantime?
@mano-cz Are you getting the exact same error? In particular, is this the exception message of the inner exception?
System.Security.Cryptography.CryptographicException: A certificate referenced a private key which was already referenced, or could not be loaded.
Does the work around work for you in the meantime?
Yes the message is same.
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. Stack Trace: at Internal.Cryptography.Pal.UnixPkcs12Reader.Decrypt(SafePasswordHandle password) at Internal.Cryptography.Pal.PkcsFormatReader.TryReadPkcs12(OpenSslPkcs12Reader pfx, SafePasswordHandle password, Boolean single, ICertificatePal& readPal, List1& readCerts)
at Internal.Cryptography.Pal.PkcsFormatReader.TryReadPkcs12(ReadOnlySpan1 rawData, SafePasswordHandle password, Boolean single, ICertificatePal& readPal, List1& readCerts, Exception& openSslException)
at Internal.Cryptography.Pal.OpenSslX509CertificateReader.FromBlob(ReadOnlySpan1 rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags) at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData, String password) ...<our code>... --CryptographicException 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(ReadOnlySpan1 password, ReadOnlyMemory1 authSafeContents) at Internal.Cryptography.Pal.UnixPkcs12Reader.VerifyAndDecrypt(ReadOnlySpan1 password, ReadOnlyMemory1 authSafeContents) at Internal.Cryptography.Pal.UnixPkcs12Reader.Decrypt(SafePasswordHandle password)
This is a "unit test" with saved certificate, core3.1 was ok. But our customers may have issues with their certificates. We try the workaround tomorrow.
This test seems to support that Windows doesn't like two certificates and one key:
Okay, I dug a bit more at this and I'm less clear what is going on, but it seems that Windows permits this as well, unless you are importing with X509KeyStorageFlags.EphemeralKeySet, then it fails?
e.g. this will fail:
```c#
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using RSA rsa = RSA.Create();
CertificateRequest req = new CertificateRequest("CN=blah", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
using X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now);
Pkcs12Builder builder = new Pkcs12Builder();
Pkcs12SafeContents keyContents = new Pkcs12SafeContents();
Pkcs12SafeContents certContents = new Pkcs12SafeContents();
string password = "YabbaDabbaDoo";
PbeParameters pbeParameters = new PbeParameters(PbeEncryptionAlgorithm.TripleDes3KeyPkcs12, HashAlgorithmName.SHA1, 2000);
Pkcs12SafeBag keyBag = keyContents.AddShroudedKey(rsa, password, pbeParameters);
Pkcs12SafeBag certBag = certContents.AddCertificate(cert);
Pkcs12SafeBag certBag2 = certContents.AddCertificate(cert);
builder.AddSafeContentsEncrypted(keyContents, password, pbeParameters);
builder.AddSafeContentsUnencrypted(certContents);
builder.SealWithMac(password, HashAlgorithmName.SHA1, 2000);
byte[] pfxBytes = builder.Encode();
X509KeyStorageFlags flags = X509KeyStorageFlags.EphemeralKeySet;
using X509Certificate2 reOpened = new X509Certificate2(pfxBytes, password, flags);
```
but change the flags to DefaultKeySet, and it won't fail.
@bartonjs any clues what might be the cause of that behavior?
I don't know that the CertTwice_KeyOnce is passing (failing to open the PFX) for the reason we think it is.
Okay, I dug a bit more at this and I'm less clear what is going on, but it seems that Windows permits this as well, unless you are importing with X509KeyStorageFlags.EphemeralKeySet, then it fails?
When the import isn't ephemeral, Windows imports the keys to persisted files, and adds properties to specify the identifiers for what persisted files were used. There's nothing lossy there, so they don't have any issue with double-assignment and don't detect it.
When the import is ephemeral they've processed the key material into objects, and then assign the object pointer as a cert property. Since CNG keys don't support up/down-ref they can't let it be multiply assigned without compromising system reliability, so a double assignment gets detected and fails.
So if Windows isn't consistent with Windows, the question is which mode we want to emulate... the most restrictive (so these problems are surfaced more readily), the most common (least pain), the most flexible (which causes problems to remain hidden until moving to a more restrictive platform/mode). Since the tests were written with Ephemeral to avoid machine side effects, and it hadn't occurred to me that Ephemeral and !Ephemeral would have different behavior, I though I was just writing "match Windows", which was an easily justified position. This morning, I'm not sure what change I'd want to make here (including none).
the question is which mode we want to emulate
Can we match the current behavior? Introduce a bool somewhere in the UnixPkcs12Reader like allowKeyReuse, and plumb it through as !storageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)? I haven't looked too closely at all of the PKCS12 paths to see if that is a bad idea or not.
Can we match the current behavior? ... and plumb it through as !storageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet)
That's.... reasonable... the only real oddity there is that on Linux that bit currently has no purpose, since the only time keys are written down is when a cert-with-key is added to a persisted store. But it would increase the consistency.
The default mode would need to make sure that it was safe from side effects, probably the easiest answer is to use distinct native keys for each time it mattered.
on Linux that bit currently has no purpose,
Of course, one could argue that I should have remembered the bit was set and then conveniently forgot to write down the private key on persist-to-store, so that it was more consistent with Windows, but that wasn't a thing I realized until a few releases later.
The default mode would need to make sure that it was safe from side effects, probably the easiest answer is to use distinct native keys for each time it mattered.
Hm, I don't follow this. Can you expand on that?
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 in
```C#
X509Certificate2 cert1 = ...;
X509Certificate2 cert2 = ...;
RSAOpenSsl typedKey = (RSAOpenSsl)cert1.GetRSAPrivateKey();
// do something with typedKey.DuplicateKeyHandle() to get an EVP_PKEY, then grab the RSA* out of that, then import into it.
// hey, look, cert2.GetRSAPrivateKey() now produces a corrupt private key...
// unless it had two distinct private key entries in the PFX (different key ids)
Maybe that's so far off the beaten path it doesn't matter. I'm pretty sure that
```C#
RSA rsa = cert.GetRSAPrivateKey();
rsa.ImportSomething(...);
doesn't corrupt the value for the next call, because the Import routines end up building a new RSA* (or EC_KEY*, etc) then changing the pointer... rather than updating the existing RSA* 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.
So, theoretically, allowing dual-bind could result in
Hm, seems like CNG has that behavior. It looks like a key used across multiple certificates uses the same persisted CNG key, so supposedly someone could make a modification to the underlying CNG key from one certificate that might be visible to the other. Continuing with the example above, I end up with the same CNG persisted key:
```c#
X509KeyStorageFlags flags = X509KeyStorageFlags.DefaultKeySet;
X509Certificate2Collection certs = new X509Certificate2Collection();
certs.Import(pfxBytes, password, flags);
using X509Certificate2 cert1 = certs[0];
using X509Certificate2 cert2 = certs[1];
var key1 = ((RSACng)cert1.GetRSAPrivateKey()).Key;
var key2 = ((RSACng)cert2.GetRSAPrivateKey()).Key;
Console.WriteLine(key1.UniqueName);
Console.WriteLine(key2.UniqueName);
Console.WriteLine(key1.UniqueName == key2.UniqueName); // Prints 'true'
```
For me, I end up with the exact same key identifiers. Seems like if someone grabbed the key handle from cert1 they could probably do something that affects the behavior of cert2.
So maybe if we are okay with Windows doing it if you go far off the beaten path, doing that on Linux is probably okay, too, as long as this statement is true:
doesn't corrupt the value for the next call, because the Import routines end up building a new RSA* (or EC_KEY, etc) then changing the pointer... rather than updating the existing RSA value
As long as a caller cannot mutate the underlying key without resorting to key handles (like the CNG case).
Most helpful comment
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 in```C#
X509Certificate2 cert1 = ...;
X509Certificate2 cert2 = ...;
RSAOpenSsl typedKey = (RSAOpenSsl)cert1.GetRSAPrivateKey();
// do something with typedKey.DuplicateKeyHandle() to get an EVP_PKEY, then grab the RSA* out of that, then import into it.
// hey, look, cert2.GetRSAPrivateKey() now produces a corrupt private key...
// unless it had two distinct private key entries in the PFX (different key ids)
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.