File: System\Security\Cryptography\X509Certificates\OpenSslPkcsFormatReader.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.Diagnostics.CodeAnalysis;
using Microsoft.Win32.SafeHandles;
 
namespace System.Security.Cryptography.X509Certificates
{
    internal static class OpenSslPkcsFormatReader
    {
        internal static bool IsPkcs7(ReadOnlySpan<byte> rawData)
        {
            using (SafePkcs7Handle pkcs7 = Interop.Crypto.DecodePkcs7(rawData))
            {
                if (pkcs7.IsInvalid)
                {
                    Interop.Crypto.ErrClearError();
                }
                else
                {
                    return true;
                }
 
            }
 
            using (SafeBioHandle bio = Interop.Crypto.CreateMemoryBio())
            {
                Interop.Crypto.CheckValidOpenSslHandle(bio);
 
                if (Interop.Crypto.BioWrite(bio, rawData) != rawData.Length)
                {
                    Interop.Crypto.ErrClearError();
                }
 
                using (SafePkcs7Handle pkcs7 = Interop.Crypto.PemReadBioPkcs7(bio))
                {
                    if (pkcs7.IsInvalid)
                    {
                        Interop.Crypto.ErrClearError();
                        return false;
                    }
 
                    return true;
                }
            }
        }
 
        internal static bool IsPkcs7Der(SafeBioHandle fileBio)
        {
            using (SafePkcs7Handle pkcs7 = Interop.Crypto.D2IPkcs7Bio(fileBio))
            {
                if (pkcs7.IsInvalid)
                {
                    Interop.Crypto.ErrClearError();
                    return false;
                }
 
                return true;
            }
        }
 
        internal static bool IsPkcs7Pem(SafeBioHandle fileBio)
        {
            using (SafePkcs7Handle pkcs7 = Interop.Crypto.PemReadBioPkcs7(fileBio))
            {
                if (pkcs7.IsInvalid)
                {
                    Interop.Crypto.ErrClearError();
                    return false;
                }
 
                return true;
            }
        }
 
        internal static bool TryReadPkcs7Der(ReadOnlySpan<byte> rawData, out ICertificatePal? certPal)
        {
            return TryReadPkcs7Der(rawData, true, out certPal, out _);
        }
 
        internal static bool TryReadPkcs7Der(SafeBioHandle bio, out ICertificatePal? certPal)
        {
            return TryReadPkcs7Der(bio, true, out certPal, out _);
        }
 
        internal static bool TryReadPkcs7Der(ReadOnlySpan<byte> rawData, [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            return TryReadPkcs7Der(rawData, false, out _, out certPals);
        }
 
        internal static bool TryReadPkcs7Der(SafeBioHandle bio, [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            return TryReadPkcs7Der(bio, false, out _, out certPals);
        }
 
        private static bool TryReadPkcs7Der(
            ReadOnlySpan<byte> rawData,
            bool single,
            out ICertificatePal? certPal,
            [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            using (SafePkcs7Handle pkcs7 = Interop.Crypto.DecodePkcs7(rawData))
            {
                if (pkcs7.IsInvalid)
                {
                    certPal = null;
                    certPals = null;
                    Interop.Crypto.ErrClearError();
                    return false;
                }
 
                return TryReadPkcs7(pkcs7, single, out certPal, out certPals);
            }
        }
 
        private static bool TryReadPkcs7Der(
            SafeBioHandle bio,
            bool single,
            out ICertificatePal? certPal,
            [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            using (SafePkcs7Handle pkcs7 = Interop.Crypto.D2IPkcs7Bio(bio))
            {
                if (pkcs7.IsInvalid)
                {
                    certPal = null;
                    certPals = null;
                    Interop.Crypto.ErrClearError();
                    return false;
                }
 
                return TryReadPkcs7(pkcs7, single, out certPal, out certPals);
            }
        }
 
        internal static bool TryReadPkcs7Pem(ReadOnlySpan<byte> rawData, out ICertificatePal? certPal)
        {
            return TryReadPkcs7Pem(rawData, true, out certPal, out _);
        }
 
        internal static bool TryReadPkcs7Pem(SafeBioHandle bio, out ICertificatePal? certPal)
        {
            return TryReadPkcs7Pem(bio, true, out certPal, out _);
        }
 
        internal static bool TryReadPkcs7Pem(ReadOnlySpan<byte> rawData, [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            return TryReadPkcs7Pem(rawData, false, out _, out certPals);
        }
 
        internal static bool TryReadPkcs7Pem(SafeBioHandle bio, [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            return TryReadPkcs7Pem(bio, false, out _, out certPals);
        }
 
        private static bool TryReadPkcs7Pem(
            ReadOnlySpan<byte> rawData,
            bool single,
            out ICertificatePal? certPal,
            [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            using (SafeBioHandle bio = Interop.Crypto.CreateMemoryBio())
            {
                Interop.Crypto.CheckValidOpenSslHandle(bio);
 
                if (Interop.Crypto.BioWrite(bio, rawData) != rawData.Length)
                {
                    Interop.Crypto.ErrClearError();
                }
 
                return TryReadPkcs7Pem(bio, single, out certPal, out certPals);
            }
        }
 
        private static bool TryReadPkcs7Pem(
            SafeBioHandle bio,
            bool single,
            out ICertificatePal? certPal,
            [NotNullWhen(true)] out List<ICertificatePal>? certPals)
        {
            using (SafePkcs7Handle pkcs7 = Interop.Crypto.PemReadBioPkcs7(bio))
            {
                if (pkcs7.IsInvalid)
                {
                    certPal = null;
                    certPals = null;
                    Interop.Crypto.ErrClearError();
                    return false;
                }
 
                return TryReadPkcs7(pkcs7, single, out certPal, out certPals);
            }
        }
 
        private static bool TryReadPkcs7(
            SafePkcs7Handle pkcs7,
            bool single,
            out ICertificatePal? certPal,
            [NotNullWhen(true)] out List<ICertificatePal> certPals)
        {
            List<ICertificatePal>? readPals = single ? null : new List<ICertificatePal>();
 
            using (SafeSharedX509StackHandle certs = Interop.Crypto.GetPkcs7Certificates(pkcs7))
            {
                int count = Interop.Crypto.GetX509StackFieldCount(certs);
 
                if (single)
                {
                    // In single mode for a PKCS#7 signed or signed-and-enveloped file we're supposed to return
                    // the certificate which signed the PKCS#7 file.
                    //
                    // X509Certificate2Collection::Export(X509ContentType.Pkcs7) claims to be a signed PKCS#7,
                    // but doesn't emit a signature block. So this is hard to test.
                    //
                    // TODO(2910): Figure out how to extract the signing certificate, when it's present.
                    throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner);
                }
 
                Debug.Assert(readPals != null); // null if single == true
 
                for (int i = 0; i < count; i++)
                {
                    // Use FromHandle to duplicate the handle since it would otherwise be freed when the PKCS7
                    // is Disposed.
                    IntPtr certHandle = Interop.Crypto.GetX509StackField(certs, i);
                    ICertificatePal pal = CertificatePal.FromHandle(certHandle);
                    readPals.Add(pal);
                }
            }
 
            certPal = null;
            certPals = readPals;
            return true;
        }
    }
}