File: System\Security\Cryptography\X509Certificates\X500DistinguishedName.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Formats.Asn1;
 
namespace System.Security.Cryptography.X509Certificates
{
    public sealed class X500DistinguishedName : AsnEncodedData
    {
        private volatile string? _lazyDistinguishedName;
        private List<X500RelativeDistinguishedName>? _parsedAttributes;
 
        public X500DistinguishedName(byte[] encodedDistinguishedName)
            : base(new Oid(null, null), encodedDistinguishedName)
        {
        }
 
        /// <summary>
        ///   Initializes a new instance of the <see cref="X500DistinguishedName"/>
        ///   class using information from the provided data.
        /// </summary>
        /// <param name="encodedDistinguishedName">
        ///   The encoded distinguished name.
        /// </param>
        /// <seealso cref="Encode"/>
        public X500DistinguishedName(ReadOnlySpan<byte> encodedDistinguishedName)
            : base(new Oid(null, null), encodedDistinguishedName)
        {
        }
 
        public X500DistinguishedName(AsnEncodedData encodedDistinguishedName)
            : base(encodedDistinguishedName)
        {
        }
 
        public X500DistinguishedName(X500DistinguishedName distinguishedName)
            : base(distinguishedName)
        {
            _lazyDistinguishedName = distinguishedName.Name;
        }
 
        public X500DistinguishedName(string distinguishedName)
            : this(distinguishedName, X500DistinguishedNameFlags.Reversed)
        {
        }
 
        public X500DistinguishedName(string distinguishedName, X500DistinguishedNameFlags flag)
            : base(new Oid(null, null), Encode(distinguishedName, flag))
        {
            _lazyDistinguishedName = distinguishedName;
        }
 
        public string Name => _lazyDistinguishedName ??= Decode(X500DistinguishedNameFlags.Reversed);
 
        public string Decode(X500DistinguishedNameFlags flag)
        {
            ThrowIfInvalid(flag);
            return X509Pal.Instance.X500DistinguishedNameDecode(RawData, flag);
        }
 
        public override string Format(bool multiLine)
        {
            return X509Pal.Instance.X500DistinguishedNameFormat(RawData, multiLine);
        }
 
        /// <summary>
        ///   Iterates over the RelativeDistinguishedName values within this distinguished name value.
        /// </summary>
        /// <param name="reversed">
        ///   <see langword="true" /> to enumerate in the order used by <see cref="Name"/>;
        ///   <see langword="false" /> to enumerate in the declared order.
        /// </param>
        /// <returns>
        ///   An enumerator that iterates over the relative distinguished names in the X.500 Dinstinguished Name.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   The X.500 Name is not a proper DER-encoded X.500 Name value.
        /// </exception>
        public IEnumerable<X500RelativeDistinguishedName> EnumerateRelativeDistinguishedNames(bool reversed = true)
        {
            List<X500RelativeDistinguishedName> parsedAttributes = _parsedAttributes ??= ParseAttributes(RawData);
 
            return EnumerateRelativeDistinguishedNames(parsedAttributes, reversed);
        }
 
        private static byte[] Encode(string distinguishedName, X500DistinguishedNameFlags flags)
        {
            ArgumentNullException.ThrowIfNull(distinguishedName);
 
            ThrowIfInvalid(flags);
 
            return X509Pal.Instance.X500DistinguishedNameEncode(distinguishedName, flags);
        }
 
        private static void ThrowIfInvalid(X500DistinguishedNameFlags flags)
        {
            // All values or'ed together. Change this if you add values to the enumeration.
            uint allFlags = 0x71F1;
            uint dwFlags = (uint)flags;
            if ((dwFlags & ~allFlags) != 0)
                throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, "flag"));
        }
 
        private static IEnumerable<X500RelativeDistinguishedName> EnumerateRelativeDistinguishedNames(
            List<X500RelativeDistinguishedName> parsedAttributes,
            bool reversed)
        {
            if (reversed)
            {
                for (int i = parsedAttributes.Count - 1; i >= 0; i--)
                {
                    yield return parsedAttributes[i];
                }
            }
            else
            {
                for (int i = 0; i < parsedAttributes.Count; i++)
                {
                    yield return parsedAttributes[i];
                }
            }
        }
 
        private static List<X500RelativeDistinguishedName> ParseAttributes(byte[] rawData)
        {
            List<X500RelativeDistinguishedName>? parsedAttributes = null;
            ReadOnlyMemory<byte> rawDataMemory = rawData;
            ReadOnlySpan<byte> rawDataSpan = rawData;
 
            try
            {
                AsnValueReader outer = new AsnValueReader(rawDataSpan, AsnEncodingRules.DER);
                AsnValueReader sequence = outer.ReadSequence();
                outer.ThrowIfNotEmpty();
 
                while (sequence.HasData)
                {
                    ReadOnlySpan<byte> encodedValue = sequence.PeekEncodedValue();
 
                    if (!rawDataSpan.Overlaps(encodedValue, out int offset))
                    {
                        Debug.Fail("AsnValueReader produced a span outside of the original bounds");
                        throw new UnreachableException();
                    }
 
                    var rdn = new X500RelativeDistinguishedName(rawDataMemory.Slice(offset, encodedValue.Length));
                    sequence.ReadEncodedValue();
                    (parsedAttributes ??= new List<X500RelativeDistinguishedName>()).Add(rdn);
                }
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
 
            return parsedAttributes ?? new List<X500RelativeDistinguishedName>();
        }
    }
}