Open nmalowmicrosoft opened 1 month ago
Does your problem go away if you use your PFX with .NET 6 or .NET 8, as opposed to mono?
If it's really mono-only, then I think the issue belongs in a different repository. I know at one point Mono was trying to unify onto our libraries, but I don't know if that finished or if X509Certificate made the cut.
@bartonjs I just tried using .NET 8. The issue is still there
It generates a different invalid certificate than the one from Windows
Can you be more specific what "generates an invalid certificate" means here? Using the X509Certificate
constructor does not generate a certificate, it just loads whatever is in the PFX.
What about it is invalid? Do you get an error? Do you have an example that reproduces the problem?
There is no error thrown from the constructor. I got an error that the certificate with thumbprint XXXXX did not exist when trying to use the X509Certificate2 object. The first thing I noticed was the Thumbprint of the X509Certificate2 was different from the one I got when I ran it through Windows. Then when I looked at the entire X509Certificate2 object, I noticed there was no private key, the subject and issuer were different from Windows, the public key was different (basically the entire X509Certificate2 was different when I printed it out with certificate.ToString(true)) I didn't see anything in common between the X509Certificate2 object generated on Windows and the X509Certificate2 generated on Ubuntu:20.04.
Here is some example code to what I am doing. I printed out the s.Value and confirmed it was the same/correct between both systems. I then printed the hash of the privateKeyBytes and got the same value on both systems, so I know the error must be with the X509Certificate2 constructor.
public async Task
The first thing I noticed was the Thumbprint of the X509Certificate2 was different from the one I got when I ran it through Windows
The only way the thumbprint can be different is if they are two different certificates. The thumbprint is just a hash over the certificate. It is not parsed or otherwise "read" from the certificate.
the subject and issuer were different from Windows
There are basically two possibilities here.
I printed out the s.Value and confirmed it was the same/correct between both systems
For the first case, there is not actionable for us. For the second case, that would require some way to reproduce this.
What we would need is
string input = "";
byte[] inputBytes = Convert.FromBase64String(input);
var cert = new X509Certificate2(inputBytes, "<the password>");
Console.WriteLine(cert.Thumbprint);
If you can provide sample data where you can put something in input
and it result in the thumbprint being different between Windows and Linux, then we will have an actionable bug to fix. Until then we can't really speculate what is happening.
I am not sure how to safely provide the example input as it is generated in an Azure Keyvault. Do you have a sample input I can try from my end, maybe from one of your test cases? Or could you generate a certificate on your end https://learn.microsoft.com/en-us/azure/key-vault/certificates/certificate-scenarios
I generated a dummy cert and the thumbprints are the same in this example. However, the linux one is missing its Private Key field and the Windows one has a Private Key field code:
// Create a self-signed certificate
var ecdsa = ECDsa.Create(); // generate asymmetric key pair
var req = new CertificateRequest("cn=dummy", ecdsa, HashAlgorithmName.SHA256);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
// Export the certificate as a PFX file
var pfxBytes = cert.Export(X509ContentType.Pfx);
// Convert the PFX file to a Base64 string
var input = Convert.ToBase64String(pfxBytes);
// Print the Base64 string
Console.WriteLine(input);
linux one is missing its Private Key field and the Windows one has a Private Key field
Can you describe how you are making this observation? When I use the that code on Linux (Ubuntu 24.04)
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
// Create a self-signed certificate
var ecdsa = ECDsa.Create(); // generate asymmetric key pair
var req = new CertificateRequest("cn=dummy", ecdsa, HashAlgorithmName.SHA256);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
// Export the certificate as a PFX file
var pfxBytes = cert.Export(X509ContentType.Pfx);
// Convert the PFX file to a Base64 string
var input = Convert.ToBase64String(pfxBytes);
// Print the Base64 string
Console.WriteLine(input);
var loaded = new X509Certificate2(pfxBytes);
Console.WriteLine(loaded.HasPrivateKey);
This prints "true" for me on Linux.
I printed the output of loaded.ToString(true) and I get [Private Key]
.
Thanks! The presence of [Private Key]
is the indicator there is a private key. The text under the section just differ between Windows and Linux.
On Windows, it's going to look something like this:
[Private Key]
Key Store: User
Provider Name: Microsoft Software Key Storage Provider
Provider type: 0
Key Spec: 0
Key Container Name: {00000000-0000-0000-0000-000000000000}
Those values, Key Store, Provider Name, Provider type... etc. they are all Windows only concepts. There is no such thing as a Key Container Name on Linux. .NET does this on Windows because that is what .NET Framework did many many years ago.
So your certificate does have a private key. It just doesn't show you that bit of Windows information about the key because you aren't on Windows.
The better way to determine if a certificate has a private key or not is to use the HasPrivateKey
property.
I found the issue for my specific certificate. Linux is using the root certificate instead of the leaf certificate. Windows is getting the leaf cert. My PFX file contains a root cert, intermediary cert, and leaf cert
I found the issue for my specific certificate. Linux is using the root certificate instead of the leaf certificate. Windows is getting the leaf cert. My PFX file contains a root cert, intermediary cert, and leaf cert
That is some good info to go on. Let me see if I can reproduce it. Was the PFX generated by AKV or something else?
AKV
Description
The constructor "new X509Certificate2(privateKeyBytes, password)" is generating a different and incorrect certificate when run on ubuntu:20.04 with mono. It is working fine on Windows 11 with dotnet. I have verified the privateKeyBytes is the same on both systems and the password is the same.
Reproduction Steps
Run the line "new X509Certificate2(privateKeyBytes, password)" from a kubernetes pod with ubuntu 20.04 using mono.
Expected behavior
Generates a certificate that is the same as in Windows
Actual behavior
It generates a different invalid certificate than the one from Windows
Regression?
No response
Known Workarounds
No response
Configuration
--Framework:.NETFramework,Version=v4.8.1 --Platform:x64 ubuntu:20.04
The ubuntu:20.04 runs the tests with mono MyTest.exe. The windows successfully runs the tests with dotnet test MyTest.dll and vstest.console.exe
Other information
No response