Convert GeneralNameAsn to ASNXML generation
authorFilip Navara <filip.navara@gmail.com>
Wed, 5 Sep 2018 14:27:22 +0000 (16:27 +0200)
committerJeremy Barton <jbarton@microsoft.com>
Wed, 5 Sep 2018 14:27:22 +0000 (07:27 -0700)
This changes the GeneralNameAsn type and all of its field types to ASNXML generated code, fixing the lack of tags on EdiPartyNameAsn in the process.

It also replaces the manual reading of GeneralName in AppleAsnFormatter, reducing the use of the legacy DerSequenceReader type.

Commit migrated from https://github.com/dotnet/corefx/commit/3e817a4dbed0776dff7f91723906e95237892c0e

19 files changed:
src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.cs [deleted file]
src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml [new file with mode: 0644]
src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs [new file with mode: 0644]
src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs
src/libraries/System.Security.Cryptography.Encoding/src/Resources/Strings.resx
src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj
src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleDeserialize.cs
src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleSerialize.cs
src/libraries/System.Security.Cryptography.Encoding/tests/AsnEncodedData.cs
src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj
src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj
src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/SubjectAlternativeNameBuilder.cs

diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml
new file mode 100644 (file)
index 0000000..7ef2f3f
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Choice
+  xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+  name="DirectoryStringAsn"
+  namespace="System.Security.Cryptography.Asn1">
+
+  <!--
+    https://tools.ietf.org/html/rfc5280#section-4.1.2.4
+
+    DirectoryString ::= CHOICE {
+        teletexString           TeletexString (SIZE (1..MAX)),
+        printableString         PrintableString (SIZE (1..MAX)),
+        universalString         UniversalString (SIZE (1..MAX)),
+        utf8String              UTF8String (SIZE (1..MAX)),
+        bmpString               BMPString (SIZE (1..MAX))
+    }
+  -->
+  <asn:T61String name="TeletexString" />
+  <asn:PrintableString name="PrintableString" />
+  <asn:AnyValue name="UniversalString"  universalTagNumber="28" />
+  <asn:UTF8String name="Utf8String" />
+  <asn:BMPString name="BmpString" />
+</asn:Choice>
\ No newline at end of file
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs
new file mode 100644 (file)
index 0000000..99c15e7
--- /dev/null
@@ -0,0 +1,146 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.Asn1
+{
+    [StructLayout(LayoutKind.Sequential)]
+    internal partial struct DirectoryStringAsn
+    {
+        internal string TeletexString;
+        internal string PrintableString;
+        internal ReadOnlyMemory<byte>? UniversalString;
+        internal string Utf8String;
+        internal string BmpString;
+
+#if DEBUG
+        static DirectoryStringAsn()
+        {
+            var usedTags = new System.Collections.Generic.Dictionary<Asn1Tag, string>();
+            Action<Asn1Tag, string> ensureUniqueTag = (tag, fieldName) =>
+            {
+                if (usedTags.TryGetValue(tag, out string existing))
+                {
+                    throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'");
+                }
+
+                usedTags.Add(tag, fieldName);
+            };
+            
+            ensureUniqueTag(new Asn1Tag(UniversalTagNumber.T61String), "TeletexString");
+            ensureUniqueTag(new Asn1Tag(UniversalTagNumber.PrintableString), "PrintableString");
+            ensureUniqueTag(new Asn1Tag((UniversalTagNumber)28), "UniversalString");
+            ensureUniqueTag(new Asn1Tag(UniversalTagNumber.UTF8String), "Utf8String");
+            ensureUniqueTag(new Asn1Tag(UniversalTagNumber.BMPString), "BmpString");
+        }
+#endif
+
+        internal void Encode(AsnWriter writer)
+        {
+            bool wroteValue = false; 
+            
+            if (TeletexString != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(UniversalTagNumber.T61String, TeletexString);
+                wroteValue = true;
+            }
+
+            if (PrintableString != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(UniversalTagNumber.PrintableString, PrintableString);
+                wroteValue = true;
+            }
+
+            if (UniversalString.HasValue)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                // Validator for tag constraint for UniversalString
+                {
+                    if (!Asn1Tag.TryParse(UniversalString.Value.Span, out Asn1Tag validateTag, out _) ||
+                        !validateTag.HasSameClassAndValue(new Asn1Tag((UniversalTagNumber)28)))
+                    {
+                        throw new CryptographicException();
+                    }
+                }
+
+                writer.WriteEncodedValue(UniversalString.Value);
+                wroteValue = true;
+            }
+
+            if (Utf8String != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(UniversalTagNumber.UTF8String, Utf8String);
+                wroteValue = true;
+            }
+
+            if (BmpString != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(UniversalTagNumber.BMPString, BmpString);
+                wroteValue = true;
+            }
+
+            if (!wroteValue)
+            {
+                throw new CryptographicException();
+            }
+        }
+
+        internal static DirectoryStringAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+        {
+            AsnReader reader = new AsnReader(encoded, ruleSet);
+            
+            Decode(reader, out DirectoryStringAsn decoded);
+            reader.ThrowIfNotEmpty();
+            return decoded;
+        }
+
+        internal static void Decode(AsnReader reader, out DirectoryStringAsn decoded)
+        {
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            decoded = default;
+            Asn1Tag tag = reader.PeekTag();
+            
+            if (tag.HasSameClassAndValue(new Asn1Tag(UniversalTagNumber.T61String)))
+            {
+                decoded.TeletexString = reader.GetCharacterString(UniversalTagNumber.T61String);
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(UniversalTagNumber.PrintableString)))
+            {
+                decoded.PrintableString = reader.GetCharacterString(UniversalTagNumber.PrintableString);
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag((UniversalTagNumber)28)))
+            {
+                decoded.UniversalString = reader.GetEncodedValue();
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(UniversalTagNumber.UTF8String)))
+            {
+                decoded.Utf8String = reader.GetCharacterString(UniversalTagNumber.UTF8String);
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(UniversalTagNumber.BMPString)))
+            {
+                decoded.BmpString = reader.GetCharacterString(UniversalTagNumber.BMPString);
+            }
+            else
+            {
+                throw new CryptographicException();
+            }
+        }
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml
new file mode 100644 (file)
index 0000000..5f2b9d2
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+  xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+  name="EdiPartyNameAsn"
+  namespace="System.Security.Cryptography.Asn1">
+
+  <!--
+    https://tools.ietf.org/html/rfc5280#section-4.2.1.6
+
+    EDIPartyName ::= SEQUENCE {
+        nameAssigner            [0]     DirectoryString OPTIONAL,
+        partyName               [1]     DirectoryString
+    }
+  -->
+  <asn:AsnType name="NameAssigner" explicitTag="0" typeName="System.Security.Cryptography.Asn1.DirectoryStringAsn" optional="true" />
+  <asn:AsnType name="PartyName" explicitTag="1" typeName="System.Security.Cryptography.Asn1.DirectoryStringAsn" />
+</asn:Sequence>
\ No newline at end of file
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs
new file mode 100644 (file)
index 0000000..1803d29
--- /dev/null
@@ -0,0 +1,88 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.Asn1
+{
+    [StructLayout(LayoutKind.Sequential)]
+    internal partial struct EdiPartyNameAsn
+    {
+        internal System.Security.Cryptography.Asn1.DirectoryStringAsn? NameAssigner;
+        internal System.Security.Cryptography.Asn1.DirectoryStringAsn PartyName;
+      
+        internal void Encode(AsnWriter writer)
+        {
+            Encode(writer, Asn1Tag.Sequence);
+        }
+    
+        internal void Encode(AsnWriter writer, Asn1Tag tag)
+        {
+            writer.PushSequence(tag);
+            
+
+            if (NameAssigner.HasValue)
+            {
+                writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+                NameAssigner.Value.Encode(writer);
+                writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+            }
+
+            writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1));
+            PartyName.Encode(writer);
+            writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1));
+            writer.PopSequence(tag);
+        }
+
+        internal static EdiPartyNameAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+        {
+            return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+        }
+        
+        internal static EdiPartyNameAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+        {
+            AsnReader reader = new AsnReader(encoded, ruleSet);
+            
+            Decode(reader, expectedTag, out EdiPartyNameAsn decoded);
+            reader.ThrowIfNotEmpty();
+            return decoded;
+        }
+
+        internal static void Decode(AsnReader reader, out EdiPartyNameAsn decoded)
+        {
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            Decode(reader, Asn1Tag.Sequence, out decoded);
+        }
+
+        internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out EdiPartyNameAsn decoded)
+        {
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            decoded = default;
+            AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+            AsnReader explicitReader;
+            
+
+            if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
+            {
+                explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+                System.Security.Cryptography.Asn1.DirectoryStringAsn tmpNameAssigner;
+                System.Security.Cryptography.Asn1.DirectoryStringAsn.Decode(explicitReader, out tmpNameAssigner);
+                decoded.NameAssigner = tmpNameAssigner;
+
+                explicitReader.ThrowIfNotEmpty();
+            }
+
+
+            explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1));
+            System.Security.Cryptography.Asn1.DirectoryStringAsn.Decode(explicitReader, out decoded.PartyName);
+            explicitReader.ThrowIfNotEmpty();
+
+
+            sequenceReader.ThrowIfNotEmpty();
+        }
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.cs
deleted file mode 100644 (file)
index 967ec31..0000000
+++ /dev/null
@@ -1,144 +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.Runtime.InteropServices;
-
-namespace System.Security.Cryptography.Asn1
-{
-    // https://tools.ietf.org/html/rfc5280#section-4.2.1.6
-    //
-    // GeneralName ::= CHOICE {
-    //     otherName                 [0]  OtherName,
-    //     rfc822Name                [1]  IA5String,
-    //     dNSName                   [2]  IA5String,
-    //     x400Address               [3]  ORAddress,
-    //     directoryName             [4]  Name,
-    //     ediPartyName              [5]  EDIPartyName,
-    //     uniformResourceIdentifier [6]  IA5String,
-    //     iPAddress                 [7]  OCTET STRING,
-    //     registeredID              [8]  OBJECT IDENTIFIER
-    // }
-    [Choice]
-    [StructLayout(LayoutKind.Sequential)]
-    internal struct GeneralNameAsn
-    {
-        [ExpectedTag(0)]
-        internal OtherNameAsn? OtherName;
-
-        [ExpectedTag(1)]
-        [IA5String]
-        internal string Rfc822Name;
-
-        [ExpectedTag(2)]
-        [IA5String]
-        internal string DnsName;
-
-        [ExpectedTag(3)]
-        [AnyValue]
-        internal ReadOnlyMemory<byte>? X400Address;
-
-        [ExpectedTag(4, ExplicitTag = true)]
-        [AnyValue]
-        internal ReadOnlyMemory<byte>? DirectoryName;
-
-        [ExpectedTag(5)]
-        internal EdiPartyNameAsn? EdiPartyName;
-
-        [ExpectedTag(6)]
-        [IA5String]
-        internal string Uri;
-
-        [ExpectedTag(7)]
-        [OctetString]
-        internal ReadOnlyMemory<byte>? IPAddress;
-
-        [ExpectedTag(8)]
-        [ObjectIdentifier]
-        internal string RegisteredId;
-
-        internal void Encode(AsnWriter writer)
-        {
-            AsnSerializer.Serialize(this, writer);
-        }
-
-        internal static void Decode(AsnReader reader, out GeneralNameAsn decoded)
-        {
-            if (reader == null)
-                throw new ArgumentNullException(nameof(reader));
-
-            ReadOnlyMemory<byte> value = reader.GetEncodedValue();
-            decoded = AsnSerializer.Deserialize<GeneralNameAsn>(value, reader.RuleSet);
-        }
-
-        internal static GeneralNameAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
-        {
-            AsnReader reader = new AsnReader(encoded, ruleSet);
-
-            Decode(reader, out GeneralNameAsn decoded);
-            reader.ThrowIfNotEmpty();
-            return decoded;
-        }
-    }
-
-    // https://tools.ietf.org/html/rfc5280#section-4.2.1.6
-    //
-    // OtherName ::= SEQUENCE {
-    //     type-id    OBJECT IDENTIFIER,
-    //     value      [0] EXPLICIT ANY DEFINED BY type-id }
-    // }
-    [StructLayout(LayoutKind.Sequential)]
-    internal struct OtherNameAsn
-    {
-        [ObjectIdentifier]
-        internal string TypeId;
-
-        [ExpectedTag(0, ExplicitTag = true)]
-        [AnyValue]
-        internal ReadOnlyMemory<byte> Value;
-    }
-
-    // https://tools.ietf.org/html/rfc5280#section-4.2.1.6
-    //
-    // EDIPartyName ::= SEQUENCE {
-    //     nameAssigner            [0]     DirectoryString OPTIONAL,
-    //     partyName               [1]     DirectoryString
-    // }
-    [StructLayout(LayoutKind.Sequential)]
-    internal struct EdiPartyNameAsn
-    {
-        [OptionalValue]
-        internal DirectoryStringAsn? NameAssigner;
-
-        internal DirectoryStringAsn PartyName;
-    }
-
-    // https://tools.ietf.org/html/rfc5280#section-4.1.2.4
-    //
-    // DirectoryString ::= CHOICE {
-    //     teletexString           TeletexString (SIZE (1..MAX)),
-    //     printableString         PrintableString (SIZE (1..MAX)),
-    //     universalString         UniversalString (SIZE (1..MAX)),
-    //     utf8String              UTF8String (SIZE (1..MAX)),
-    //     bmpString               BMPString (SIZE (1..MAX))
-    // }
-    [Choice]
-    [StructLayout(LayoutKind.Sequential)]
-    internal struct DirectoryStringAsn
-    {
-        [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.TeletexString)]
-        internal ReadOnlyMemory<byte>? TeletexString;
-
-        [PrintableString]
-        internal string PrintableString;
-
-        [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.UniversalString)]
-        internal ReadOnlyMemory<byte>? UniversalString;
-
-        [UTF8String]
-        internal string Utf8String;
-
-        [BMPString]
-        internal string BmpString;
-    }
-}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml
new file mode 100644 (file)
index 0000000..81b5175
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Choice
+  xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+  name="GeneralNameAsn"
+  namespace="System.Security.Cryptography.Asn1">
+
+  <!--
+    https://tools.ietf.org/html/rfc5280#section-4.2.1.6
+
+    GeneralName ::= CHOICE {
+        otherName                 [0]  OtherName,
+        rfc822Name                [1]  IA5String,
+        dNSName                   [2]  IA5String,
+        x400Address               [3]  ORAddress,
+        directoryName             [4]  Name,
+        ediPartyName              [5]  EDIPartyName,
+        uniformResourceIdentifier [6]  IA5String,
+        iPAddress                 [7]  OCTET STRING,
+        registeredID              [8]  OBJECT IDENTIFIER
+    }
+  -->
+  <asn:AsnType name="OtherName" implicitTag="0" typeName="System.Security.Cryptography.Asn1.OtherNameAsn" />
+  <asn:IA5String name="Rfc822Name" implicitTag="1" />
+  <asn:IA5String name="DnsName" implicitTag="2" />
+  <asn:AnyValue name="X400Address" implicitTag="3" />
+  <asn:AnyValue name="DirectoryName" explicitTag="4" />
+  <asn:AsnType name="EdiPartyName" implicitTag="5" typeName="System.Security.Cryptography.Asn1.EdiPartyNameAsn" />
+  <asn:IA5String name="Uri" implicitTag="6" />
+  <asn:OctetString name="IPAddress" implicitTag="7" />
+  <asn:ObjectIdentifier name="RegisteredId" implicitTag="8" backingType="string" />
+</asn:Choice>
\ No newline at end of file
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs
new file mode 100644 (file)
index 0000000..c8bb7c2
--- /dev/null
@@ -0,0 +1,226 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.Asn1
+{
+    [StructLayout(LayoutKind.Sequential)]
+    internal partial struct GeneralNameAsn
+    {
+        internal System.Security.Cryptography.Asn1.OtherNameAsn? OtherName;
+        internal string Rfc822Name;
+        internal string DnsName;
+        internal ReadOnlyMemory<byte>? X400Address;
+        internal ReadOnlyMemory<byte>? DirectoryName;
+        internal System.Security.Cryptography.Asn1.EdiPartyNameAsn? EdiPartyName;
+        internal string Uri;
+        internal ReadOnlyMemory<byte>? IPAddress;
+        internal string RegisteredId;
+
+#if DEBUG
+        static GeneralNameAsn()
+        {
+            var usedTags = new System.Collections.Generic.Dictionary<Asn1Tag, string>();
+            Action<Asn1Tag, string> ensureUniqueTag = (tag, fieldName) =>
+            {
+                if (usedTags.TryGetValue(tag, out string existing))
+                {
+                    throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'");
+                }
+
+                usedTags.Add(tag, fieldName);
+            };
+            
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 0), "OtherName");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 1), "Rfc822Name");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 2), "DnsName");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 3), "X400Address");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 4), "DirectoryName");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 5), "EdiPartyName");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 6), "Uri");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 7), "IPAddress");
+            ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 8), "RegisteredId");
+        }
+#endif
+
+        internal void Encode(AsnWriter writer)
+        {
+            bool wroteValue = false; 
+            
+            if (OtherName.HasValue)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                OtherName.Value.Encode(writer, new Asn1Tag(TagClass.ContextSpecific, 0));
+                wroteValue = true;
+            }
+
+            if (Rfc822Name != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String, Rfc822Name);
+                wroteValue = true;
+            }
+
+            if (DnsName != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String, DnsName);
+                wroteValue = true;
+            }
+
+            if (X400Address.HasValue)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                // Validator for tag constraint for X400Address
+                {
+                    if (!Asn1Tag.TryParse(X400Address.Value.Span, out Asn1Tag validateTag, out _) ||
+                        !validateTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3)))
+                    {
+                        throw new CryptographicException();
+                    }
+                }
+
+                writer.WriteEncodedValue(X400Address.Value);
+                wroteValue = true;
+            }
+
+            if (DirectoryName.HasValue)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4));
+                writer.WriteEncodedValue(DirectoryName.Value);
+                writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4));
+                wroteValue = true;
+            }
+
+            if (EdiPartyName.HasValue)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                EdiPartyName.Value.Encode(writer, new Asn1Tag(TagClass.ContextSpecific, 5));
+                wroteValue = true;
+            }
+
+            if (Uri != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String, Uri);
+                wroteValue = true;
+            }
+
+            if (IPAddress.HasValue)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 7), IPAddress.Value.Span);
+                wroteValue = true;
+            }
+
+            if (RegisteredId != null)
+            {
+                if (wroteValue)
+                    throw new CryptographicException();
+                
+                writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 8), RegisteredId);
+                wroteValue = true;
+            }
+
+            if (!wroteValue)
+            {
+                throw new CryptographicException();
+            }
+        }
+
+        internal static GeneralNameAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+        {
+            AsnReader reader = new AsnReader(encoded, ruleSet);
+            
+            Decode(reader, out GeneralNameAsn decoded);
+            reader.ThrowIfNotEmpty();
+            return decoded;
+        }
+
+        internal static void Decode(AsnReader reader, out GeneralNameAsn decoded)
+        {
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            decoded = default;
+            Asn1Tag tag = reader.PeekTag();
+            AsnReader explicitReader;
+            
+            if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
+            {
+                System.Security.Cryptography.Asn1.OtherNameAsn tmpOtherName;
+                System.Security.Cryptography.Asn1.OtherNameAsn.Decode(reader, new Asn1Tag(TagClass.ContextSpecific, 0), out tmpOtherName);
+                decoded.OtherName = tmpOtherName;
+
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
+            {
+                decoded.Rfc822Name = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String);
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2)))
+            {
+                decoded.DnsName = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String);
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3)))
+            {
+                decoded.X400Address = reader.GetEncodedValue();
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4)))
+            {
+                explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4));
+                decoded.DirectoryName = explicitReader.GetEncodedValue();
+                explicitReader.ThrowIfNotEmpty();
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5)))
+            {
+                System.Security.Cryptography.Asn1.EdiPartyNameAsn tmpEdiPartyName;
+                System.Security.Cryptography.Asn1.EdiPartyNameAsn.Decode(reader, new Asn1Tag(TagClass.ContextSpecific, 5), out tmpEdiPartyName);
+                decoded.EdiPartyName = tmpEdiPartyName;
+
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6)))
+            {
+                decoded.Uri = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String);
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7)))
+            {
+
+                if (reader.TryGetPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 7), out ReadOnlyMemory<byte> tmpIPAddress))
+                {
+                    decoded.IPAddress = tmpIPAddress;
+                }
+                else
+                {
+                    decoded.IPAddress = reader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 7));
+                }
+
+            }
+            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8)))
+            {
+                decoded.RegisteredId = reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 8));
+            }
+            else
+            {
+                throw new CryptographicException();
+            }
+        }
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml
new file mode 100644 (file)
index 0000000..a8b71c7
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<asn:Sequence
+  xmlns:asn="http://schemas.dot.net/asnxml/201808/"
+  name="OtherNameAsn"
+  namespace="System.Security.Cryptography.Asn1">
+
+  <!--
+    https://tools.ietf.org/html/rfc5280#section-4.2.1.6
+
+    OtherName ::= SEQUENCE {
+        type-id    OBJECT IDENTIFIER,
+        value      [0] EXPLICIT ANY DEFINED BY type-id }
+    }
+  -->
+  <asn:ObjectIdentifier name="TypeId" backingType="string" />
+  <asn:AnyValue name="Value" explicitTag="0" />
+</asn:Sequence>
\ No newline at end of file
diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs
new file mode 100644 (file)
index 0000000..8d30720
--- /dev/null
@@ -0,0 +1,71 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography.Asn1
+{
+    [StructLayout(LayoutKind.Sequential)]
+    internal partial struct OtherNameAsn
+    {
+        internal string TypeId;
+        internal ReadOnlyMemory<byte> Value;
+      
+        internal void Encode(AsnWriter writer)
+        {
+            Encode(writer, Asn1Tag.Sequence);
+        }
+    
+        internal void Encode(AsnWriter writer, Asn1Tag tag)
+        {
+            writer.PushSequence(tag);
+            
+            writer.WriteObjectIdentifier(TypeId);
+            writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+            writer.WriteEncodedValue(Value);
+            writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+            writer.PopSequence(tag);
+        }
+
+        internal static OtherNameAsn Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+        {
+            return Decode(Asn1Tag.Sequence, encoded, ruleSet);
+        }
+        
+        internal static OtherNameAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet)
+        {
+            AsnReader reader = new AsnReader(encoded, ruleSet);
+            
+            Decode(reader, expectedTag, out OtherNameAsn decoded);
+            reader.ThrowIfNotEmpty();
+            return decoded;
+        }
+
+        internal static void Decode(AsnReader reader, out OtherNameAsn decoded)
+        {
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            Decode(reader, Asn1Tag.Sequence, out decoded);
+        }
+
+        internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out OtherNameAsn decoded)
+        {
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            decoded = default;
+            AsnReader sequenceReader = reader.ReadSequence(expectedTag);
+            AsnReader explicitReader;
+            
+            decoded.TypeId = sequenceReader.ReadObjectIdentifierAsString();
+
+            explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+            decoded.Value = explicitReader.GetEncodedValue();
+            explicitReader.ThrowIfNotEmpty();
+
+
+            sequenceReader.ThrowIfNotEmpty();
+        }
+    }
+}
index 1df487f..6cf169c 100644 (file)
@@ -2,8 +2,10 @@
 // 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.Collections.Generic;
 using System.Security.Cryptography;
+using System.Security.Cryptography.Asn1;
 using System.Text;
 
 namespace Internal.Cryptography
@@ -35,24 +37,6 @@ namespace Internal.Cryptography
             return null;
         }
 
-        private const string CommaSpace = ", ";
-
-        internal enum GeneralNameType
-        {
-            OtherName = 0,
-            Rfc822Name = 1,
-            // RFC 822: Standard for the format of ARPA Internet Text Messages.
-            // That means "email", and an RFC 822 Name: "Email address"
-            Email = Rfc822Name,
-            DnsName = 2,
-            X400Address = 3,
-            DirectoryName = 4,
-            EdiPartyName = 5,
-            UniformResourceIdentifier = 6,
-            IPAddress = 7,
-            RegisteredId = 8,
-        }
-
         private string FormatSubjectAlternativeName(byte[] rawData)
         {
             // Because SubjectAlternativeName is a commonly parsed structure, we'll
@@ -61,152 +45,100 @@ namespace Internal.Cryptography
             //
             // The intent here is to be functionally equivalent to OpenSSL GENERAL_NAME_print.
 
-            // The end size of this string is hard to predict.
-            // * dNSName values have a tag that takes four characters to represent ("DNS:")
-            //   and then their payload is ASCII encoded (so one byte -> one char), so they
-            //   work out to be about equal (in chars) to their DER encoded length (in bytes).
-            // * iPAddress values have a tag that takes 11 characters ("IP Address:") and then
-            //   grow from 4 bytes to up to 15 characters for IPv4, or 16 bytes to 47 characters
-            //   for IPv6
-            //
-            // So use a List<string> and just Concat them all when we're done, and we reduce the
-            // number of times we copy the header values (vs pointers to the header values).
-            List<string> segments = new List<string>();
-
             try
             {
-                // SubjectAltName ::= GeneralNames
-                //
-                // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
-                //
-                // GeneralName ::= CHOICE {
-                //   otherName                       [0]     OtherName,
-                //   rfc822Name                      [1]     IA5String,
-                //   dNSName                         [2]     IA5String,
-                //   x400Address                     [3]     ORAddress,
-                //   directoryName                   [4]     Name,
-                //   ediPartyName                    [5]     EDIPartyName,
-                //   uniformResourceIdentifier       [6]     IA5String,
-                //   iPAddress                       [7]     OCTET STRING,
-                //   registeredID                    [8]     OBJECT IDENTIFIER }
-                //
-                // OtherName::= SEQUENCE {
-                //   type - id    OBJECT IDENTIFIER,
-                //   value[0] EXPLICIT ANY DEFINED BY type - id }
-                DerSequenceReader altNameReader = new DerSequenceReader(rawData);
+                StringBuilder output = new StringBuilder();
+                AsnReader reader = new AsnReader(rawData, AsnEncodingRules.DER);
+                AsnReader collectionReader = reader.ReadSequence();
+
+                reader.ThrowIfNotEmpty();
 
-                while (altNameReader.HasData)
+                while (collectionReader.HasData)
                 {
-                    if (segments.Count != 0)
+                    GeneralNameAsn.Decode(collectionReader, out GeneralNameAsn generalName);
+
+                    if (output.Length != 0)
                     {
-                        segments.Add(CommaSpace);
+                        output.Append(", ");
                     }
 
-                    byte tag = altNameReader.PeekTag();
-
-                    if ((tag & DerSequenceReader.ContextSpecificTagFlag) == 0)
+                    if (generalName.OtherName.HasValue)
                     {
-                        // All GeneralName values need the ContextSpecific flag.
-                        return null;
+                        output.Append("othername:<unsupported>");
                     }
-
-                    GeneralNameType nameType = (GeneralNameType)(tag & DerSequenceReader.TagNumberMask);
-
-                    bool needsConstructedFlag = false;
-
-                    switch (nameType)
+                    else if (generalName.Rfc822Name != null)
                     {
-                        case GeneralNameType.OtherName:
-                        case GeneralNameType.X400Address:
-                        case GeneralNameType.DirectoryName:
-                        case GeneralNameType.EdiPartyName:
-                            needsConstructedFlag = true;
-                            break;
+                        output.Append("email:");
+                        output.Append(generalName.Rfc822Name);
                     }
-
-                    if (needsConstructedFlag &&
-                        (tag & DerSequenceReader.ConstructedFlag) == 0)
+                    else if (generalName.DnsName != null)
                     {
-                        // All of the SEQUENCE types require the constructed bit,
-                        // or OpenSSL will have refused to print it.
-                        return null;
+                        output.Append("DNS:");
+                        output.Append(generalName.DnsName);
                     }
-
-                    switch (nameType)
+                    else if (generalName.X400Address != null)
                     {
-                        case GeneralNameType.OtherName:
-                            segments.Add("othername:<unsupported>");
-                            altNameReader.SkipValue();
-                            break;
-                        case GeneralNameType.Rfc822Name:
-                            segments.Add("email:");
-                            segments.Add(altNameReader.ReadIA5String());
-                            break;
-                        case GeneralNameType.DnsName:
-                            segments.Add("DNS:");
-                            segments.Add(altNameReader.ReadIA5String());
-                            break;
-                        case GeneralNameType.X400Address:
-                            segments.Add("X400Name:<unsupported>");
-                            altNameReader.SkipValue();
-                            break;
-                        case GeneralNameType.DirectoryName:
-                            // OpenSSL supports printing one of these, but the logic lives in X509Certificates,
-                            // and it isn't very common.  So we'll skip this one until someone asks for it.
-                            segments.Add("DirName:<unsupported>");
-                            altNameReader.SkipValue();
-                            break;
-                        case GeneralNameType.EdiPartyName:
-                            segments.Add("EdiPartyName:<unsupported>");
-                            altNameReader.SkipValue();
-                            break;
-                        case GeneralNameType.UniformResourceIdentifier:
-                            segments.Add("URI:");
-                            segments.Add(altNameReader.ReadIA5String());
-                            break;
-                        case GeneralNameType.IPAddress:
-                            segments.Add("IP Address");
-
-                            byte[] ipAddressBytes = altNameReader.ReadOctetString();
-
-                            if (ipAddressBytes.Length == 4)
-                            {
-                                // Add the colon and dotted-decimal representation of IPv4.
-                                segments.Add(
-                                    $":{ipAddressBytes[0]}.{ipAddressBytes[1]}.{ipAddressBytes[2]}.{ipAddressBytes[3]}");
-                            }
-                            else if (ipAddressBytes.Length == 16)
-                            {
-                                // Print the IP Address value as colon separated UInt16 hex values without leading zeroes.
-                                // 20 01 0D B8 AC 10 FE 01 00 00 00 00 00 00 00 00
-                                //
-                                // IP Address:2001:DB8:AC10:FE01:0:0:0:0
-                                for (int i = 0; i < ipAddressBytes.Length; i += 2)
-                                {
-                                    segments.Add($":{ipAddressBytes[i] << 8 | ipAddressBytes[i + 1]:X}");
-                                }
-                            }
-                            else
+                        output.Append("X400Name:<unsupported>");
+                    }
+                    else if (generalName.DirectoryName != null)
+                    {
+                        // OpenSSL supports printing one of these, but the logic lives in X509Certificates,
+                        // and it isn't very common.  So we'll skip this one until someone asks for it.
+                        output.Append("DirName:<unsupported>");
+                    }
+                    else if (generalName.EdiPartyName != null)
+                    {
+                        output.Append("EdiPartyName:<unsupported>");
+                    }
+                    else if (generalName.Uri != null)
+                    {
+                        output.Append("URI:");
+                        output.Append(generalName.Uri);
+                    }
+                    else if (generalName.IPAddress.HasValue)
+                    {
+                        ReadOnlySpan<byte> ipAddressBytes = generalName.IPAddress.Value.Span;
+
+                        output.Append("IP Address");
+                        if (ipAddressBytes.Length == 4)
+                        {
+                            // Add the colon and dotted-decimal representation of IPv4.
+                            output.Append(
+                                $":{ipAddressBytes[0]}.{ipAddressBytes[1]}.{ipAddressBytes[2]}.{ipAddressBytes[3]}");
+                        }
+                        else if (ipAddressBytes.Length == 16)
+                        {
+                            // Print the IP Address value as colon separated UInt16 hex values without leading zeroes.
+                            // 20 01 0D B8 AC 10 FE 01 00 00 00 00 00 00 00 00
+                            //
+                            // IP Address:2001:DB8:AC10:FE01:0:0:0:0
+                            for (int i = 0; i < ipAddressBytes.Length; i += 2)
                             {
-                                segments.Add(":<invalid>");
+                                output.Append($":{ipAddressBytes[i] << 8 | ipAddressBytes[i + 1]:X}");
                             }
-
-                            break;
-                        case GeneralNameType.RegisteredId:
-                            segments.Add("Registered ID:");
-                            segments.Add(altNameReader.ReadOidAsString());
-                            break;
-                        default:
-                            // A new extension to GeneralName could legitimately hit this,
-                            // but it's correct to say that until we know what that is that
-                            // the pretty-print has failed, and we should fall back to hex.
-                            //
-                            // But it could also simply be poorly encoded user data.
-                            return null;
+                        }
+                        else
+                        {
+                            output.Append(":<invalid>");
+                        }
+                    }
+                    else if (generalName.RegisteredId != null)
+                    {
+                        output.Append("Registered ID:");
+                        output.Append(generalName.RegisteredId);
+                    }
+                    else
+                    {
+                        // A new extension to GeneralName could legitimately hit this,
+                        // but it's correct to say that until we know what that is that
+                        // the pretty-print has failed, and we should fall back to hex.
+                        //
+                        // But it could also simply be poorly encoded user data.
+                        return null;
                     }
                 }
 
-                return string.Concat(segments);
+                return output.ToString();
             }
             catch (CryptographicException)
             {
index b28f989..baed2a4 100644 (file)
   <data name="Cryptography_SSE_InvalidDataSize" xml:space="preserve">
     <value>NoLength of the data to encrypt is invalid.</value>
   </data>
+  <data name="Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum" xml:space="preserve">
+    <value>ASN.1 Enumerated values only apply to enum types without the [Flags] attribute.</value>
+  </data>
+  <data name="Cryptography_Asn_NamedBitListRequiresFlagsEnum" xml:space="preserve">
+    <value>Named bit list operations require an enum with the [Flags] attribute.</value>
+  </data>
+  <data name="Cryptography_Asn_NamedBitListValueTooBig" xml:space="preserve">
+    <value>The encoded named bit list value is larger than the value size of the '{0}' enum.</value>
+  </data>
+  <data name="Cryptography_Asn_UniversalValueIsFixed" xml:space="preserve">
+    <value>Tags with TagClass Universal must have the appropriate TagValue value for the data type being read or written.</value>
+  </data>
+  <data name="Cryptography_Asn_UnusedBitCountRange" xml:space="preserve">
+    <value>Unused bit count must be between 0 and 7, inclusive.</value>
+  </data>
+  <data name="Cryptography_AsnWriter_EncodeUnbalancedStack" xml:space="preserve">
+    <value>Encode cannot be called while a Sequence or SetOf is still open.</value>
+  </data>
+  <data name="Cryptography_AsnWriter_PopWrongTag" xml:space="preserve">
+    <value>Cannot pop the requested tag as it is not currently in progress.</value>
+  </data>
+  <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
+    <value>ASN1 corrupted data.</value>
+  </data>
+  <data name="Cryptography_Invalid_IA5String" xml:space="preserve">
+    <value>The string contains a character not in the 7 bit ASCII character set.</value>
+  </data>
+  <data name="Cryptography_WriteEncodedValue_OneValueAtATime" xml:space="preserve">
+    <value>The input to WriteEncodedValue must represent a single encoded value with no trailing data.</value>
+  </data>
   <data name="Arg_RankMultiDimNotSupported" xml:space="preserve">
     <value>Only single dimensional arrays are supported for the requested action.</value>
   </data>
   <data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
     <value>Non-negative number required.</value>
   </data>
+  <data name="Argument_InvalidOidValue" xml:space="preserve">
+    <value>The OID value was invalid.</value>
+  </data>
   <data name="Argument_InvalidOffLen" xml:space="preserve">
     <value>Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.</value>
   </data>
   <data name="Argument_InvalidValue" xml:space="preserve">
     <value>Value was invalid.</value>
   </data>
-  <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
-    <value>ASN1 corrupted data.</value>
-  </data>
   <data name="ObjectDisposed_Generic" xml:space="preserve">
     <value>Cannot access a disposed object.</value>
   </data>
index 0483a1d..714475d 100644 (file)
@@ -3,6 +3,8 @@
     <ProjectGuid>{AA81E343-5E54-40B0-9381-C459419BE780}</ProjectGuid>
     <AssemblyName>System.Security.Cryptography.Encoding</AssemblyName>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DefineConstants Condition="'$(TargetGroup)'=='netcoreapp'">$(DefineConstants);netcoreapp</DefineConstants>
+    <DefineConstants Condition="'$(TargetGroup)'=='uap'">$(DefineConstants);uap</DefineConstants>
     <Configurations>netcoreapp-OSX-Debug;netcoreapp-OSX-Release;netcoreapp-Unix-Debug;netcoreapp-Unix-Release;netcoreapp-Windows_NT-Debug;netcoreapp-Windows_NT-Release;uap-Windows_NT-Debug;uap-Windows_NT-Release</Configurations>
   </PropertyGroup>
   <ItemGroup>
     </Compile>
   </ItemGroup>
   <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
-    <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
-      <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1V2.cs">
+      <Link>Common\System\Security\Cryptography\Asn1V2.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\AsnReader.cs">
+      <Link>Common\System\Security\Cryptography\AsnReader.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\AsnWriter.cs">
+      <Link>Common\System\Security\Cryptography\AsnWriter.cs</Link>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\OtherNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\OtherNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml</DependentUpon>
     </Compile>
     <Compile Include="Internal\Cryptography\AsnFormatter.OSX.cs" />
     <Compile Include="Internal\Cryptography\OidLookup.OSX.cs" />
   </ItemGroup>
   <ItemGroup>
+    <Reference Include="System.Buffers" />
     <Reference Include="System.Collections" />
     <Reference Include="System.Collections.Concurrent" />
     <Reference Include="System.Diagnostics.Debug" />
index 47a09b1..4801ed8 100644 (file)
@@ -176,11 +176,11 @@ namespace System.Security.Cryptography.Tests.Asn1
             const string BmpInputHex = "1E0400480069";
             const string Utf8InputHex = "0C024869";
 
-            var ds1 = AsnSerializer.Deserialize<DirectoryStringAsn>(
+            var ds1 = AsnSerializer.Deserialize<DirectoryString>(
                 BmpInputHex.HexToByteArray(),
                 AsnEncodingRules.DER);
 
-            var ds2 = AsnSerializer.Deserialize<DirectoryStringAsn>(
+            var ds2 = AsnSerializer.Deserialize<DirectoryString>(
                 Utf8InputHex.HexToByteArray(),
                 AsnEncodingRules.DER);
 
@@ -705,11 +705,40 @@ namespace System.Security.Cryptography.Tests.Asn1
         }
     }
 
+    // https://tools.ietf.org/html/rfc5280#section-4.1.2.4
+    //
+    // DirectoryString ::= CHOICE {
+    //     teletexString           TeletexString (SIZE (1..MAX)),
+    //     printableString         PrintableString (SIZE (1..MAX)),
+    //     universalString         UniversalString (SIZE (1..MAX)),
+    //     utf8String              UTF8String (SIZE (1..MAX)),
+    //     bmpString               BMPString (SIZE (1..MAX))
+    // }
+    [Choice]
+    [StructLayout(LayoutKind.Sequential)]
+    internal struct DirectoryString
+    {
+        [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.TeletexString)]
+        internal ReadOnlyMemory<byte>? TeletexString;
+
+        [PrintableString]
+        internal string PrintableString;
+
+        [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.UniversalString)]
+        internal ReadOnlyMemory<byte>? UniversalString;
+
+        [UTF8String]
+        internal string Utf8String;
+
+        [BMPString]
+        internal string BmpString;
+    }
+
     [Choice]
     [StructLayout(LayoutKind.Sequential)]
     internal struct FlexibleString
     {
-        public DirectoryStringAsn? DirectoryString;
+        public DirectoryString? DirectoryString;
 
         [IA5String]
         public string Ascii;
@@ -741,7 +770,7 @@ namespace System.Security.Cryptography.Tests.Asn1
     [StructLayout(LayoutKind.Sequential)]
     internal sealed class FlexibleStringClassHybrid
     {
-        public DirectoryStringAsn? DirectoryString;
+        public DirectoryString? DirectoryString;
 
         [IA5String]
         public string Ascii;
index baf1ff0..228fef6 100644 (file)
@@ -276,7 +276,7 @@ namespace System.Security.Cryptography.Tests.Asn1
         {
             var hybrid = new FlexibleStringClassHybrid
             {
-                DirectoryString = new DirectoryStringAsn
+                DirectoryString = new DirectoryString
                 {
                     Utf8String = "Marco",
                 },
@@ -293,7 +293,7 @@ namespace System.Security.Cryptography.Tests.Asn1
         {
             var hybrid = new FlexibleStringClassHybrid
             {
-                DirectoryString = new DirectoryStringAsn
+                DirectoryString = new DirectoryString
                 {
                     BmpString = "Polo",
                 },
index 108abce..178120c 100644 (file)
@@ -73,20 +73,20 @@ namespace System.Security.Cryptography.Encoding.Tests
         public static void TestSubjectAlternativeName_Unix()
         {
             byte[] sanExtension = (
-                "3081D3A027060A2B0601040182371402" +
+                "3081D1A027060A2B0601040182371402" +
                 "03A0190C177375626A65637475706E31" +
                 "406578616D706C652E6F726781157361" +
                 "6E656D61696C31406578616D706C652E" +
                 "6F72678218646E73312E7375626A6563" +
                 "742E6578616D706C652E6F7267A30630" +
-                "0441027573A500863168747470733A2F" +
-                "2F7777772E6578616D706C652E6F7267" +
-                "2F706174682F746F2F612F7265736F75" +
-                "72636523616E63686F7287047F000001" +
-                "871020010DB8AC10FE01000000000000" +
-                "0000870F20010DB8AC10FE0100000000" +
-                "0000008704FFFFFFFF8704020F636488" +
-                "052901020203").HexToByteArray();
+                "0441027573863168747470733A2F2F77" +
+                "77772E6578616D706C652E6F72672F70" +
+                "6174682F746F2F612F7265736F757263" +
+                "6523616E63686F7287047F0000018710" +
+                "20010DB8AC10FE010000000000000000" +
+                "870F20010DB8AC10FE01000000000000" +
+                "008704FFFFFFFF8704020F6364880529" +
+                "01020203").HexToByteArray();
 
             AsnEncodedData asnData = new AsnEncodedData(
                 new Oid("2.5.29.17"),
@@ -106,8 +106,8 @@ namespace System.Security.Cryptography.Encoding.Tests
                 "X400Name:<unsupported>",
                 // Skip Choice[4]: DirName
                 //   (Supported by OpenSSL, but not by our Apple version)
-                // Choice[5]: EdiName
-                "EdiPartyName:<unsupported>",
+                // Skip Choice[5]: EdiName
+                //   (Buggy parsing in OpenSSL)
                 // Choice[6]: URI
                 "URI:https://www.example.org/path/to/a/resource#anchor",
                 // Choice[7]: IPAddress (IPv4)
@@ -127,5 +127,18 @@ namespace System.Security.Cryptography.Encoding.Tests
 
             Assert.Equal(expected, s);
         }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.OSX)]
+        public static void TestSubjectAlternativeName_Mac()
+        {
+            byte[] sanExtension = "300EA50CA10A13086564695061727479".HexToByteArray();
+
+            AsnEncodedData asnData = new AsnEncodedData(
+                new Oid("2.5.29.17"),
+                sanExtension);
+
+            Assert.Equal("EdiPartyName:<unsupported>", asnData.Format(false));
+        }
     }
 }
index 3af6439..a9482f1 100644 (file)
@@ -73,9 +73,6 @@
     <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
       <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.cs">
-      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.cs">
       <Link>Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.cs</Link>
     </Compile>
index 22d33fa..c5fd89a 100644 (file)
     <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\EncryptedPrivateKeyInfoAsn.cs">
       <Link>Common\System\Security\Cryptography\Asn1\EncryptedPrivateKeyInfoAsn.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.cs">
-      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.cs</Link>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\OtherNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\OtherNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml</DependentUpon>
     </Compile>
     <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\PBEParameter.cs">
       <Link>Common\System\Security\Cryptography\Asn1\PBEParameter.cs</Link>
index fcda371..ffc99bb 100644 (file)
     <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.cs">
       <Link>Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.cs">
-      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.cs</Link>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\DirectoryStringAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\EdiPartyNameAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\GeneralNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\GeneralNameAsn.xml</DependentUpon>
+    </Compile>
+    <AsnXml Include="$(CommonPath)\System\Security\Cryptography\Asn1\OtherNameAsn.xml">
+      <Link>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml</Link>
+    </AsnXml>
+    <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\OtherNameAsn.xml.cs">
+      <Link>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml.cs</Link>
+      <DependentUpon>Common\System\Security\Cryptography\Asn1\OtherNameAsn.xml</DependentUpon>
     </Compile>
     <Compile Include="$(CommonPath)\System\Security\Cryptography\Asn1\PssParamsAsn.cs">
       <Link>Common\System\Security\Cryptography\Asn1\PssParamsAsn.cs</Link>
index a668aba..b105981 100644 (file)
@@ -94,8 +94,9 @@ namespace System.Security.Cryptography.X509Certificates
             try
             {
                 // Verify that the general name can be serialized and store it.
-                using (AsnWriter writer = AsnSerializer.Serialize(generalName, AsnEncodingRules.DER))
+                using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
                 {
+                    generalName.Encode(writer);
                     _encodedNames.Add(writer.Encode());
                 }
             }