Skip to content

Update DnsNameList for X509Certificate2 to use X509SubjectAlternativeNameExtension.EnumerateDnsNames Method #24714

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

Conversation

ArmaanMcleod
Copy link
Contributor

@ArmaanMcleod ArmaanMcleod commented Dec 28, 2024

PR Summary

Updated DnsNameList for X509Certificate2 to use X509SubjectAlternativeNameExtension.EnumerateDnsNames method. This replaces the DNS Name= prefix + OID 2.5.29.17 checking code which is error prone when using different language clients.

Using the public X509SubjectAlternativeNameExtension (byte[] rawData, bool critical = false) constructor with cert.RawData did not work and resulted in CryptographicException exceptions. Instead, what was much more robust was just casting the extension to X509SubjectAlternativeNameExtension and then using the EnumerateDnsNames() method.

Also testing previous code with different culture/locale I could not get DNS name prefix to output in a different language, so that case is a bit difficult to reproduce. Although it seems like this is supposed to just work moving to the .NET EnumerateDnsNames() method. I am not even sure switching cultures is good enough to expose this problem and how it can even be validated in the tests.

PR Context

Fixes #24594

PR Checklist

@ArmaanMcleod ArmaanMcleod marked this pull request as ready for review December 28, 2024 07:31
@iSazonov iSazonov added the CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log label Dec 28, 2024
@ArmaanMcleod
Copy link
Contributor Author

ArmaanMcleod commented Dec 28, 2024

@MaurizioFocareta Did you want to test these changes using a non-english locale to see if using .NET runtime approach fixes your problem?

I'll keep looking to see if we can get the tests to verify it but for now we can probably do a manual test off this branch to see if it works for you 🙂.

Comment on lines +3351 to +3352
string parsedSubjectDistinguishedDnsName = cert.Subject.Substring(distinguishedNamePrefix.Length);
DnsNameRepresentation dnsName = GetDnsNameRepresentation(parsedSubjectDistinguishedDnsName);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iSazonov I wonder if this can also be replaced with https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x500distinguishedname.enumeraterelativedistinguishednames?view=net-9.0.

I don't think even distinguished name needs string parsing anymore.

Copy link
Contributor Author

@ArmaanMcleod ArmaanMcleod Dec 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually will park this, just realised this will include all distinguished names outside of CN=. dotnet runtime probably needs to expose something for just common name without too much extra work. Right now you'd need to still check the OID during enumeration which is not ideal and probably leads to more complex code in its current state.

dotnet/runtime#33914

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would end up being something like:

private const string CommonNameOid = "2.5.4.3";

// Extract DNS name from Subject distinguished name
foreach (X500RelativeDistinguishedName distinguishedName in cert.SubjectName.EnumerateRelativeDistinguishedNames())
{
   if (!distinguishedName.HasMultipleElements &&
        distinguishedName.GetSingleElementType().Value.Equals(CommonNameOid, StringComparison.OrdinalIgnoreCase))
    {
        DnsNameRepresentation dnsName = GetDnsNameRepresentation(distinguishedName.GetSingleElementValue());
        _dnsList.Add(dnsName);
    }
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change is not related the PR. Please revert.
If you think original code has an issue, please open new issue to discuss.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah so this was just referring to how distinguished names are handled. I have reverted that change and will open a new issue to discuss. This is resolved for this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see new commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I force pushed and removed the changes.

@ArmaanMcleod ArmaanMcleod force-pushed the x509Certificate2-enumerate-dns-names-fix branch from 442149a to 18a73cd Compare December 29, 2024 01:38
@iSazonov iSazonov merged commit 10d1785 into PowerShell:master Dec 29, 2024
38 checks passed
@ArmaanMcleod ArmaanMcleod deleted the x509Certificate2-enumerate-dns-names-fix branch December 29, 2024 10:25
@KalleOlaviNiemitalo
Copy link

Using the public X509SubjectAlternativeNameExtension (byte[] rawData, bool critical = false) constructor with cert.RawData did not work and resulted in CryptographicException exceptions.

That makes sense; X509Certificate2.RawData is the entire certificate including all extensions, but the byte[] rawData parameter of the X509SubjectAlternativeNameExtension constructor must be only the single extension.

Also testing previous code with different culture/locale I could not get DNS name prefix to output in a different language, so that case is a bit difficult to reproduce.

Because the prefix was added by unmanaged code in Windows, I expect it respects SetThreadPreferredUILanguages rather than CultureInfo.CurrentUICulture. The corresponding language pack for Windows might also have to be installed.

@ArmaanMcleod
Copy link
Contributor Author

Using the public X509SubjectAlternativeNameExtension (byte[] rawData, bool critical = false) constructor with cert.RawData did not work and resulted in CryptographicException exceptions.

That makes sense; X509Certificate2.RawData is the entire certificate including all extensions, but the byte[] rawData parameter of the X509SubjectAlternativeNameExtension constructor must be only the single extension.

Also testing previous code with different culture/locale I could not get DNS name prefix to output in a different language, so that case is a bit difficult to reproduce.

Because the prefix was added by unmanaged code in Windows, I expect it respects SetThreadPreferredUILanguages rather than CultureInfo.CurrentUICulture. The corresponding language pack for Windows might also have to be installed.

Thanks @KalleOlaviNiemitalo. That makes a lot of sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log
Projects
None yet
Development

Successfully merging this pull request may close these issues.

X509Certificate2 DNSNameList is not language independent, don't work on non-english clients
3 participants