File: System\Security\Cryptography\X509Certificates\FindPal.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.Numerics;
using Internal.Cryptography;
 
namespace System.Security.Cryptography.X509Certificates
{
    internal partial class FindPal
    {
        private const int NamedKeyUsageFlagsCount = 9;
 
        private static readonly Dictionary<string, X509KeyUsageFlags> s_keyUsages =
            new Dictionary<string, X509KeyUsageFlags>(NamedKeyUsageFlagsCount, StringComparer.OrdinalIgnoreCase)
            {
                { "DigitalSignature", X509KeyUsageFlags.DigitalSignature },
                { "NonRepudiation", X509KeyUsageFlags.NonRepudiation },
                { "KeyEncipherment", X509KeyUsageFlags.KeyEncipherment },
                { "DataEncipherment", X509KeyUsageFlags.DataEncipherment },
                { "KeyAgreement", X509KeyUsageFlags.KeyAgreement },
                { "KeyCertSign", X509KeyUsageFlags.KeyCertSign },
                { "CrlSign", X509KeyUsageFlags.CrlSign },
                { "EncipherOnly", X509KeyUsageFlags.EncipherOnly },
                { "DecipherOnly", X509KeyUsageFlags.DecipherOnly },
            };
 
#if DEBUG
        static FindPal()
        {
            Debug.Assert(s_keyUsages.Count == NamedKeyUsageFlagsCount);
        }
#endif
 
        private static partial IFindPal OpenPal(
            X509Certificate2Collection findFrom,
            X509Certificate2Collection copyTo,
            bool validOnly);
 
        public static X509Certificate2Collection FindFromCollection(
            X509Certificate2Collection coll,
            X509FindType findType,
            object findValue,
            bool validOnly)
        {
            X509Certificate2Collection results = new X509Certificate2Collection();
 
            using (IFindPal findPal = OpenPal(coll, results, validOnly))
            {
                switch (findType)
                {
                    case X509FindType.FindByThumbprint:
                        {
                            byte[] thumbPrint = ConfirmedCast<string>(findValue).LaxDecodeHexString();
                            findPal.FindByThumbprint(thumbPrint);
                            break;
                        }
                    case X509FindType.FindBySubjectName:
                        {
                            string subjectName = ConfirmedCast<string>(findValue);
                            findPal.FindBySubjectName(subjectName);
                            break;
                        }
                    case X509FindType.FindBySubjectDistinguishedName:
                        {
                            string subjectDistinguishedName = ConfirmedCast<string>(findValue);
                            findPal.FindBySubjectDistinguishedName(subjectDistinguishedName);
                            break;
                        }
                    case X509FindType.FindByIssuerName:
                        {
                            string issuerName = ConfirmedCast<string>(findValue);
                            findPal.FindByIssuerName(issuerName);
                            break;
                        }
                    case X509FindType.FindByIssuerDistinguishedName:
                        {
                            string issuerDistinguishedName = ConfirmedCast<string>(findValue);
                            findPal.FindByIssuerDistinguishedName(issuerDistinguishedName);
                            break;
                        }
                    case X509FindType.FindBySerialNumber:
                        {
                            string decimalOrHexString = ConfirmedCast<string>(findValue);
 
                            // FindBySerialNumber allows the input format to be either in
                            // hex or decimal. Since we can't know which one was intended,
                            // it compares against both interpretations and treats a match
                            // of either as a successful find.
 
                            // string is big-endian
                            byte[] hexBytes = decimalOrHexString.LaxDecodeHexString();
 
                            BigInteger hexValue = new BigInteger(hexBytes, isUnsigned: true, isBigEndian: true);
                            BigInteger decimalValue = LaxParseDecimalBigInteger(decimalOrHexString);
                            findPal.FindBySerialNumber(hexValue, decimalValue);
                            break;
                        }
                    case X509FindType.FindByTimeValid:
                        {
                            DateTime dateTime = ConfirmedCast<DateTime>(findValue);
                            findPal.FindByTimeValid(dateTime);
                            break;
                        }
                    case X509FindType.FindByTimeNotYetValid:
                        {
                            DateTime dateTime = ConfirmedCast<DateTime>(findValue);
                            findPal.FindByTimeNotYetValid(dateTime);
                            break;
                        }
                    case X509FindType.FindByTimeExpired:
                        {
                            DateTime dateTime = ConfirmedCast<DateTime>(findValue);
                            findPal.FindByTimeExpired(dateTime);
                            break;
                        }
                    case X509FindType.FindByTemplateName:
                        {
                            string expected = ConfirmedCast<string>(findValue);
                            findPal.FindByTemplateName(expected);
                            break;
                        }
                    case X509FindType.FindByApplicationPolicy:
                        {
                            string oidValue = ConfirmedOidValue(findPal, findValue, OidGroup.Policy);
                            findPal.FindByApplicationPolicy(oidValue);
                            break;
                        }
                    case X509FindType.FindByCertificatePolicy:
                        {
                            string oidValue = ConfirmedOidValue(findPal, findValue, OidGroup.Policy);
                            findPal.FindByCertificatePolicy(oidValue);
                            break;
                        }
                    case X509FindType.FindByExtension:
                        {
                            string oidValue = ConfirmedOidValue(findPal, findValue, OidGroup.ExtensionOrAttribute);
                            findPal.FindByExtension(oidValue);
                            break;
                        }
                    case X509FindType.FindByKeyUsage:
                        {
                            X509KeyUsageFlags keyUsage = ConfirmedX509KeyUsage(findValue);
                            findPal.FindByKeyUsage(keyUsage);
                            break;
                        }
                    case X509FindType.FindBySubjectKeyIdentifier:
                        {
                            byte[] keyIdentifier = ConfirmedCast<string>(findValue).LaxDecodeHexString();
                            findPal.FindBySubjectKeyIdentifier(keyIdentifier);
                            break;
                        }
                    default:
                        throw new CryptographicException(SR.Cryptography_X509_InvalidFindType);
                }
            }
 
            return results;
        }
 
        private static T ConfirmedCast<T>(object findValue)
        {
            Debug.Assert(findValue != null);
 
            if (findValue.GetType() != typeof(T))
                throw new CryptographicException(SR.Cryptography_X509_InvalidFindValue);
 
            return (T)findValue;
        }
 
        private static string ConfirmedOidValue(IFindPal findPal, object findValue, OidGroup oidGroup)
        {
            string maybeOid = ConfirmedCast<string>(findValue);
 
            if (maybeOid.Length == 0)
            {
                throw new ArgumentException(SR.Argument_InvalidOidValue);
            }
 
            return findPal.NormalizeOid(maybeOid, oidGroup);
        }
 
        private static X509KeyUsageFlags ConfirmedX509KeyUsage(object findValue)
        {
            if (findValue is X509KeyUsageFlags)
                return (X509KeyUsageFlags)findValue;
 
            if (findValue is int)
                return (X509KeyUsageFlags)(int)findValue;
 
            if (findValue is uint)
                return (X509KeyUsageFlags)(uint)findValue;
 
            if (findValue is string findValueString)
            {
                X509KeyUsageFlags usageFlags;
 
                if (s_keyUsages.TryGetValue(findValueString, out usageFlags))
                {
                    return usageFlags;
                }
            }
 
            throw new CryptographicException(SR.Cryptography_X509_InvalidFindValue);
        }
 
        //
        // verify the passed keyValue is valid as per X.208
        //
        // The first number must be 0, 1 or 2.
        // Enforce all characters are digits and dots.
        // Enforce that no dot starts or ends the Oid, and disallow double dots.
        // Enforce there is at least one dot separator.
        //
        internal static void ValidateOidValue(string keyValue)
        {
            ArgumentNullException.ThrowIfNull(keyValue);
 
            int len = keyValue.Length;
            if (len < 2)
                throw new ArgumentException(SR.Argument_InvalidOidValue);
 
            // should not start with a dot. The first digit must be 0, 1 or 2.
            char c = keyValue[0];
            if (c != '0' && c != '1' && c != '2')
                throw new ArgumentException(SR.Argument_InvalidOidValue);
            if (keyValue[1] != '.' || keyValue[len - 1] == '.') // should not end in a dot
                throw new ArgumentException(SR.Argument_InvalidOidValue);
 
            // While characters 0 and 1 were both validated, start at character 1 to validate
            // that there aren't two dots in a row.
            for (int i = 1; i < len; i++)
            {
                // ensure every character is either a digit or a dot
                if (char.IsDigit(keyValue[i]))
                    continue;
                if (keyValue[i] != '.' || keyValue[i + 1] == '.') // disallow double dots
                    throw new ArgumentException(SR.Argument_InvalidOidValue);
            }
        }
 
        private static BigInteger LaxParseDecimalBigInteger(string decimalString)
        {
            BigInteger ten = new BigInteger(10);
            BigInteger accum = BigInteger.Zero;
 
            foreach (char c in decimalString)
            {
                if (char.IsAsciiDigit(c))
                {
                    accum = BigInteger.Multiply(accum, ten);
                    accum = BigInteger.Add(accum, c - '0');
                }
            }
 
            return accum;
        }
    }
}