Thanks to visit codestin.com
Credit goes to github.com

Skip to content

X509CertificateLoader can't load public key from pfx file without password #115137

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

Closed
jhudsoncedaron opened this issue Apr 29, 2025 · 6 comments
Closed

Comments

@jhudsoncedaron
Copy link

Description

This should work but doesn't.

Console.WriteLine(System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadPkcs12(System.IO.File.ReadAllBytes("test.pfx"),
			null, 0, new System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits { IgnorePrivateKeys = true, IgnoreEncryptedAuthSafes = true } ));

I haven't been able to actually remove the private key from the pfx file so I won't be uploading it; however this pfx file is unusual; on examining it on strings it's obvious the public key side is not encrypted.

You can generate one of these yourself: export a key from Windows certificate manager; uncheck enable certificate privacy.

$ strings test.pfx 
0J0)
o<H>
Gm&-
|*lT
Me('
tZLm1
]J}jc
DTah
!<>@
Dh>{
\~tj
:}\II
	localhost0
231121155232Z
281121000000Z0
	localhost0
d*'d2
|i| 
:080
	localhost0
}i~h
M`12
KDSK
090705
.S-1-5-21-2569210111-3594215139-1303219835-11740
T#Ou_
^Z)Va
HG}V
0K0/0
 NTn~
~}U|a>\

(Yes the certificate really is for localhost)

Reproduction Steps

  1. Export a pfx file without certificate privacy
  2. copy it to another machine
  3. try to read the public key using the code fragment above; from a discussion on the breaking change; this call is supposed to work

Expected behavior

Get X509Certificate2 class back without a private key

Actual behavior

throws

Regression?

Kind of

Known Workarounds

No response

Configuration

No response

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Apr 29, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@vcsjones
Copy link
Member

The presence of a SID string (.S-1-5-21-2569210111-3594215139-1303219835-11740) is interesting.

When you exported the certificate from certmgr, did you use the "Group or user names" option to protect the private key instead of a password?

@bartonjs probably remembers better than I do, but DPAPI protected PFX files have some oddities about them that require specific flags to open them.

@jhudsoncedaron
Copy link
Author

jhudsoncedaron commented Apr 29, 2025

@vcsjones : I checked and then unchecked the box, and then keyboard smashed a password. So maybe there's a bug in Windows where it exported it with a user-encrypted private key anyway; or maybe it just remembers the user who exported it. I would be surprised if it matters because I only want the public key anyway.

@krwq
Copy link
Member

krwq commented Apr 30, 2025

@bartonjs
Copy link
Member

bartonjs commented May 2, 2025

I checked and then unchecked the box, and then keyboard smashed a password.

The PFX produced by that still uses a password in two places... 1) the integrity MAC (which for the most part serves as "make sure the password is valid when we use it later") 2) the private keys are still encrypted (but the certs aren't)

Since the PFX has a MAC, and you've specified a password (as null), and we can't verify the MAC, we fail to load the file. We'd have to add an "IgnoreIntegrityCheck" parameter to the loader limits to support this from normal API.

But, since this sounds like advanced usage, you might instead just want to use the Pkcs12Info class.

Pkcs12Info info = Pkcs12Info.Decode(File.ReadAllBytes(path), out _);

foreach (Pkcs12SafeContents safeContents in info.AuthenticatedSafe)
{
    if (safeContents.ConfidentialityMode == Pkcs12ConfidentialityMode.None)
    {
        foreach (Pkcs12SafeBag safeBag in safeContents.GetBags())
        {
            if (safeBag is Pkcs12CertBag certBag)
            {
                if (certBag.IsX509Certificate)
                {
                    yield return certBag.GetCertificate();
                }
            }
        }
    }
}

That skips right over the authentication check, ignores all private keys, ignores all attributes, and skips any encrypted SafeContents belonging to the authsafe.

@jhudsoncedaron
Copy link
Author

jhudsoncedaron commented May 2, 2025

OK that works.

It's kind of embarrassing that I wasn't able to construct a test example of the actual use case. The production use case appears to allow a PFX here but it shouldn't have a private key at all. However I couldn't get any of my tools to generate one without a private key that really shouldn't be there.

I guess that's what we get for bad spec writing by whoever wrote this auth spec. It's a base64 encoded certificate, but it's not directly a PEM file. Too bad. Usually it's a base64 encoded CER file, but a base64 encoded PEM file or even a PFX file is also valid(!).

@bartonjs bartonjs closed this as completed May 2, 2025
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label May 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants