-
Notifications
You must be signed in to change notification settings - Fork 4.9k
NetFX compatibility fixes for X500DistinguishedName. #30572
Conversation
@bartonjs, can you review please? |
@@ -51,7 +51,7 @@ internal static partial class X500NameEncoder | |||
{ | |||
dnSeparator = "; "; | |||
} | |||
else if ((flags & X500DistinguishedNameFlags.UseNewLines) == X500DistinguishedNameFlags.UseNewLines) | |||
else if ((flags & (X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.UseCommas)) == X500DistinguishedNameFlags.UseNewLines) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment to explain that explicit commas beats newline, but that commas are the implicit default.
new object[] | ||
{ | ||
"CN=GrapeCity inc., OU=Tools Development, O=GrapeCity inc., L=Sendai Izumi-ku, S=Miyagi, C=JP", | ||
"308186310b3009060355040613024a50310f300d060355040813064d6979616769311830160603550407130f53656e64616920497a756d692d6b7531173015060355040a140e47726170654369747920696e632e311a3018060355040b1411546f6f6c7320446576656c6f706d656e74311730150603550403140e47726170654369747920696e632e" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please break these long constants up into multiple lines.
// Mono test case taken from old bug report | ||
new object[] | ||
{ | ||
"SERIALNUMBER=CVR:13471967-UID:121212121212, E=vhm@use.test.dk, CN=Hedeby's Møbelhandel - Salgsafdelingen, O=Hedeby's Møbelhandel // CVR:13471967, C=DK", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how reliably o-slash is going to roundtrip through various git clients and text editors. Please replace it with \u00d8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
// Technically the T.61 encoding (code page 20261) should be used here, but many | ||
// implementations don't follow that and use ISO 8859-1 instead. This matches the | ||
// behavior of CryptoAPI on NetFX (https://github.com/dotnet/corefx/issues/27466). | ||
string t61String = System.Text.Encoding.GetEncoding("ISO-8859-1").GetString(_data, _position, contentLength); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing that it's actually Windows-1252, or, more transparently:
char[] chars = new char[contentLength];
for (int i = 0; i < contentLength; i++)
{
chars[i] = (char)_data[_position + i];
}
string t61string = new string(chars);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually tried couple of 1252 characters on Windows that are missing in iso-8859-1 and they were missing from the output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a test to add 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out it is neither 1252 nor iso-8859-1. Windows actually interprets it as UTF-8. I will add test case for that.
new object[] | ||
{ | ||
"CN=\u00A2", | ||
"300D310B300906035504061402C2A2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0603550406 is "2.5.4.6" => id-countryName ("C=", not "CN=").
on macOS the "Møbelhandel" didn't decode correctly (it got a FFFD replacement character). So maybe it's something more nuanced. UTF-8 unless it had a decode error (e.g. had to add an FFFD), and Windows-1252 otherwise. Looks like, maybe, this could be private static Text.Encoding MakeT61Encoding()
{
// Using Clone() allows DecoderFallback to be assigned.
var utf8 = Text.Encoding.Utf8.Clone();
utf8.DecoderFallback = new TheFallbackWouldHaveToBeWritten();
// Until further need.
utf8.EncoderFallback = EncoderFallback.ExceptionFallback;
} Or
depending on if the fallback is "whole string" or "per character". |
Looks like macOS is not the only problematic one. The OpenSSL decoder fails on the UTF-8 string. It could be easier to just find out what OpenSSL does and mimic that instead of Windows. |
The NETFX test failure is unrelated to the changes. |
new object[] | ||
{ | ||
"C=\u00C2\u00A2", | ||
"300D310B300906035504061402C2A2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the payload had a UTF-8 multi-byte character followed by an invalid UTF-8 sequence?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
300E310C300A06035504071403C2A2F8 -> L=墿 (https://dotnetfiddle.net/ERdAyB), will add it to the tests
[Theory] | ||
[MemberData(nameof(T61CasesLatin1))] | ||
[PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] | ||
public static void T61StringsLatin1(string expected, string hexEncoded) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm fine with dumping X500NameEncoder.OpenSslDecode.cs
as part of this change, and just using the same managed implementation on macOS and Linux. It'll cut down on a lot of SafeHandles and P/Invokes, as well as unify the behaviors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
@dotnet-bot Test NETFX x86 Release Build please (now that #30565 has merged and X509Certificates tests are enabled for netfx) |
try | ||
{ | ||
var utf8 = (System.Text.Encoding)System.Text.Encoding.UTF8.Clone(); | ||
utf8.DecoderFallback = System.Text.DecoderFallback.ExceptionFallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be simplified to new Utf8Encoding(false, true)
since it's using the ExceptionFallback.
I'd probably promote this and the ISO-8859-1 encoding objects to statics, no strong opinion on fieldinit or LazyInitializer.EnsureInitialized or just if (s_utf8WithExceptionsEncoding == null) { s_utf8WithExceptionsEncoding = new Utf8Encoding(false, true); }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't use new Utf8Encoding(false, true)
the first time because of the additional reference to System.Text.Encoding.Extensions. LazyInitializer.EnsureInitialized
seems like a reasonable option since it's already used elsewhere in the class.
Aside from the last comment about the two Encoding objects I think I'm happy with it. @stephentoub would you mind giving it a once-over? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than the one issue about not creating a new Encoding for each use, LGTM.
Thanks, @filipnavara :) |
Fixes https://github.com/dotnet/corefx/issues/27466.