File: System\Security\Cryptography\X509Certificates\ECDsaX509SignatureGenerator.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 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));
        }
    }
}