diff --git a/CryptoExchange.Net/Authentication/ApiCredentials.cs b/CryptoExchange.Net/Authentication/ApiCredentials.cs index 416b57f..9098eef 100644 --- a/CryptoExchange.Net/Authentication/ApiCredentials.cs +++ b/CryptoExchange.Net/Authentication/ApiCredentials.cs @@ -5,6 +5,11 @@ /// public abstract class ApiCredentials { + /// + /// Validate the API credentials + /// + public abstract void Validate(); + /// /// Copy the credentials /// diff --git a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs index 0d26a47..a78e091 100644 --- a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs +++ b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs @@ -453,43 +453,33 @@ namespace CryptoExchange.Net.Authentication /// public abstract class AuthenticationProvider : AuthenticationProvider + where TApiCredentials: ApiCredentials { /// /// API credentials used for signing requests /// public TApiCredentials ApiCredentials { get; set; } - public CredentialPair? Credential { get; set; } - /// /// ctor /// protected AuthenticationProvider(TApiCredentials credentials) { - ApiCredentials = credentials; - } - - /// - /// ctor - /// - protected AuthenticationProvider(TApiCredentials credentials, CredentialPair? credentialPair) - { - if (credentialPair == null) - throw new ArgumentNullException(nameof(credentialPair), $"Credential pair needed but not provided"); + credentials.Validate(); ApiCredentials = credentials; - Credential = credentialPair; } } /// public abstract class AuthenticationProvider : AuthenticationProvider + where TApiCredentials : ApiCredentials where TCredentialType : CredentialPair { /// /// The specific credential type used for signing requests. /// - public new TCredentialType Credential => (TCredentialType)base.Credential!; + public TCredentialType Credential { get; } /// public override string Key => Credential.Key; @@ -497,8 +487,12 @@ namespace CryptoExchange.Net.Authentication /// /// ctor /// - protected AuthenticationProvider(TApiCredentials credentials, TCredentialType? credential) : base(credentials, credential) + protected AuthenticationProvider(TApiCredentials credentials, TCredentialType? credential) : base(credentials) { + if (credential == null) + throw new ArgumentException("Missing required credentials"); + + Credential = credential; } /// diff --git a/CryptoExchange.Net/Authentication/CredentialPair.cs b/CryptoExchange.Net/Authentication/CredentialPair.cs index 26f6228..e7b135a 100644 --- a/CryptoExchange.Net/Authentication/CredentialPair.cs +++ b/CryptoExchange.Net/Authentication/CredentialPair.cs @@ -2,6 +2,8 @@ using System.Security.Cryptography; using System.Text; +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + namespace CryptoExchange.Net.Authentication { /// @@ -14,6 +16,11 @@ namespace CryptoExchange.Net.Authentication /// public string Key { get; set; } + /// + /// ctor + /// + public CredentialPair() { } + /// /// ctor /// @@ -21,6 +28,15 @@ namespace CryptoExchange.Net.Authentication { Key = key; } + + /// + /// Validate the API credential + /// + public override void Validate() + { + if (string.IsNullOrEmpty(Key)) + throw new ArgumentException("Key unset", nameof(Key)); + } } /// @@ -34,12 +50,11 @@ namespace CryptoExchange.Net.Authentication /// Api key public ApiKeyCredential(string key) : base(key) { - if (string.IsNullOrEmpty(key)) - throw new ArgumentException("Key can't be null/empty"); } /// public override ApiCredentials Copy() => new ApiKeyCredential(Key); + } /// @@ -53,24 +68,22 @@ namespace CryptoExchange.Net.Authentication /// API secret /// public string Secret { get; set; } + /// - /// Passphrase, not needed on all exchanges + /// ctor /// - public string? Pass { get; set; } + public HMACCredential() + { + } /// /// ctor /// /// Api key/label /// Api secret - /// Optional passphrase - public HMACCredential(string key, string secret, string? pass = null) : base(key) + public HMACCredential(string key, string secret) : base(key) { - if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(secret)) - throw new ArgumentException("Key and secret can't be null/empty"); - Secret = secret; - Pass = pass; } /// @@ -83,7 +96,56 @@ namespace CryptoExchange.Net.Authentication } /// - public override ApiCredentials Copy() => new HMACCredential(Key, Secret, Pass); + public override ApiCredentials Copy() => new HMACCredential(Key, Secret); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(Secret)) + throw new ArgumentException("Secret unset", nameof(Secret)); + } + } + + /// + /// HMAC credentials + /// + public class HMACPassCredential : HMACCredential + { + /// + /// Passphrase + /// + public string Pass { get; set; } + + /// + /// ctor + /// + public HMACPassCredential() + { + } + + /// + /// ctor + /// + /// Api key/label + /// Api secret + /// Passphrase + public HMACPassCredential(string key, string secret, string pass) : base(key, secret) + { + Pass = pass; + } + + + /// + public override ApiCredentials Copy() => new HMACPassCredential(Key, Secret, Pass); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(Pass)) + throw new ArgumentException("Pass unset", nameof(Pass)); + } } /// @@ -95,10 +157,54 @@ namespace CryptoExchange.Net.Authentication /// Private key /// public string PrivateKey { get; set; } + + /// + /// ctor + /// + public RSACredential() + { + } + + /// + /// ctor + /// + /// Public key + /// Private key + public RSACredential(string key, string privateKey) : base(key) + { + PrivateKey = privateKey; + } + + /// + /// Get RSA signer + /// + public abstract RSA GetSigner(); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(PrivateKey)) + throw new ArgumentException("PrivateKey unset", nameof(PrivateKey)); + } + } + + /// + /// RSA credentials + /// + public abstract class RSAPassCredential : RSACredential + { /// /// Passphrase /// - public string? Pass { get; set; } + public string Pass { get; set; } + + /// + /// ctor + /// + public RSAPassCredential() + { + } /// /// ctor @@ -106,19 +212,19 @@ namespace CryptoExchange.Net.Authentication /// Public key /// Private key /// Passphrase - public RSACredential(string key, string privateKey, string? pass = null) : base(key) + public RSAPassCredential(string key, string privateKey, string pass) : base(key, privateKey) { - if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(privateKey)) - throw new ArgumentException("Public and private key can't be null/empty"); - PrivateKey = privateKey; Pass = pass; } - /// - /// Get RSA signer - /// - public abstract RSA GetSigner(); + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(Pass)) + throw new ArgumentException("Pass unset", nameof(Pass)); + } } #if NETSTANDARD2_1_OR_GREATER || NET7_0_OR_GREATER @@ -127,13 +233,19 @@ namespace CryptoExchange.Net.Authentication /// public class RSAPemCredential : RSACredential { + /// + /// ctor + /// + public RSAPemCredential() + { + } + /// /// ctor /// /// Public key /// Private key - /// Passphrase - public RSAPemCredential(string key, string privateKey, string? passphrase = null) : base(key, privateKey, passphrase) + public RSAPemCredential(string key, string privateKey) : base(key, privateKey) { } @@ -156,7 +268,59 @@ namespace CryptoExchange.Net.Authentication } /// - public override ApiCredentials Copy() => new RSAPemCredential(Key, PrivateKey, Pass); + public override ApiCredentials Copy() => new RSAPemCredential(Key, PrivateKey); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(PrivateKey)) + throw new ArgumentException("PrivateKey unset", nameof(PrivateKey)); + } + } + + /// + /// RSA PEM/Base64 credentials + /// + public class RSAPemPassCredential : RSAPassCredential + { + /// + /// ctor + /// + public RSAPemPassCredential() + { + } + + /// + /// ctor + /// + /// Api key/label + /// Api secret + /// Passphrase + public RSAPemPassCredential(string key, string privateKey, string pass) : base(key, privateKey, pass) + { + } + + /// + /// Get RSA signer + /// + public override RSA GetSigner() + { + var rsa = RSA.Create(); + var key = PrivateKey! + .Replace("\n", "") + .Replace("-----BEGIN PRIVATE KEY-----", "") + .Replace("-----END PRIVATE KEY-----", "") + .Trim(); + rsa.ImportPkcs8PrivateKey(Convert.FromBase64String( + key) + , out _); + + return rsa; + } + + /// + public override ApiCredentials Copy() => new RSAPemPassCredential(Key, PrivateKey, Pass); } #endif @@ -165,13 +329,19 @@ namespace CryptoExchange.Net.Authentication /// public class RSAXmlCredential : RSACredential { + /// + /// ctor + /// + public RSAXmlCredential() + { + } + /// /// ctor /// /// Public key /// Private key - /// Passphrase - public RSAXmlCredential(string key, string privateKey, string? passphrase = null) : base(key, privateKey, passphrase) + public RSAXmlCredential(string key, string privateKey) : base(key, privateKey) { } @@ -186,7 +356,43 @@ namespace CryptoExchange.Net.Authentication } /// - public override ApiCredentials Copy() => new RSAXmlCredential(Key, PrivateKey, Pass); + public override ApiCredentials Copy() => new RSAXmlCredential(Key, PrivateKey); + } + + /// + /// RSA XML credentials + /// + public class RSAXmlPassCredential : RSAPassCredential + { + /// + /// ctor + /// + public RSAXmlPassCredential() + { + } + + /// + /// ctor + /// + /// Api key/label + /// Api secret + /// Passphrase + public RSAXmlPassCredential(string key, string privateKey, string pass) : base(key, privateKey, pass) + { + } + + /// + /// Get RSA signer + /// + public override RSA GetSigner() + { + var rsa = RSA.Create(); + rsa.FromXmlString(PrivateKey); + return rsa; + } + + /// + public override ApiCredentials Copy() => new RSAXmlPassCredential(Key, PrivateKey, Pass); } #if NET8_0_OR_GREATER @@ -201,21 +407,22 @@ namespace CryptoExchange.Net.Authentication /// Private key /// public string PrivateKey { get; set; } + /// - /// Passphrase + /// ctor /// - public string? Pass { get; set; } + public Ed25519Credential() + { + } /// /// ctor /// /// Public key /// Private key - /// Passphrase - public Ed25519Credential(string key, string privateKey, string? pass = null) : base(key) + public Ed25519Credential(string key, string privateKey) : base(key) { PrivateKey = privateKey; - Pass = pass; } /// @@ -237,7 +444,55 @@ namespace CryptoExchange.Net.Authentication } /// - public override ApiCredentials Copy() => new Ed25519Credential(Key, PrivateKey, Pass); + public override ApiCredentials Copy() => new Ed25519Credential(Key, PrivateKey); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(PrivateKey)) + throw new ArgumentException("PrivateKey unset", nameof(PrivateKey)); + } + } + + /// + /// Ed25519 credentials + /// + public class Ed25519PassCredential : Ed25519Credential + { + /// + /// Passphrase + /// + public string Pass { get; set; } + + /// + /// ctor + /// + public Ed25519PassCredential() + { + } + + /// + /// ctor + /// + /// Api key/label + /// Private key + /// Passphrase + public Ed25519PassCredential(string key, string privateKey, string pass) : base(key, privateKey) + { + Pass = pass; + } + + /// + public override ApiCredentials Copy() => new Ed25519PassCredential(Key, PrivateKey, Pass); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(Pass)) + throw new ArgumentException("Pass unset", nameof(Pass)); + } } #endif @@ -250,24 +505,73 @@ namespace CryptoExchange.Net.Authentication /// Private key /// public string PrivateKey { get; set; } + /// - /// Passphrase + /// ctor /// - public string? Pass { get; set; } + public ECDsaCredential() + { + } /// /// ctor /// /// Public key /// Private key - /// Passphrase - public ECDsaCredential(string key, string privateKey, string? pass = null) : base(key) + public ECDsaCredential(string key, string privateKey) : base(key) { PrivateKey = privateKey; + } + + /// + public override ApiCredentials Copy() => new ECDsaCredential(Key, PrivateKey); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(PrivateKey)) + throw new ArgumentException("PrivateKey unset", nameof(PrivateKey)); + } + } + + /// + /// ECDsa credentials + /// + public class ECDsaPassCredential : ECDsaCredential + { + /// + /// Passphrase + /// + public string Pass { get; set; } + + /// + /// ctor + /// + public ECDsaPassCredential() + { + } + + /// + /// ctor + /// + /// Api key/label + /// Private key + /// Passphrase + public ECDsaPassCredential(string key, string privateKey, string pass) : base(key, privateKey) + { Pass = pass; } /// - public override ApiCredentials Copy() => new ECDsaCredential(Key, PrivateKey, Pass); + public override ApiCredentials Copy() => new ECDsaPassCredential(Key, PrivateKey, Pass); + + /// + public override void Validate() + { + base.Validate(); + if (string.IsNullOrEmpty(Pass)) + throw new ArgumentException("Pass unset", nameof(Pass)); + } } }