未验证 提交 d3d537fa 编写于 作者: K Kevin Jones 提交者: GitHub

Support displaying X.500 AttributeValue in binary form

上级 205ac766
......@@ -530,7 +530,7 @@
<Compile Include="System\Security\Cryptography\X509Certificates\StoreName.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\StorePal.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\SubjectAlternativeNameBuilder.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\X500DictionaryStringHelper.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\X500DirectoryStringHelper.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\X500DistinguishedName.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\X500DistinguishedNameBuilder.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\X500DistinguishedNameFlags.cs" />
......
......@@ -5,7 +5,7 @@
namespace System.Security.Cryptography.X509Certificates
{
internal static class X500DictionaryStringHelper
internal static class X500DirectoryStringHelper
{
internal static string ReadAnyAsnString(this AsnReader tavReader)
{
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Formats.Asn1;
using System.Text;
......@@ -92,7 +93,7 @@ internal static partial class X500NameEncoder
{
AsnReader tavReader = rdnReader.ReadSequence();
string oid = tavReader.ReadObjectIdentifier();
string attributeValue = tavReader.ReadAnyAsnString();
string attributeValue = ReadAttributeValue(tavReader, out bool fallback);
tavReader.ThrowIfNotEmpty();
......@@ -110,7 +111,7 @@ internal static partial class X500NameEncoder
AppendOid(ref decodedName, oid);
}
bool quote = quoteIfNeeded && NeedsQuoting(attributeValue);
bool quote = quoteIfNeeded && NeedsQuoting(attributeValue) && !fallback;
if (quote)
{
......@@ -144,5 +145,52 @@ internal static partial class X500NameEncoder
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
}
private static string ReadAttributeValue(AsnReader tavReader, out bool binaryFallback)
{
Debug.Assert(tavReader.RuleSet == AsnEncodingRules.DER);
Asn1Tag tag = tavReader.PeekTag();
if (tag.TagClass == TagClass.Universal)
{
switch ((UniversalTagNumber)tag.TagValue)
{
case UniversalTagNumber.BMPString:
case UniversalTagNumber.IA5String:
case UniversalTagNumber.NumericString:
case UniversalTagNumber.PrintableString:
case UniversalTagNumber.UTF8String:
case UniversalTagNumber.T61String:
// .NET's string comparisons start by checking the length, so a trailing
// NULL character which was literally embedded in the DER would cause a
// failure in .NET whereas it wouldn't have with strcmp.
binaryFallback = false;
return tavReader.ReadCharacterString((UniversalTagNumber)tag.TagValue).TrimEnd('\0');
case UniversalTagNumber.OctetString:
// Windows will implicitly unwrap one OCTET STRING and display only the contents.
if (tavReader.TryReadPrimitiveOctetString(out ReadOnlyMemory<byte> contents))
{
binaryFallback = true;
return BinaryEncode(contents);
}
Debug.Fail("TryReadPrimitiveOctetString should either succeed or throw with DER.");
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
}
binaryFallback = true;
return BinaryEncode(tavReader.ReadEncodedValue());
static string BinaryEncode(ReadOnlyMemory<byte> data)
{
return string.Create(1 + data.Length * 2, data, static (buff, state) =>
{
buff[0] = '#';
HexConverter.EncodeToUtf16(state.Span, buff.Slice(1));
});
}
}
}
}
......@@ -458,6 +458,84 @@ public static void CheckCachedOids()
Assert.Equal("1.2.840.113549.1.9.1", rdns[2].GetSingleElementType().Value);
}
[Theory]
[InlineData(new [] { "2.5.4.3" }, new [] { "3000" }, "CN=#3000")]
[InlineData(new [] { "2.5.4.5" }, new[] { "0603550406" }, "SERIALNUMBER=#0603550406")]
[InlineData(new [] { "0.0" }, new[] { "31020500" }, "OID.0.0=#31020500")]
[InlineData(new [] { "2.5.4.3" }, new [] { "04023000" }, "CN=#3000")] // OCTET STRING is implicitly stripped
[InlineData(new [] { "2.5.4.3" }, new [] { "040404023000" }, "CN=#04023000")] // Only one OCTET STRING is stripped
[InlineData(new [] { "2.5.4.3" }, new [] { "0303003000" }, "CN=#0303003000")] // BIT STRING is not implicitly stripped
[InlineData(new [] { "2.5.4.8" }, new [] { "0500" }, "S=#0500")]
[InlineData(new [] { "2.5.4.8" }, new [] { "0101FF" }, "S=#0101FF")]
[InlineData(new [] { "2.5.4.3", "2.5.4.8" }, new [] { "0101FF", "3000" }, "CN=#0101FF, S=#3000")]
[InlineData(new [] { "2.5.4.3", "2.5.4.8", "0.0" }, new [] { "0C02504A", "3000", "0C024141" }, "CN=PJ, S=#3000, OID.0.0=AA")]
[InlineData(new [] { "2.5.4.3", "2.5.4.8" }, new [] { "0C03233030", "3000" }, "CN=\"#00\", S=#3000")]
[SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support an X.509 PAL")]
public static void Format_ComponentWithNonStringContent(string[] oids, string[] attributeValues, string expected)
{
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
using (writer.PushSequence())
{
for (int i = 0; i < oids.Length; i++)
{
using (writer.PushSetOf())
using (writer.PushSequence())
{
writer.WriteObjectIdentifier(oids[i]);
writer.WriteEncodedValue(Convert.FromHexString(attributeValues[i]));
}
}
}
X500DistinguishedName distinguishedName = new X500DistinguishedName(writer.Encode());
string dnString = distinguishedName.Format(false);
Assert.Equal(expected, dnString);
string decode = distinguishedName.Decode(X500DistinguishedNameFlags.None);
Assert.Equal(expected, decode);
}
[Fact]
[SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support an X.509 PAL")]
public static void Format_MultiValueComponentWithNonStringContent()
{
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
using (writer.PushSequence())
{
using (writer.PushSetOf())
{
WriteRDNComponent(writer, "2.5.4.3", "3000");
WriteRDNComponent(writer, "2.5.4.8", "3100");
}
using (writer.PushSetOf())
{
WriteRDNComponent(writer, "2.5.4.5", "0C0430313233");
WriteRDNComponent(writer, "2.5.4.10", "31055050505050");
WriteRDNComponent(writer, "2.5.4.9", "0C075441434F434154");
}
}
const string Expected = "CN=#3000 + S=#3100, SERIALNUMBER=0123 + O=#31055050505050 + STREET=TACOCAT";
X500DistinguishedName distinguishedName = new X500DistinguishedName(writer.Encode());
string dnString = distinguishedName.Format(false);
Assert.Equal(Expected, dnString);
string decode = distinguishedName.Decode(X500DistinguishedNameFlags.None);
Assert.Equal(Expected, decode);
static void WriteRDNComponent(AsnWriter writer, string oid, string value)
{
using (writer.PushSequence())
{
writer.WriteObjectIdentifier(oid);
writer.WriteEncodedValue(Convert.FromHexString(value));
}
}
}
public static readonly object[][] WhitespaceBeforeCases =
{
// Regular space.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册