From 8dd93e2929bb5ef6504626a983b4249aee8b65df Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 9 Dec 2025 21:13:01 +0100 Subject: [PATCH] wip --- .../Authentication/ApiCredentials.cs | 12 ++++ .../Authentication/ApiCredentialsType.cs | 6 +- .../Authentication/AuthenticationProvider.cs | 56 +++++++++++++++++++ CryptoExchange.Net/CryptoExchange.Net.csproj | 1 + 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/CryptoExchange.Net/Authentication/ApiCredentials.cs b/CryptoExchange.Net/Authentication/ApiCredentials.cs index f5a2ca4..1b1cbab 100644 --- a/CryptoExchange.Net/Authentication/ApiCredentials.cs +++ b/CryptoExchange.Net/Authentication/ApiCredentials.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Threading.Tasks; namespace CryptoExchange.Net.Authentication { @@ -45,6 +47,16 @@ namespace CryptoExchange.Net.Authentication Pass = pass; } + /// + /// Load a private key from a file path + /// + public async Task LoadPrivateKey(string path) + { + using var filestream = File.OpenRead(path); + using var streamReader = new StreamReader(filestream); + return await streamReader.ReadToEndAsync().ConfigureAwait(false); + } + /// /// Copy the credentials /// diff --git a/CryptoExchange.Net/Authentication/ApiCredentialsType.cs b/CryptoExchange.Net/Authentication/ApiCredentialsType.cs index 2da474f..63b0281 100644 --- a/CryptoExchange.Net/Authentication/ApiCredentialsType.cs +++ b/CryptoExchange.Net/Authentication/ApiCredentialsType.cs @@ -16,6 +16,10 @@ /// /// Rsa keys credentials in pem/base64 format. Only available for .NetStandard 2.1 and up, use xml format for lower. /// - RsaPem + RsaPem, + /// + /// Ed25519 keys credentials + /// + Ed25519 } } diff --git a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs index f6a291d..7a4112f 100644 --- a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs +++ b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs @@ -2,8 +2,12 @@ using CryptoExchange.Net.Converters.SystemTextJson; using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Objects; +#if NET8_0_OR_GREATER +using NSec.Cryptography; +#endif using System; using System.Collections.Generic; +using System.Linq; using System.Globalization; using System.Security.Cryptography; using System.Text; @@ -17,6 +21,11 @@ namespace CryptoExchange.Net.Authentication { internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider(); + /// + /// The supported credential types + /// + public abstract ApiCredentialsType[] SupportedCredentialTypes { get; } + /// /// Provided credentials /// @@ -27,6 +36,13 @@ namespace CryptoExchange.Net.Authentication /// protected byte[] _sBytes; +#if NET8_0_OR_GREATER + /// + /// The Ed25519 private key + /// + protected Key? Ed25519Key; +#endif + /// /// Get the API key of the current credentials /// @@ -45,6 +61,16 @@ namespace CryptoExchange.Net.Authentication if (credentials.Key == null || credentials.Secret == null) throw new ArgumentException("ApiKey/Secret needed"); + if (!SupportedCredentialTypes.Any(x => x == credentials.CredentialType)) + throw new ArgumentException($"Credential type {credentials.CredentialType} not supported"); + + if (credentials.CredentialType == ApiCredentialsType.Ed25519) + { +#if !NET8_0_OR_GREATER + throw new ArgumentException($"Credential type Ed25519 only supported on Net8.0 or newer"); +#endif + } + _credentials = credentials; _sBytes = Encoding.UTF8.GetBytes(credentials.Secret); } @@ -348,6 +374,36 @@ namespace CryptoExchange.Net.Authentication return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes); } + /// + /// Ed25519 sign the data + /// + public string SignEd25519(string data, SignOutputType? outputType = null) + => SignEd25519(Encoding.ASCII.GetBytes(data), outputType); + + /// + /// Ed25519 sign the data + /// + public string SignEd25519(byte[] data, SignOutputType? outputType = null) + { +#if NET8_0_OR_GREATER + if (Ed25519Key == null) + { + var key = _credentials.Secret! + .Replace("\n", "") + .Replace("-----BEGIN PRIVATE KEY-----", "") + .Replace("-----END PRIVATE KEY-----", "") + .Trim(); + var keyBytes = Convert.FromBase64String(key); + Ed25519Key = Key.Import(SignatureAlgorithm.Ed25519, keyBytes, KeyBlobFormat.PkixPrivateKey); + } + + var resultBytes = SignatureAlgorithm.Ed25519.Sign(Ed25519Key, data); + return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes); +#else + throw new InvalidOperationException(); +#endif + } + private RSA CreateRSA() { var rsa = RSA.Create(); diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index 864a3bf..041b40f 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -53,6 +53,7 @@ +