File: src\libraries\Common\src\System\Security\Cryptography\KeyFormatHelper.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.Buffers;
using System.Diagnostics;
using System.Formats.Asn1;
using System.Runtime.InteropServices;
using System.Security.Cryptography.Asn1;
 
namespace System.Security.Cryptography
{
    internal static partial class KeyFormatHelper
    {
        internal delegate void KeyReader<TRet>(ReadOnlyMemory<byte> key, in AlgorithmIdentifierAsn algId, out TRet ret);
 
        internal static unsafe void ReadSubjectPublicKeyInfo<TRet>(
            string[] validOids,
            ReadOnlySpan<byte> source,
            KeyReader<TRet> keyReader,
            out int bytesRead,
            out TRet ret)
        {
            fixed (byte* ptr = &MemoryMarshal.GetReference(source))
            {
                using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
                {
                    ReadSubjectPublicKeyInfo(validOids, manager.Memory, keyReader, out bytesRead, out ret);
                }
            }
        }
 
        internal static ReadOnlyMemory<byte> ReadSubjectPublicKeyInfo(
            string[] validOids,
            ReadOnlyMemory<byte> source,
            out int bytesRead)
        {
            SubjectPublicKeyInfoAsn spki;
            int read;
 
            try
            {
                // X.509 SubjectPublicKeyInfo is described as DER.
                AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER);
                read = reader.PeekEncodedValue().Length;
                SubjectPublicKeyInfoAsn.Decode(ref reader, source, out spki);
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
 
            if (Array.IndexOf(validOids, spki.Algorithm.Algorithm) < 0)
            {
                throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
            }
 
            bytesRead = read;
            return spki.SubjectPublicKey;
        }
 
        private static void ReadSubjectPublicKeyInfo<TRet>(
            string[] validOids,
            ReadOnlyMemory<byte> source,
            KeyReader<TRet> keyReader,
            out int bytesRead,
            out TRet ret)
        {
            SubjectPublicKeyInfoAsn spki;
            int read;
 
            try
            {
                // X.509 SubjectPublicKeyInfo is described as DER.
                AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER);
                read = reader.PeekEncodedValue().Length;
                SubjectPublicKeyInfoAsn.Decode(ref reader, source, out spki);
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
 
            if (Array.IndexOf(validOids, spki.Algorithm.Algorithm) < 0)
            {
                throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
            }
 
            keyReader(spki.SubjectPublicKey, spki.Algorithm, out ret);
            bytesRead = read;
        }
 
        internal static unsafe void ReadPkcs8<TRet>(
            string[] validOids,
            ReadOnlySpan<byte> source,
            KeyReader<TRet> keyReader,
            out int bytesRead,
            out TRet ret)
        {
            fixed (byte* ptr = &MemoryMarshal.GetReference(source))
            {
                using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
                {
                    ReadPkcs8(validOids, manager.Memory, keyReader, out bytesRead, out ret);
                }
            }
        }
 
        internal static ReadOnlyMemory<byte> ReadPkcs8(
            string[] validOids,
            ReadOnlyMemory<byte> source,
            out int bytesRead)
        {
            try
            {
                AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER);
                int read = reader.PeekEncodedValue().Length;
                PrivateKeyInfoAsn.Decode(ref reader, source, out PrivateKeyInfoAsn privateKeyInfo);
 
                if (Array.IndexOf(validOids, privateKeyInfo.PrivateKeyAlgorithm.Algorithm) < 0)
                {
                    throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
                }
 
                bytesRead = read;
                return privateKeyInfo.PrivateKey;
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
        }
 
        private static void ReadPkcs8<TRet>(
            string[] validOids,
            ReadOnlyMemory<byte> source,
            KeyReader<TRet> keyReader,
            out int bytesRead,
            out TRet ret)
        {
            try
            {
                AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER);
                int read = reader.PeekEncodedValue().Length;
                PrivateKeyInfoAsn.Decode(ref reader, source, out PrivateKeyInfoAsn privateKeyInfo);
 
                if (Array.IndexOf(validOids, privateKeyInfo.PrivateKeyAlgorithm.Algorithm) < 0)
                {
                    throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
                }
 
                // Fails if there are unconsumed bytes.
                keyReader(privateKeyInfo.PrivateKey, privateKeyInfo.PrivateKeyAlgorithm, out ret);
                bytesRead = read;
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
        }
 
        internal static AsnWriter WritePkcs8(
            AsnWriter algorithmIdentifierWriter,
            AsnWriter privateKeyWriter,
            AsnWriter? attributesWriter = null)
        {
            // Ensure both algorithm identifier and key writers are balanced.
            int algorithmIdentifierLength = algorithmIdentifierWriter.GetEncodedLength();
            int privateKeyLength = privateKeyWriter.GetEncodedLength();
 
            Debug.Assert(algorithmIdentifierLength > 0, "algorithmIdentifier was empty");
            Debug.Assert(privateKeyLength > 0, "privateKey was empty");
 
            // https://tools.ietf.org/html/rfc5208#section-5
            //
            // PrivateKeyInfo ::= SEQUENCE {
            //   version                   Version,
            //   privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
            //   privateKey                PrivateKey,
            //   attributes           [0]  IMPLICIT Attributes OPTIONAL }
            //
            // Version ::= INTEGER
            // PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
            // PrivateKey ::= OCTET STRING
            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
 
            // PrivateKeyInfo
            writer.PushSequence();
 
            // https://tools.ietf.org/html/rfc5208#section-5 says the current version is 0.
            writer.WriteInteger(0);
 
            // PKI.Algorithm (AlgorithmIdentifier)
            algorithmIdentifierWriter.CopyTo(writer);
 
            // PKI.privateKey
            using (writer.PushOctetString())
            {
                privateKeyWriter.CopyTo(writer);
            }
 
            // PKI.Attributes
            attributesWriter?.CopyTo(writer);
 
            writer.PopSequence();
            return writer;
        }
    }
}