File: System\Security\Cryptography\X509Certificates\ECDsaX509SignatureGenerator.cs
Web Access
Project: src\runtime\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 Internal.Cryptography;

namespace System.Security.Cryptography.X509Certificates
{
    internal sealed class ECDsaX509SignatureGenerator : X509SignatureGenerator
    {
        private readonly ECDsa _key;

        internal ECDsaX509SignatureGenerator(ECDsa key)
        {
            Debug.Assert(key != null);

            _key = key;
        }

        public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlgorithm)
        {
            string oid;

            if (hashAlgorithm == HashAlgorithmName.SHA256)
            {
                oid = Oids.ECDsaWithSha256;
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA384)
            {
                oid = Oids.ECDsaWithSha384;
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA512)
            {
                oid = Oids.ECDsaWithSha512;
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA3_256)
            {
                oid = Oids.ECDsaWithSha3_256;
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA3_384)
            {
                oid = Oids.ECDsaWithSha3_384;
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA3_512)
            {
                oid = Oids.ECDsaWithSha3_512;
            }
            else
            {
                throw new ArgumentOutOfRangeException(
                    nameof(hashAlgorithm),
                    hashAlgorithm,
                    SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name));
            }

            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
            writer.PushSequence();
            writer.WriteObjectIdentifier(oid);
            writer.PopSequence();
            return writer.Encode();
        }

        public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm)
        {
            return _key.SignData(data, hashAlgorithm, DSASignatureFormat.Rfc3279DerSequence);
        }

        protected override PublicKey BuildPublicKey()
        {
            ECParameters ecParameters = _key.ExportParameters(false);

            if (!ecParameters.Curve.IsNamed)
            {
                throw new InvalidOperationException(SR.Cryptography_ECC_NamedCurvesOnly);
            }

            string? curveOid = ecParameters.Curve.Oid.Value;
            byte[] curveOidEncoded;

            if (string.IsNullOrEmpty(curveOid))
            {
                string friendlyName = ecParameters.Curve.Oid.FriendlyName!;

                // Translate the three curves that were supported Windows 7-8.1, but emit no Oid.Value;
                // otherwise just wash the friendly name back through Oid to see if we can get a value.
                curveOid = friendlyName switch
                {
                    "nistP256" => Oids.secp256r1,
                    "nistP384" => Oids.secp384r1,
                    "nistP521" => Oids.secp521r1,
                    _ => new Oid(friendlyName).Value,
                };
            }

            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
            writer.WriteObjectIdentifier(curveOid!);
            curveOidEncoded = writer.Encode();

            Debug.Assert(ecParameters.Q.X!.Length == ecParameters.Q.Y!.Length);
            byte[] uncompressedPoint = new byte[1 + ecParameters.Q.X.Length + ecParameters.Q.Y.Length];

            // Uncompressed point (0x04)
            uncompressedPoint[0] = 0x04;

            Buffer.BlockCopy(ecParameters.Q.X, 0, uncompressedPoint, 1, ecParameters.Q.X.Length);
            Buffer.BlockCopy(ecParameters.Q.Y, 0, uncompressedPoint, 1 + ecParameters.Q.X.Length, ecParameters.Q.Y.Length);

            Oid ecPublicKey = Oids.EcPublicKeyOid;

            return new PublicKey(
                ecPublicKey,
                new AsnEncodedData(ecPublicKey, curveOidEncoded, skipCopy: true),
                new AsnEncodedData(ecPublicKey, uncompressedPoint, skipCopy: true));
        }
    }
}