// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Security.Cryptography.X509Certificates; using Microsoft.AspNetCore.Server.Kestrel.Core; namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal { public static class CertificateLoader { // See http://oid-info.com/get/1.3.6.1.5.5.7.3.1 // Indicates that a certificate can be used as a SSL server certificate private const string ServerAuthenticationOid = "1.3.6.1.5.5.7.3.1"; public static X509Certificate2 LoadFromStoreCert(string subject, string storeName, StoreLocation storeLocation, bool allowInvalid) { using (var store = new X509Store(storeName, storeLocation)) { X509Certificate2Collection storeCertificates = null; X509Certificate2 foundCertificate = null; try { store.Open(OpenFlags.ReadOnly); storeCertificates = store.Certificates; var foundCertificates = storeCertificates.Find(X509FindType.FindBySubjectName, subject, !allowInvalid); foundCertificate = foundCertificates .OfType() .Where(IsCertificateAllowedForServerAuth) .OrderByDescending(certificate => certificate.NotAfter) .FirstOrDefault(); if (foundCertificate == null) { throw new InvalidOperationException(CoreStrings.FormatCertNotFoundInStore(subject, storeLocation, storeName, allowInvalid)); } return foundCertificate; } finally { DisposeCertificates(storeCertificates, except: foundCertificate); } } } internal static bool IsCertificateAllowedForServerAuth(X509Certificate2 certificate) { /* If the Extended Key Usage extension is included, then we check that the serverAuth usage is included. (http://oid-info.com/get/1.3.6.1.5.5.7.3.1) * If the Extended Key Usage extension is not included, then we assume the certificate is allowed for all usages. * * See also https://blogs.msdn.microsoft.com/kaushal/2012/02/17/client-certificates-vs-server-certificates/ * * From https://tools.ietf.org/html/rfc3280#section-4.2.1.13 "Certificate Extensions: Extended Key Usage" * * If the (Extended Key Usage) extension is present, then the certificate MUST only be used * for one of the purposes indicated. If multiple purposes are * indicated the application need not recognize all purposes indicated, * as long as the intended purpose is present. Certificate using * applications MAY require that a particular purpose be indicated in * order for the certificate to be acceptable to that application. */ var hasEkuExtension = false; foreach (var extension in certificate.Extensions.OfType()) { hasEkuExtension = true; foreach (var oid in extension.EnhancedKeyUsages) { if (oid.Value.Equals(ServerAuthenticationOid, StringComparison.Ordinal)) { return true; } } } return !hasEkuExtension; } private static void DisposeCertificates(X509Certificate2Collection certificates, X509Certificate2 except) { if (certificates != null) { foreach (var certificate in certificates) { if (!certificate.Equals(except)) { certificate.Dispose(); } } } } } }