From 7f5ccbde792c7fd94ec27c33861ae1c3118ecf93 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 24 Jun 2018 06:29:39 +0200 Subject: [PATCH 1/7] NetFX compatibility fixes for X500DistinguishedName. * Don't write a separator after the empty DN * Make T61String behave like it does on Windows (UTF-8 with a Latin-1 fallback) * Use the managed decoder on Linux, instead of a lot of P/Invokes back into OpenSSL. --- .../Interop.ASN1.cs | 27 ---- .../Interop.X509Name.cs | 39 ------ .../Interop.X509NameEntry.cs | 61 --------- .../SafeHandles/SafeX509NameHandle.Unix.cs | 30 ----- .../Cryptography/DerSequenceReader.cs | 35 +++++ .../pal_x509_name.cpp | 5 + .../pal_x509_name.h | 5 + .../src/System.Net.Http.csproj | 3 - .../src/System.Net.Security.csproj | 3 - ...em.Security.Cryptography.Algorithms.csproj | 1 + ...stem.Security.Cryptography.Encoding.csproj | 1 + ...ystem.Security.Cryptography.OpenSsl.csproj | 1 + .../Pal.Unix/X500NameEncoder.ManagedDecode.cs | 4 +- .../Pal.Unix/X500NameEncoder.OpenSslDecode.cs | 123 ------------------ .../Cryptography/Pal.Unix/X500NameEncoder.cs | 7 +- ...urity.Cryptography.X509Certificates.csproj | 10 +- .../tests/X500DistinguishedNameTests.cs | 69 ++++++++++ 17 files changed, 125 insertions(+), 299 deletions(-) delete mode 100644 src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509NameEntry.cs delete mode 100644 src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs delete mode 100644 src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.cs index 962eaa426f..5bd2e8b9b4 100644 --- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.cs +++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.cs @@ -61,22 +61,6 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Asn1StringFree")] internal static extern void Asn1StringFree(IntPtr o); - internal static string GetOidValue(SafeSharedAsn1ObjectHandle asn1Object) - { - Debug.Assert(asn1Object != null); - - bool added = false; - asn1Object.DangerousAddRef(ref added); - try - { - return GetOidValue(asn1Object.DangerousGetHandle()); - } - finally - { - asn1Object.DangerousRelease(); - } - } - internal static unsafe string GetOidValue(IntPtr asn1ObjectPtr) { // OBJ_obj2txt returns the number of bytes that should have been in the answer, but it does not accept @@ -127,14 +111,3 @@ internal static partial class Interop } } } - -namespace Microsoft.Win32.SafeHandles -{ - internal class SafeSharedAsn1ObjectHandle : SafeInteriorHandle - { - private SafeSharedAsn1ObjectHandle() : - base(IntPtr.Zero, ownsHandle: true) - { - } - } -} diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs index 0f0fecad24..70097d7db2 100644 --- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs +++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs @@ -15,16 +15,6 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameStackFieldCount")] internal static extern int GetX509NameStackFieldCount(SafeSharedX509NameStackHandle sk); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_PushX509NameStackField")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool PushX509NameStackField(SafeX509NameStackHandle stack, SafeX509NameHandle x509_Name); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RecursiveFreeX509NameStack")] - internal static extern void RecursiveFreeX509NameStack(IntPtr stack); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_NewX509NameStack")] - internal static extern SafeX509NameStackHandle NewX509NameStack(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameStackField")] private static extern SafeSharedX509NameHandle GetX509NameStackField_private(SafeSharedX509NameStackHandle sk, int loc); @@ -32,15 +22,6 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameRawBytes")] private static extern int GetX509NameRawBytes(SafeSharedX509NameHandle x509Name, byte[] buf, int cBuf); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_DecodeX509Name")] - internal static extern SafeX509NameHandle DecodeX509Name(byte[] buf, int len); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509NameDestroy")] - internal static extern void X509NameDestroy(IntPtr a); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameEntryCount")] - internal static extern int GetX509NameEntryCount(SafeX509NameHandle x509Name); - internal static X500DistinguishedName LoadX500Name(SafeSharedX509NameHandle namePtr) { CheckValidOpenSslHandle(namePtr); @@ -86,25 +67,5 @@ namespace Microsoft.Win32.SafeHandles { } } - - internal sealed class SafeX509NameStackHandle : SafeHandle - { - private SafeX509NameStackHandle() : - base(IntPtr.Zero, ownsHandle: true) - { - } - - protected override bool ReleaseHandle() - { - Interop.Crypto.RecursiveFreeX509NameStack(handle); - SetHandle(IntPtr.Zero); - return true; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - } } diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509NameEntry.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509NameEntry.cs deleted file mode 100644 index eeeea54d0c..0000000000 --- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509NameEntry.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -internal static partial class Interop -{ - internal static partial class Crypto - { - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameEntry")] - private static extern SafeSharedX509NameEntryHandle GetX509NameEntry_private(SafeX509NameHandle x509Name, int loc); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameEntryOid")] - private static extern SafeSharedAsn1ObjectHandle GetX509NameEntryOid_private(SafeSharedX509NameEntryHandle nameEntry); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameEntryData")] - private static extern SafeSharedAsn1StringHandle GetX509NameEntryData_private(SafeSharedX509NameEntryHandle nameEntry); - - internal static SafeSharedX509NameEntryHandle GetX509NameEntry(SafeX509NameHandle x509Name, int loc) - { - CheckValidOpenSslHandle(x509Name); - - return SafeInteriorHandle.OpenInteriorHandle( - (nameHandle, i) => GetX509NameEntry_private(nameHandle, i), - x509Name, - loc); - } - - internal static SafeSharedAsn1ObjectHandle GetX509NameEntryOid(SafeSharedX509NameEntryHandle nameEntry) - { - CheckValidOpenSslHandle(nameEntry); - - return SafeInteriorHandle.OpenInteriorHandle( - handle => GetX509NameEntryOid_private(handle), - nameEntry); - } - - internal static SafeSharedAsn1StringHandle GetX509NameEntryData(SafeSharedX509NameEntryHandle nameEntry) - { - CheckValidOpenSslHandle(nameEntry); - - return SafeInteriorHandle.OpenInteriorHandle( - handle => GetX509NameEntryData_private(handle), - nameEntry); - } - } -} - -namespace Microsoft.Win32.SafeHandles -{ - internal sealed class SafeSharedX509NameEntryHandle : SafeInteriorHandle - { - private SafeSharedX509NameEntryHandle() : - base(IntPtr.Zero, ownsHandle: true) - { - } - } -} diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs b/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs deleted file mode 100644 index ae22b92303..0000000000 --- a/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Security; -using System.Runtime.InteropServices; - -namespace Microsoft.Win32.SafeHandles -{ - internal sealed class SafeX509NameHandle : SafeHandle - { - private SafeX509NameHandle() : - base(IntPtr.Zero, ownsHandle: true) - { - } - - protected override bool ReleaseHandle() - { - Interop.Crypto.X509NameDestroy(handle); - SetHandle(IntPtr.Zero); - return true; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - } -} diff --git a/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs b/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs index b68c9da093..68d9ac1bd2 100644 --- a/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs +++ b/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs @@ -30,6 +30,9 @@ namespace System.Security.Cryptography internal static DateTimeFormatInfo s_validityDateTimeFormatInfo; + private static System.Text.Encoding s_utf8EncodingWithExceptionFallback; + private static System.Text.Encoding s_latin1Encoding; + private readonly byte[] _data; private readonly int _end; private int _position; @@ -383,6 +386,38 @@ namespace System.Security.Cryptography return TrimTrailingNulls(ia5String); } + internal string ReadT61String() + { + EatTag(DerTag.T61String); + int contentLength = EatLength(); + string t61String; + + // Technically the T.61 encoding (code page 20261) should be used here, but many + // implementations don't follow that and use different character sets. CryptoAPI + // on NetFX seems to interpret it as UTF-8 with fallback to ISO 8859-1. OpenSSL + // seems to interpret it as ISO 8859-1 with no support for UTF-8. + // https://github.com/dotnet/corefx/issues/27466 + + System.Text.Encoding utf8EncodingWithExceptionFallback = LazyInitializer.EnsureInitialized( + ref s_utf8EncodingWithExceptionFallback, + () => new UTF8Encoding(false, true)); + System.Text.Encoding latin1Encoding = LazyInitializer.EnsureInitialized( + ref s_latin1Encoding, + () => System.Text.Encoding.GetEncoding("iso-8859-1")); + + try + { + t61String = utf8EncodingWithExceptionFallback.GetString(_data, _position, contentLength); + } + catch (DecoderFallbackException) + { + t61String = latin1Encoding.GetString(_data, _position, contentLength); + } + _position += contentLength; + + return TrimTrailingNulls(t61String); + } + internal DateTime ReadX509Date() { byte tag = PeekTag(); diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.cpp b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.cpp index 2195b74906..02cbfee4a2 100644 --- a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.cpp +++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.cpp @@ -14,6 +14,11 @@ extern "C" X509_NAME* CryptoNative_GetX509NameStackField(X509NameStack* sk, int3 return sk_X509_NAME_value(sk, loc); } +/* + * The following methods are kept around for compatiblity during servicing. + * They are not used. + */ + extern "C" X509_NAME* CryptoNative_DecodeX509Name(const uint8_t* buf, int32_t len) { if (!buf || !len) diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.h index 76a77ccc1f..b24a794bbd 100644 --- a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.h +++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509_name.h @@ -18,6 +18,11 @@ Direct shim to sk_X509_NAME_value */ extern "C" X509_NAME* CryptoNative_GetX509NameStackField(X509NameStack* sk, int32_t loc); +/* + * The following methods are kept around for compatiblity during servicing. + * They are not used. + */ + /* Shims the d2i_X509_NAME method and makes it easier to invoke from managed code. */ diff --git a/src/System.Net.Http/src/System.Net.Http.csproj b/src/System.Net.Http/src/System.Net.Http.csproj index 3fa7cdd26f..66e5b8d5f8 100644 --- a/src/System.Net.Http/src/System.Net.Http.csproj +++ b/src/System.Net.Http/src/System.Net.Http.csproj @@ -530,9 +530,6 @@ Common\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs - - Common\Microsoft\Win32\SafeHandles\SafeX509NameHandle.Unix.cs - Common\Microsoft\Win32\SafeHandles\X509ExtensionSafeHandles.Unix.cs diff --git a/src/System.Net.Security/src/System.Net.Security.csproj b/src/System.Net.Security/src/System.Net.Security.csproj index 9dff20160e..bf8d773a4e 100644 --- a/src/System.Net.Security/src/System.Net.Security.csproj +++ b/src/System.Net.Security/src/System.Net.Security.csproj @@ -328,9 +328,6 @@ Common\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs - - Common\Microsoft\Win32\SafeHandles\SafeX509NameHandle.Unix.cs - Common\Microsoft\Win32\SafeHandles\X509ExtensionSafeHandles.Unix.cs diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 9b1cb26508..fbe6f4c269 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -558,6 +558,7 @@ + diff --git a/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj b/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj index 9681ef62e4..388d4f36a6 100644 --- a/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj +++ b/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj @@ -106,6 +106,7 @@ + diff --git a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj index 095d62bc6d..3846bcad23 100644 --- a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj +++ b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj @@ -160,6 +160,7 @@ + diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs index 094e95c20d..0285415ba3 100644 --- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs +++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs @@ -127,7 +127,7 @@ namespace Internal.Cryptography.Pal } } - if (addTrailingDelimiter) + if (addTrailingDelimiter && decodedName.Length > 0) { decodedName.Append(dnSeparator); } @@ -150,7 +150,7 @@ namespace Internal.Cryptography.Pal case DerSequenceReader.DerTag.UTF8String: return tavReader.ReadUtf8String(); case DerSequenceReader.DerTag.T61String: - return ""; + return tavReader.ReadT61String(); default: throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs deleted file mode 100644 index 0c03283475..0000000000 --- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text; - -using Microsoft.Win32.SafeHandles; - -namespace Internal.Cryptography.Pal -{ - internal static partial class X500NameEncoder - { - private static string X500DistinguishedNameDecode( - byte[] encodedName, - bool printOid, - bool reverse, - bool quoteIfNeeded, - string dnSeparator, - string multiValueSeparator, - bool addTrailingDelimiter) - { - using (SafeX509NameHandle x509Name = Interop.Crypto.DecodeX509Name(encodedName, encodedName.Length)) - { - if (x509Name.IsInvalid) - { - Interop.Crypto.ErrClearError(); - return ""; - } - - // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual - // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting - // too much space in the average case. - // - // So, let's look at an example of what our output might be. - // - // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs): - // businessCategory=Private Organization - // 1.3.6.1.4.1.311.60.2.1.3=US - // 1.3.6.1.4.1.311.60.2.1.2=Delaware - // serialNumber=5157550 - // street=548 4th Street - // postalCode=94107 - // C=US - // ST=California - // L=San Francisco - // O=GitHub, Inc. - // CN=github.com - // - // Which comes out to 228 characters using OpenSSL's default pretty-print - // (openssl x509 -in github.cer -text -noout) - // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values - // and round that up to the next programmer number, and you get that 512 should avoid reallocations - // in all but the most dire of cases. - StringBuilder decodedName = new StringBuilder(512); - int entryCount = Interop.Crypto.GetX509NameEntryCount(x509Name); - bool printSpacing = false; - - for (int i = 0; i < entryCount; i++) - { - int loc = reverse ? entryCount - i - 1 : i; - - using (SafeSharedX509NameEntryHandle nameEntry = Interop.Crypto.GetX509NameEntry(x509Name, loc)) - { - Interop.Crypto.CheckValidOpenSslHandle(nameEntry); - - string thisOidValue; - - using (SafeSharedAsn1ObjectHandle oidHandle = Interop.Crypto.GetX509NameEntryOid(nameEntry)) - { - thisOidValue = Interop.Crypto.GetOidValue(oidHandle); - } - - if (printSpacing) - { - decodedName.Append(dnSeparator); - } - else - { - printSpacing = true; - } - - if (printOid) - { - AppendOid(decodedName, thisOidValue); - } - - string rdnValue; - - using (SafeSharedAsn1StringHandle valueHandle = Interop.Crypto.GetX509NameEntryData(nameEntry)) - { - rdnValue = Interop.Crypto.Asn1StringToManagedString(valueHandle); - } - - bool quote = quoteIfNeeded && NeedsQuoting(rdnValue); - - if (quote) - { - decodedName.Append('"'); - - // If the RDN itself had a quote within it, that quote needs to be escaped - // with another quote. - rdnValue = rdnValue.Replace("\"", "\"\""); - } - - decodedName.Append(rdnValue); - - if (quote) - { - decodedName.Append('"'); - } - } - } - - if (addTrailingDelimiter) - { - decodedName.Append(dnSeparator); - } - - return decodedName.ToString(); - } - } - } -} diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs index 56da9ae40c..005e09abe3 100644 --- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs +++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs @@ -40,7 +40,7 @@ namespace Internal.Cryptography.Pal byte[] encodedName, bool printOid, X500DistinguishedNameFlags flags, - bool addTrailingDelimieter=false) + bool addTrailingDelimiter = false) { bool reverse = (flags & X500DistinguishedNameFlags.Reversed) == X500DistinguishedNameFlags.Reversed; bool quoteIfNeeded = (flags & X500DistinguishedNameFlags.DoNotUseQuotes) != X500DistinguishedNameFlags.DoNotUseQuotes; @@ -51,7 +51,8 @@ namespace Internal.Cryptography.Pal { dnSeparator = "; "; } - else if ((flags & X500DistinguishedNameFlags.UseNewLines) == X500DistinguishedNameFlags.UseNewLines) + // Explicit UseCommas has preference over explicit UseNewLines. + else if ((flags & (X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.UseCommas)) == X500DistinguishedNameFlags.UseNewLines) { dnSeparator = Environment.NewLine; } @@ -73,7 +74,7 @@ namespace Internal.Cryptography.Pal quoteIfNeeded, dnSeparator, multiValueSparator, - addTrailingDelimieter); + addTrailingDelimiter); } catch (CryptographicException) { diff --git a/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 20b0328943..3bdef009b9 100644 --- a/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -169,7 +169,6 @@ - @@ -223,9 +222,6 @@ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509Name.cs - - Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509NameEntry.cs - Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509Stack.cs @@ -274,9 +270,6 @@ Common\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs - - Common\Microsoft\Win32\SafeHandles\SafeX509NameHandle.Unix.cs - Common\Microsoft\Win32\SafeHandles\X509ExtensionSafeHandles.Unix.cs @@ -390,7 +383,6 @@ - @@ -398,6 +390,7 @@ + @@ -417,6 +410,7 @@ + diff --git a/src/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs index 027823ffbd..95ac1ed519 100644 --- a/src/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs +++ b/src/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs @@ -126,6 +126,16 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.Equal(notQuoted, dn.Decode(X500DistinguishedNameFlags.DoNotUseQuotes)); } + + [Theory] + [MemberData(nameof(T61Cases))] + public static void T61Strings(string expected, string hexEncoded) + { + byte[] encoded = hexEncoded.HexToByteArray(); + X500DistinguishedName dn = new X500DistinguishedName(encoded); + + Assert.Equal(expected, dn.Name); + } [Fact] public static void PrintComplexReversed() @@ -158,6 +168,22 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.EndsWith(expected, dn.Decode(X500DistinguishedNameFlags.None), StringComparison.Ordinal); } + [Fact] + public static void EdgeCaseEmptyFormat() + { + X500DistinguishedName dn = new X500DistinguishedName(""); + Assert.Equal(String.Empty, dn.Format(true)); + Assert.Equal(String.Empty, dn.Format(false)); + } + + [Fact] + public static void EdgeCaseUseCommaAndNewLines() + { + const string rname = "C=US, O=\"RSA Data Security, Inc.\", OU=Secure Server Certification Authority"; + X500DistinguishedName dn = new X500DistinguishedName(rname, X500DistinguishedNameFlags.None); + Assert.Equal(rname, dn.Decode(X500DistinguishedNameFlags.UseCommas | X500DistinguishedNameFlags.UseNewLines)); + } + public static readonly object[][] WhitespaceBeforeCases = { // Regular space. @@ -386,6 +412,49 @@ namespace System.Security.Cryptography.X509Certificates.Tests }, }; + public static readonly object[][] T61Cases = + { + // https://github.com/dotnet/corefx/issues/27466 + new object[] + { + "CN=GrapeCity inc., OU=Tools Development, O=GrapeCity inc., " + + "L=Sendai Izumi-ku, S=Miyagi, C=JP", + "308186310b3009060355040613024a50310f300d060355040813064d69796167" + + "69311830160603550407130f53656e64616920497a756d692d6b753117301506" + + "0355040a140e47726170654369747920696e632e311a3018060355040b141154" + + "6f6f6c7320446576656c6f706d656e74311730150603550403140e4772617065" + + "4369747920696e632e" + }, + + // Mono test case taken from old bug report + new object[] + { + "SERIALNUMBER=CVR:13471967-UID:121212121212, E=vhm@use.test.dk, " + + "CN=Hedeby's M\u00f8belhandel - Salgsafdelingen, " + + "O=Hedeby's M\u00f8belhandel // CVR:13471967, C=DK", + "3081B5310B300906035504061302444B312D302B060355040A14244865646562" + + "792773204DF862656C68616E64656C202F2F204356523A313334373139363731" + + "2F302D060355040314264865646562792773204DF862656C68616E64656C202D" + + "2053616C6773616664656C696E67656E311E301C06092A864886F70D01090116" + + "0F76686D407573652E746573742E646B312630240603550405131D4356523A31" + + "333437313936372D5549443A313231323132313231323132" + }, + + // Valid UTF-8 string is interpreted as UTF-8 + new object[] + { + "C=\u00a2", + "300D310B300906035504061402C2A2" + }, + + // Invalid UTF-8 string with valid UTF-8 sequence is interpreted as ISO 8859-1 + new object[] + { + "L=\u00c2\u00a2\u00f8", + "300E310C300A06035504071403C2A2F8" + }, + }; + private const string MicrosoftDotComSubject = "3082010F31133011060B2B0601040182373C02010313025553311B3019060B2B" + "0601040182373C0201020C0A57617368696E67746F6E311D301B060355040F13" + -- 2.20.1