File: System\Security\Cryptography\X509Certificates\X509Pal.Windows.X500DistinguishedName.cs
Web Access
Project: src\src\runtime\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.Diagnostics;
using System.Runtime.InteropServices;
using Internal.Cryptography;

namespace System.Security.Cryptography.X509Certificates
{
    /// <summary>
    /// A singleton class that encapsulates the native implementation of various X509 services. (Implementing this as a singleton makes it
    /// easier to split the class into abstract and implementation classes if desired.)
    /// </summary>
    internal sealed partial class X509Pal : IX509Pal
    {
        public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag)
        {
            int dwStrType = (int)(Interop.Crypt32.CertNameStrTypeAndFlags.CERT_X500_NAME_STR | MapNameToStrFlag(flag));
            unsafe
            {
                fixed (byte* pbEncoded = encodedDistinguishedName)
                {
                    Interop.Crypt32.DATA_BLOB nameBlob;
                    nameBlob.cbData = (uint)encodedDistinguishedName.Length;
                    nameBlob.pbData = new IntPtr(pbEncoded);

                    int cchDecoded = Interop.Crypt32.CertNameToStr((int)Interop.Crypt32.CertEncodingType.All, &nameBlob, dwStrType, null, 0);
                    if (cchDecoded == 0)
                        throw ErrorCode.CERT_E_INVALID_NAME.ToCryptographicException();

                    Span<char> buffer = cchDecoded <= 256 ? stackalloc char[cchDecoded] : new char[cchDecoded];
                    fixed (char* ptr = buffer)
                    {
                        if (Interop.Crypt32.CertNameToStr((int)Interop.Crypt32.CertEncodingType.All, &nameBlob, dwStrType, ptr, cchDecoded) == 0)
                            throw ErrorCode.CERT_E_INVALID_NAME.ToCryptographicException();
                    }

                    return new string(buffer.Slice(0, cchDecoded - 1));
                }
            }
        }

        public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag)
        {
            Debug.Assert(distinguishedName != null);

            Interop.Crypt32.CertNameStrTypeAndFlags dwStrType = Interop.Crypt32.CertNameStrTypeAndFlags.CERT_X500_NAME_STR | MapNameToStrFlag(flag);

            int cbEncoded = 0;
            if (!Interop.Crypt32.CertStrToName(Interop.Crypt32.CertEncodingType.All, distinguishedName, dwStrType, IntPtr.Zero, null, ref cbEncoded, IntPtr.Zero))
                throw Marshal.GetLastPInvokeError().ToCryptographicException();

            byte[] encodedName = new byte[cbEncoded];
            if (!Interop.Crypt32.CertStrToName(Interop.Crypt32.CertEncodingType.All, distinguishedName, dwStrType, IntPtr.Zero, encodedName, ref cbEncoded, IntPtr.Zero))
                throw Marshal.GetLastPInvokeError().ToCryptographicException();

            return encodedName;
        }

        public unsafe string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine)
        {
            if (encodedDistinguishedName == null || encodedDistinguishedName.Length == 0)
                return string.Empty;

            int stringType = multiLine ? Interop.Crypt32.CRYPT_FORMAT_STR_MULTI_LINE : Interop.Crypt32.CRYPT_FORMAT_STR_NONE;

            int cbFormat = 0;
            if (!Interop.Crypt32.CryptFormatObject(
                (int)Interop.Crypt32.CertEncodingType.X509_ASN_ENCODING,
                (int)FormatObjectType.None,
                stringType,
                IntPtr.Zero,
                (byte*)(int)FormatObjectStructType.X509_NAME,
                encodedDistinguishedName,
                encodedDistinguishedName.Length,
                null,
                ref cbFormat))
            {
                return encodedDistinguishedName.ToHexStringUpper();
            }

            int spanLength = (cbFormat + 1) / 2;
            Span<char> buffer = spanLength <= 256 ? stackalloc char[spanLength] : new char[spanLength];
            fixed (char* ptr = buffer)
            {
                if (!Interop.Crypt32.CryptFormatObject(
                    (int)Interop.Crypt32.CertEncodingType.X509_ASN_ENCODING,
                    (int)FormatObjectType.None,
                    stringType,
                    IntPtr.Zero,
                    (byte*)(int)FormatObjectStructType.X509_NAME,
                    encodedDistinguishedName,
                    encodedDistinguishedName.Length,
                    (byte*)ptr,
                    ref cbFormat))
                {
                    return encodedDistinguishedName.ToHexStringUpper();
                }
            }

            return new string(buffer.Slice(0, (cbFormat / 2) - 1));
        }

        private static Interop.Crypt32.CertNameStrTypeAndFlags MapNameToStrFlag(X500DistinguishedNameFlags flag)
        {
            // All values or'ed together. Change this if you add values to the enumeration.
            uint allFlags = 0x71F1;
            uint dwFlags = (uint)flag;
            Debug.Assert((dwFlags & ~allFlags) == 0);

            Interop.Crypt32.CertNameStrTypeAndFlags dwStrType = 0;
            if (dwFlags != 0)
            {
                if ((flag & X500DistinguishedNameFlags.Reversed) == X500DistinguishedNameFlags.Reversed)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_REVERSE_FLAG;

                if ((flag & X500DistinguishedNameFlags.UseSemicolons) == X500DistinguishedNameFlags.UseSemicolons)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_SEMICOLON_FLAG;
                else if ((flag & X500DistinguishedNameFlags.UseCommas) == X500DistinguishedNameFlags.UseCommas)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_COMMA_FLAG;
                else if ((flag & X500DistinguishedNameFlags.UseNewLines) == X500DistinguishedNameFlags.UseNewLines)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_CRLF_FLAG;

                if ((flag & X500DistinguishedNameFlags.DoNotUsePlusSign) == X500DistinguishedNameFlags.DoNotUsePlusSign)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_NO_PLUS_FLAG;
                if ((flag & X500DistinguishedNameFlags.DoNotUseQuotes) == X500DistinguishedNameFlags.DoNotUseQuotes)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_NO_QUOTING_FLAG;

                if ((flag & X500DistinguishedNameFlags.ForceUTF8Encoding) == X500DistinguishedNameFlags.ForceUTF8Encoding)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_FORCE_UTF8_DIR_STR_FLAG;

                if ((flag & X500DistinguishedNameFlags.UseUTF8Encoding) == X500DistinguishedNameFlags.UseUTF8Encoding)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG;
                else if ((flag & X500DistinguishedNameFlags.UseT61Encoding) == X500DistinguishedNameFlags.UseT61Encoding)
                    dwStrType |= Interop.Crypt32.CertNameStrTypeAndFlags.CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
            }
            return dwStrType;
        }
    }
}