File: System\Security\Cryptography\X509Certificates\OpenSslX509Encoder.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.Diagnostics;
using System.Formats.Asn1;
using System.IO;
using System.Security.Cryptography.Asn1;
using Microsoft.Win32.SafeHandles;
 
namespace System.Security.Cryptography.X509Certificates
{
    internal sealed class OpenSslX509Encoder : IX509Pal
    {
        public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal)
        {
            if (certificatePal is null)
                throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
 
            return ((OpenSslX509CertificateReader)certificatePal).GetECDsaPublicKey();
        }
 
        public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certificatePal)
        {
            if (certificatePal is null)
                throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
 
            return ((OpenSslX509CertificateReader)certificatePal).GetECDiffieHellmanPublicKey();
        }
 
 
        public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal? certificatePal)
        {
            switch (oid.Value)
            {
                case Oids.Rsa:
                    return BuildRsaPublicKey(encodedKeyValue);
                case Oids.Dsa:
                    return BuildDsaPublicKey(encodedKeyValue, encodedParameters);
            }
 
            // NotSupportedException is thrown by .NET Framework and .NET Core on Windows.
            throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
        }
 
        public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flags)
        {
            return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flags);
        }
 
        public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag)
        {
            return X500NameEncoder.X500DistinguishedNameEncode(distinguishedName, flag);
        }
 
        public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine)
        {
            return X500NameEncoder.X500DistinguishedNameDecode(
                encodedDistinguishedName,
                true,
                multiLine ? X500DistinguishedNameFlags.UseNewLines : X500DistinguishedNameFlags.None,
                multiLine);
        }
 
        public X509ContentType GetCertContentType(ReadOnlySpan<byte> rawData)
        {
            {
                ICertificatePal? certPal;
 
                if (OpenSslX509CertificateReader.TryReadX509Der(rawData, out certPal) ||
                    OpenSslX509CertificateReader.TryReadX509Pem(rawData, out certPal))
                {
                    certPal.Dispose();
 
                    return X509ContentType.Cert;
                }
            }
 
            if (OpenSslPkcsFormatReader.IsPkcs7(rawData))
            {
                return X509ContentType.Pkcs7;
            }
 
            if (X509CertificateLoader.IsPkcs12(rawData))
            {
                return X509ContentType.Pkcs12;
            }
 
            // Unsupported format.
            // Windows throws new CryptographicException(CRYPT_E_NO_MATCH)
            throw new CryptographicException();
        }
 
        public X509ContentType GetCertContentType(string fileName)
        {
            // If we can't open the file, fail right away.
            using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(fileName, "rb"))
            {
                Interop.Crypto.CheckValidOpenSslHandle(fileBio);
 
                int bioPosition = Interop.Crypto.BioTell(fileBio);
                Debug.Assert(bioPosition >= 0);
 
                // X509ContentType.Cert
                {
                    ICertificatePal? certPal;
 
                    if (OpenSslX509CertificateReader.TryReadX509Der(fileBio, out certPal))
                    {
                        certPal.Dispose();
 
                        return X509ContentType.Cert;
                    }
 
                    OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition);
 
                    if (OpenSslX509CertificateReader.TryReadX509Pem(fileBio, out certPal))
                    {
                        certPal.Dispose();
 
                        return X509ContentType.Cert;
                    }
 
                    OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition);
                }
 
                // X509ContentType.Pkcs7
                {
                    if (OpenSslPkcsFormatReader.IsPkcs7Der(fileBio))
                    {
                        return X509ContentType.Pkcs7;
                    }
 
                    OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition);
 
                    if (OpenSslPkcsFormatReader.IsPkcs7Pem(fileBio))
                    {
                        return X509ContentType.Pkcs7;
                    }
 
                    OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition);
                }
            }
 
            // X509ContentType.Pkcs12 (aka PFX)
            if (X509CertificateLoader.IsPkcs12(fileName))
            {
                return X509ContentType.Pkcs12;
            }
 
            // Unsupported format.
            // Windows throws new CryptographicException(CRYPT_E_NO_MATCH)
            throw new CryptographicException();
        }
 
        private static RSAOpenSsl BuildRsaPublicKey(byte[] encodedData)
        {
            var rsa = new RSAOpenSsl();
            try
            {
                rsa.ImportRSAPublicKey(new ReadOnlySpan<byte>(encodedData), out _);
            }
            catch (Exception)
            {
                rsa.Dispose();
                throw;
            }
            return rsa;
        }
 
        private static DSAOpenSsl BuildDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters)
        {
            SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn
            {
                Algorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Dsa, Parameters = encodedParameters },
                SubjectPublicKey = encodedKeyValue,
            };
 
            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
            spki.Encode(writer);
 
            DSAOpenSsl dsa = new DSAOpenSsl();
            try
            {
                return writer.Encode(dsa, static (dsa, encoded) =>
                {
                    dsa.ImportSubjectPublicKeyInfo(encoded, out _);
                    return dsa;
                });
            }
            catch (Exception)
            {
                dsa.Dispose();
                throw;
            }
        }
    }
}