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 @@
+