From 263d6d1e52901a1f53ff72f5c1845bf7ed55fd1f Mon Sep 17 00:00:00 2001 From: Simon Pearson Date: Sun, 31 Jan 2021 18:21:05 +0000 Subject: [PATCH] Added support for SHA384 and SHA512 hashing algorithms with RSASSA-PKCS1-v1_5 (#305) * Added RSAAlgorithm abstract base class which all RSA algorithms derive from * Added RS384Algorithm, RS512Algorithm * Refactored RSAlgorithmFactory * Updated, added tests * Bumped version to 8.0.0 --- src/JWT/Algorithms/HMACSHA256Algorithm.cs | 7 + src/JWT/Algorithms/HMACSHA384Algorithm.cs | 7 + src/JWT/Algorithms/HMACSHA512Algorithm.cs | 7 + src/JWT/Algorithms/IJwtAlgorithm.cs | 5 + src/JWT/Algorithms/JwtAlgorithmName.cs | 12 +- src/JWT/Algorithms/RS256Algorithm.cs | 70 +------- src/JWT/Algorithms/RS384Algorithm.cs | 51 ++++++ src/JWT/Algorithms/RS512Algorithm.cs | 51 ++++++ src/JWT/Algorithms/RSAlgorithm.cs | 120 +++++++++++++ src/JWT/Algorithms/RSAlgorithmFactory.cs | 85 +++++++++- src/JWT/JWT.csproj | 6 +- .../{ => Algorithms}/RS256AlgorithmTests.cs | 160 ++++++++++-------- .../Algorithms/RS384AlgorithmTests.cs | 34 ++++ .../Algorithms/RS512AlgorithmTests.cs | 34 ++++ .../Algorithms/RSAlgorithmFactoryTests.cs | 69 ++++++++ 15 files changed, 573 insertions(+), 145 deletions(-) create mode 100644 src/JWT/Algorithms/RS384Algorithm.cs create mode 100644 src/JWT/Algorithms/RS512Algorithm.cs create mode 100644 src/JWT/Algorithms/RSAlgorithm.cs rename tests/JWT.Tests.Common/{ => Algorithms}/RS256AlgorithmTests.cs (77%) create mode 100644 tests/JWT.Tests.Common/Algorithms/RS384AlgorithmTests.cs create mode 100644 tests/JWT.Tests.Common/Algorithms/RS512AlgorithmTests.cs create mode 100644 tests/JWT.Tests.Common/Algorithms/RSAlgorithmFactoryTests.cs diff --git a/src/JWT/Algorithms/HMACSHA256Algorithm.cs b/src/JWT/Algorithms/HMACSHA256Algorithm.cs index b3764a00b..f15ab2229 100644 --- a/src/JWT/Algorithms/HMACSHA256Algorithm.cs +++ b/src/JWT/Algorithms/HMACSHA256Algorithm.cs @@ -16,5 +16,12 @@ public byte[] Sign(byte[] key, byte[] bytesToSign) /// public string Name => JwtAlgorithmName.HS256.ToString(); + + public string HashAlgorithm => +#if NET35 || NET40 + HashAlgorithmName.SHA256; +#else + HashAlgorithmName.SHA256.Name; +#endif } } \ No newline at end of file diff --git a/src/JWT/Algorithms/HMACSHA384Algorithm.cs b/src/JWT/Algorithms/HMACSHA384Algorithm.cs index 57b6531e4..98f85426c 100644 --- a/src/JWT/Algorithms/HMACSHA384Algorithm.cs +++ b/src/JWT/Algorithms/HMACSHA384Algorithm.cs @@ -16,5 +16,12 @@ public byte[] Sign(byte[] key, byte[] bytesToSign) /// public string Name => JwtAlgorithmName.HS384.ToString(); + + public string HashAlgorithm => +#if NET35 || NET40 + HashAlgorithmName.SHA384; +#else + HashAlgorithmName.SHA384.Name; +#endif } } \ No newline at end of file diff --git a/src/JWT/Algorithms/HMACSHA512Algorithm.cs b/src/JWT/Algorithms/HMACSHA512Algorithm.cs index 2aa6c31fa..9797a6ef3 100644 --- a/src/JWT/Algorithms/HMACSHA512Algorithm.cs +++ b/src/JWT/Algorithms/HMACSHA512Algorithm.cs @@ -16,5 +16,12 @@ public byte[] Sign(byte[] key, byte[] bytesToSign) /// public string Name => JwtAlgorithmName.HS512.ToString(); + + public string HashAlgorithm => +#if NET35 || NET40 + HashAlgorithmName.SHA512; +#else + HashAlgorithmName.SHA512.Name; +#endif } } \ No newline at end of file diff --git a/src/JWT/Algorithms/IJwtAlgorithm.cs b/src/JWT/Algorithms/IJwtAlgorithm.cs index 1e9e10c28..eaefe4571 100644 --- a/src/JWT/Algorithms/IJwtAlgorithm.cs +++ b/src/JWT/Algorithms/IJwtAlgorithm.cs @@ -16,6 +16,11 @@ public interface IJwtAlgorithm /// Gets algorithm name. /// string Name { get; } + + /// + /// Gets name of the hashing algorithm (e.g. SHA-256/SHA-384/SHA-512). + /// + string HashAlgorithm { get; } } /// diff --git a/src/JWT/Algorithms/JwtAlgorithmName.cs b/src/JWT/Algorithms/JwtAlgorithmName.cs index 3c0ce73a8..f89b42957 100644 --- a/src/JWT/Algorithms/JwtAlgorithmName.cs +++ b/src/JWT/Algorithms/JwtAlgorithmName.cs @@ -23,6 +23,16 @@ public enum JwtAlgorithmName /// /// RSASSA-PKCS1-v1_5 using SHA-256 /// - RS256 + RS256, + + /// + /// RSASSA-PKCS1-v1_5 using SHA-384 + /// + RS384, + + /// + /// RSASSA-PKCS1-v1_5 using SHA-512 + /// + RS512 } } \ No newline at end of file diff --git a/src/JWT/Algorithms/RS256Algorithm.cs b/src/JWT/Algorithms/RS256Algorithm.cs index 70ebc59d2..b7e652d74 100644 --- a/src/JWT/Algorithms/RS256Algorithm.cs +++ b/src/JWT/Algorithms/RS256Algorithm.cs @@ -1,5 +1,4 @@ -using System; -using System.Security.Cryptography; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace JWT.Algorithms @@ -7,20 +6,16 @@ namespace JWT.Algorithms /// /// RSASSA-PKCS1-v1_5 using SHA-256 /// - public sealed class RS256Algorithm : IAsymmetricAlgorithm + public sealed class RS256Algorithm : RSAlgorithm { - private readonly RSA _publicKey; - private readonly RSA _privateKey; - /// /// Creates an instance of using the provided pair of public and private keys. /// /// The public key for verifying the data. /// The private key for signing the data. public RS256Algorithm(RSA publicKey, RSA privateKey) + : base(publicKey, privateKey) { - _publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); - _privateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); } /// @@ -31,9 +26,8 @@ public RS256Algorithm(RSA publicKey, RSA privateKey) /// /// The public key for verifying the data. public RS256Algorithm(RSA publicKey) + : base(publicKey) { - _publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); - _privateKey = null; } /// @@ -41,65 +35,17 @@ public RS256Algorithm(RSA publicKey) /// /// The certificate having a public key and an optional private key. public RS256Algorithm(X509Certificate2 cert) + : base(cert) { - _publicKey = GetPublicKey(cert) ?? throw new Exception("Certificate's PublicKey cannot be null."); - _privateKey = GetPrivateKey(cert); } /// - public string Name => JwtAlgorithmName.RS256.ToString(); - - /// - public byte[] Sign(byte[] key, byte[] bytesToSign) - { - if (_privateKey is null) - throw new InvalidOperationException("Can't sign data without private key"); + public override string Name => JwtAlgorithmName.RS256.ToString(); - return Sign(bytesToSign); - } - - /// - /// Signs the provided bytes. - /// - /// The bytes to sign. - /// The signed bytes. - public byte[] Sign(byte[] bytesToSign) => -#if NET35 || NET40 - ((RSACryptoServiceProvider)_privateKey).SignData(bytesToSign, HashAlgorithmName.SHA256); -#else - _privateKey.SignData(bytesToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); -#endif - - /// - public bool Verify(byte[] bytesToSign, byte[] signature) => #if NET35 || NET40 - ((RSACryptoServiceProvider)_publicKey).VerifyData(bytesToSign, HashAlgorithmName.SHA256, signature); + protected override string HashAlgorithmInternal => HashAlgorithmName.SHA256; #else - _publicKey.VerifyData(bytesToSign, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + protected override HashAlgorithmName HashAlgorithmInternal => HashAlgorithmName.SHA256; #endif - - private static RSA GetPrivateKey(X509Certificate2 cert) - { - if (cert is null) - throw new ArgumentNullException(nameof(cert)); - -#if NETSTANDARD1_3 - return cert.GetRSAPrivateKey(); -#else - return (RSA)cert.PrivateKey; -#endif - } - - private static RSA GetPublicKey(X509Certificate2 cert) - { - if (cert is null) - throw new ArgumentNullException(nameof(cert)); - -#if NETSTANDARD1_3 - return cert.GetRSAPublicKey(); -#else - return (RSA)cert.PublicKey.Key; -#endif - } } } diff --git a/src/JWT/Algorithms/RS384Algorithm.cs b/src/JWT/Algorithms/RS384Algorithm.cs new file mode 100644 index 000000000..18025ee7b --- /dev/null +++ b/src/JWT/Algorithms/RS384Algorithm.cs @@ -0,0 +1,51 @@ +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace JWT.Algorithms +{ + /// + /// RSASSA-PKCS1-v1_5 using SHA-384 + /// + public sealed class RS384Algorithm : RSAlgorithm + { + /// + /// Creates an instance of using the provided pair of public and private keys. + /// + /// The public key for verifying the data. + /// The private key for signing the data. + public RS384Algorithm(RSA publicKey, RSA privateKey) + : base(publicKey, privateKey) + { + } + + /// + /// Creates an instance of using the provided public key only. + /// + /// + /// An instance created using this constructor can only be used for verifying the data, not for signing it. + /// + /// The public key for verifying the data. + public RS384Algorithm(RSA publicKey) + : base(publicKey) + { + } + + /// + /// Creates an instance using the provided certificate. + /// + /// The certificate having a public key and an optional private key. + public RS384Algorithm(X509Certificate2 cert) + : base(cert) + { + } + + /// + public override string Name => JwtAlgorithmName.RS384.ToString(); + +#if NET35 || NET40 + protected override string HashAlgorithmInternal => HashAlgorithmName.SHA384; +#else + protected override HashAlgorithmName HashAlgorithmInternal => HashAlgorithmName.SHA384; +#endif + } +} diff --git a/src/JWT/Algorithms/RS512Algorithm.cs b/src/JWT/Algorithms/RS512Algorithm.cs new file mode 100644 index 000000000..ec9f64c73 --- /dev/null +++ b/src/JWT/Algorithms/RS512Algorithm.cs @@ -0,0 +1,51 @@ +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace JWT.Algorithms +{ + /// + /// RSASSA-PKCS1-v1_5 using SHA-512 + /// + public sealed class RS512Algorithm : RSAlgorithm + { + /// + /// Creates an instance of using the provided pair of public and private keys. + /// + /// The public key for verifying the data. + /// The private key for signing the data. + public RS512Algorithm(RSA publicKey, RSA privateKey) + : base(publicKey, privateKey) + { + } + + /// + /// Creates an instance of using the provided public key only. + /// + /// + /// An instance created using this constructor can only be used for verifying the data, not for signing it. + /// + /// The public key for verifying the data. + public RS512Algorithm(RSA publicKey) + : base(publicKey) + { + } + + /// + /// Creates an instance using the provided certificate. + /// + /// The certificate having a public key and an optional private key. + public RS512Algorithm(X509Certificate2 cert) + : base(cert) + { + } + + /// + public override string Name => JwtAlgorithmName.RS512.ToString(); + +#if NET35 || NET40 + protected override string HashAlgorithmInternal => HashAlgorithmName.SHA512; +#else + protected override HashAlgorithmName HashAlgorithmInternal => HashAlgorithmName.SHA512; +#endif + } +} diff --git a/src/JWT/Algorithms/RSAlgorithm.cs b/src/JWT/Algorithms/RSAlgorithm.cs new file mode 100644 index 000000000..bbea52442 --- /dev/null +++ b/src/JWT/Algorithms/RSAlgorithm.cs @@ -0,0 +1,120 @@ +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace JWT.Algorithms +{ + /// + /// RSASSA-PKCS1-v1_5 using SHA-256 + /// + public abstract class RSAlgorithm : IAsymmetricAlgorithm + { + private readonly RSA _publicKey; + private readonly RSA _privateKey; + + /// + /// Creates an instance of using the provided pair of public and private keys. + /// + /// The public key for verifying the data. + /// The private key for signing the data. + public RSAlgorithm(RSA publicKey, RSA privateKey) + { + _publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); + _privateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); + } + + /// + /// Creates an instance of using the provided public key only. + /// + /// + /// An instance created using this constructor can only be used for verifying the data, not for signing it. + /// + /// The public key for verifying the data. + public RSAlgorithm(RSA publicKey) + { + _publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); + _privateKey = null; + } + + /// + /// Creates an instance using the provided certificate. + /// + /// The certificate having a public key and an optional private key. + public RSAlgorithm(X509Certificate2 cert) + { + _publicKey = GetPublicKey(cert) ?? throw new Exception("Certificate's PublicKey cannot be null."); + _privateKey = GetPrivateKey(cert); + } + + /// + public abstract string Name { get; } + + public string HashAlgorithm => +#if NET35 || NET40 + HashAlgorithmInternal; +#else + HashAlgorithmInternal.Name; +#endif + + protected abstract +#if NET35 || NET40 + string +#else + HashAlgorithmName +#endif + HashAlgorithmInternal { get; } + + /// + public byte[] Sign(byte[] key, byte[] bytesToSign) + { + if (_privateKey is null) + throw new InvalidOperationException("Can't sign data without private key"); + + return Sign(bytesToSign); + } + + /// + /// Signs the provided bytes. + /// + /// The bytes to sign. + /// The signed bytes. + public byte[] Sign(byte[] bytesToSign) => +#if NET35 || NET40 + ((RSACryptoServiceProvider)_privateKey).SignData(bytesToSign, this.HashAlgorithmInternal); +#else + _privateKey.SignData(bytesToSign, HashAlgorithmInternal, RSASignaturePadding.Pkcs1); +#endif + + /// + public bool Verify(byte[] bytesToSign, byte[] signature) => +#if NET35 || NET40 + ((RSACryptoServiceProvider)_publicKey).VerifyData(bytesToSign, this.HashAlgorithmInternal, signature); +#else + _publicKey.VerifyData(bytesToSign, signature, HashAlgorithmInternal, RSASignaturePadding.Pkcs1); +#endif + + private static RSA GetPrivateKey(X509Certificate2 cert) + { + if (cert is null) + throw new ArgumentNullException(nameof(cert)); + +#if NETSTANDARD1_3 + return cert.GetRSAPrivateKey(); +#else + return (RSA)cert.PrivateKey; +#endif + } + + private static RSA GetPublicKey(X509Certificate2 cert) + { + if (cert is null) + throw new ArgumentNullException(nameof(cert)); + +#if NETSTANDARD1_3 + return cert.GetRSAPublicKey(); +#else + return (RSA)cert.PublicKey.Key; +#endif + } + } +} diff --git a/src/JWT/Algorithms/RSAlgorithmFactory.cs b/src/JWT/Algorithms/RSAlgorithmFactory.cs index 91f197c75..c95621fbb 100644 --- a/src/JWT/Algorithms/RSAlgorithmFactory.cs +++ b/src/JWT/Algorithms/RSAlgorithmFactory.cs @@ -7,39 +7,106 @@ namespace JWT.Algorithms /// public sealed class RSAlgorithmFactory : HMACSHAAlgorithmFactory { - private readonly Func _algFactory; + private readonly Func _certFactory; + private readonly RSA _publicKey; + private readonly RSA _privateKey; /// /// Creates an instance of the class using the provided . /// /// Func that returns which will be used to instantiate - public RSAlgorithmFactory(Func certFactory) => - _algFactory = () => new RS256Algorithm(certFactory()); + public RSAlgorithmFactory(Func certFactory) + { + _certFactory = certFactory ?? throw new ArgumentNullException(nameof(certFactory)); + } /// /// Creates an instance of using the provided public key only. /// /// The public key for verifying the data. - public RSAlgorithmFactory(RSA publicKey) => - _algFactory = () => new RS256Algorithm(publicKey); + public RSAlgorithmFactory(RSA publicKey) + { + _publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); + } /// /// Creates an instance of using the provided pair of public and private keys. /// /// The public key for verifying the data. /// The private key for signing the data. - public RSAlgorithmFactory(RSA publicKey, RSA privateKey) => - _algFactory = () => new RS256Algorithm(publicKey, privateKey); + public RSAlgorithmFactory(RSA publicKey, RSA privateKey) + : this(publicKey) + { + _privateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); + } protected override IJwtAlgorithm Create(JwtAlgorithmName algorithm) { switch (algorithm) { case JwtAlgorithmName.RS256: - return _algFactory(); + return CreateRS256Algorithm(); + case JwtAlgorithmName.RS384: + return CreateRS384Algorithm(); + case JwtAlgorithmName.RS512: + return CreateRS512Algorithm(); default: throw new NotSupportedException($"For algorithm {Enum.GetName(typeof(JwtAlgorithmName), algorithm)} please use the appropriate factory by implementing {nameof(IAlgorithmFactory)}"); } } + + private RS256Algorithm CreateRS256Algorithm() + { + if (_certFactory is object) + { + return new RS256Algorithm(_certFactory()); + } + if (_publicKey is object && _privateKey is object) + { + return new RS256Algorithm(_publicKey, _privateKey); + } + if (_publicKey is object) + { + return new RS256Algorithm(_publicKey); + } + + throw new InvalidOperationException("Can't create a new algorithm without a certificate factory, private key or public key"); + } + + private RS384Algorithm CreateRS384Algorithm() + { + if (_certFactory is object) + { + return new RS384Algorithm(_certFactory()); + } + if (_publicKey is object && _privateKey is object) + { + return new RS384Algorithm(_publicKey, _privateKey); + } + if (_publicKey is object) + { + return new RS384Algorithm(_publicKey); + } + + throw new InvalidOperationException("Can't create a new algorithm without a certificate factory, private key or public key"); + } + + private RS512Algorithm CreateRS512Algorithm() + { + if (_certFactory is object) + { + return new RS512Algorithm(_certFactory()); + } + if (_publicKey is object && _privateKey is object) + { + return new RS512Algorithm(_publicKey, _privateKey); + } + if (_publicKey is object) + { + return new RS512Algorithm(_publicKey); + } + + throw new InvalidOperationException("Can't create a new algorithm without a certificate factory, private key or public key"); + } } -} \ No newline at end of file +} diff --git a/src/JWT/JWT.csproj b/src/JWT/JWT.csproj index 1b980bcc7..6aa3705e0 100644 --- a/src/JWT/JWT.csproj +++ b/src/JWT/JWT.csproj @@ -18,9 +18,9 @@ Alexander Batishchev, John Sheehan, Michael Lehenbauer jwt;json CC0-1.0 - 7.3.1 - 7.0.0.0 - 7.0.0.0 + 8.0.0 + 8.0.0.0 + 8.0.0.0 JWT true diff --git a/tests/JWT.Tests.Common/RS256AlgorithmTests.cs b/tests/JWT.Tests.Common/Algorithms/RS256AlgorithmTests.cs similarity index 77% rename from tests/JWT.Tests.Common/RS256AlgorithmTests.cs rename to tests/JWT.Tests.Common/Algorithms/RS256AlgorithmTests.cs index 554816cf8..e01b7d015 100644 --- a/tests/JWT.Tests.Common/RS256AlgorithmTests.cs +++ b/tests/JWT.Tests.Common/Algorithms/RS256AlgorithmTests.cs @@ -1,70 +1,90 @@ -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using AutoFixture; -using FluentAssertions; -using JWT.Algorithms; -using JWT.Tests.Models; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace JWT.Tests -{ - [TestClass] - public class RS256AlgorithmTests - { - private static readonly Fixture _fixture = new Fixture(); - - [TestMethod] - public void Ctor_Should_Throw_Exception_When_PublicKey_Is_Null() - { - var privateKey = _fixture.Create(); - - Action action = - () => new RS256Algorithm(null, privateKey); - - action.Should() - .Throw("because asymmetric algorithm cannot be constructed without public key"); - } - - [TestMethod] - public void Ctor_Should_Throw_Exception_When_PrivateKey_Is_Null() - { - var publicKey = _fixture.Create(); - - Action action = - () => new RS256Algorithm(publicKey, null); - - action.Should() - .Throw("because asymmetric algorithm cannot be constructed without private key"); - } - - [TestMethod] - public void Sign_Should_Throw_Exception_When_PrivateKey_Is_Null() - { - var publicKey = _fixture.Create(); - var alg = new RS256Algorithm(publicKey); - - var bytesToSign = Array.Empty(); - - Action action = - () => alg.Sign(null, bytesToSign); - - action.Should() - .Throw("because asymmetric algorithm cannot sign data without private key"); - } - - [DataTestMethod] - [DataRow(TestData.ServerRsaPublicKey1)] - public void Ctor_Should_Not_Throw_Exception_When_Certificate_Has_No_PrivateKey(string publicKey) - { - var bytes = Encoding.ASCII.GetBytes(publicKey); - var certificate = new X509Certificate2(bytes); - - var algorithm = new RS256Algorithm(certificate); - - algorithm.Should() - .NotBeNull(); - } - } -} \ No newline at end of file +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using AutoFixture; +using FluentAssertions; +using JWT.Algorithms; +using JWT.Tests.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JWT.Tests.Algorithms +{ + [TestClass] + public class RS256AlgorithmTests + { + private static readonly Fixture _fixture = new Fixture(); + + [TestMethod] + public void Ctor_Should_Throw_Exception_When_PublicKey_Is_Null() + { + var privateKey = _fixture.Create(); + + Action action = + () => new RS256Algorithm(null, privateKey); + + action.Should() + .Throw("because asymmetric algorithm cannot be constructed without public key"); + } + + [TestMethod] + public void Ctor_Should_Throw_Exception_When_PrivateKey_Is_Null() + { + var publicKey = _fixture.Create(); + + Action action = + () => new RS256Algorithm(publicKey, null); + + action.Should() + .Throw("because asymmetric algorithm cannot be constructed without private key"); + } + + [TestMethod] + public void Sign_Should_Throw_Exception_When_PrivateKey_Is_Null() + { + var publicKey = _fixture.Create(); + var alg = new RS256Algorithm(publicKey); + + var bytesToSign = Array.Empty(); + + Action action = + () => alg.Sign(null, bytesToSign); + + action.Should() + .Throw("because asymmetric algorithm cannot sign data without private key"); + } + + [DataTestMethod] + [DataRow(TestData.ServerRsaPublicKey1)] + public void Ctor_Should_Not_Throw_Exception_When_Certificate_Has_No_PrivateKey(string publicKey) + { + var bytes = Encoding.ASCII.GetBytes(publicKey); + var certificate = new X509Certificate2(bytes); + + var algorithm = new RS256Algorithm(certificate); + + algorithm.Should() + .NotBeNull(); + } + + [TestMethod] + public void Name_Should_Be_RS256() + { + var publicKey = _fixture.Create(); + var alg = new RS256Algorithm(publicKey); + + alg.Name.Should() + .Be(JwtAlgorithmName.RS256.ToString()); + } + + [TestMethod] + public void HashAlgorithm_Should_Be_SHA256() + { + var publicKey = _fixture.Create(); + var alg = new RS256Algorithm(publicKey); + + alg.HashAlgorithm.Should() + .Be("SHA256"); + } + } +} diff --git a/tests/JWT.Tests.Common/Algorithms/RS384AlgorithmTests.cs b/tests/JWT.Tests.Common/Algorithms/RS384AlgorithmTests.cs new file mode 100644 index 000000000..46925056b --- /dev/null +++ b/tests/JWT.Tests.Common/Algorithms/RS384AlgorithmTests.cs @@ -0,0 +1,34 @@ +using System.Security.Cryptography; +using AutoFixture; +using FluentAssertions; +using JWT.Algorithms; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JWT.Tests.Algorithms +{ + [TestClass] + public class RS384AlgorithmTests + { + private static readonly Fixture _fixture = new Fixture(); + + [TestMethod] + public void Name_Should_Be_RS384() + { + var publicKey = _fixture.Create(); + var alg = new RS384Algorithm(publicKey); + + alg.Name.Should() + .Be(JwtAlgorithmName.RS384.ToString()); + } + + [TestMethod] + public void HashAlgorithm_Should_Be_SHA384() + { + var publicKey = _fixture.Create(); + var alg = new RS384Algorithm(publicKey); + + alg.HashAlgorithm.Should() + .Be("SHA384"); + } + } +} diff --git a/tests/JWT.Tests.Common/Algorithms/RS512AlgorithmTests.cs b/tests/JWT.Tests.Common/Algorithms/RS512AlgorithmTests.cs new file mode 100644 index 000000000..02859a3b2 --- /dev/null +++ b/tests/JWT.Tests.Common/Algorithms/RS512AlgorithmTests.cs @@ -0,0 +1,34 @@ +using System.Security.Cryptography; +using AutoFixture; +using FluentAssertions; +using JWT.Algorithms; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JWT.Tests.Algorithms +{ + [TestClass] + public class RS512AlgorithmTests + { + private static readonly Fixture _fixture = new Fixture(); + + [TestMethod] + public void Name_Should_Be_RS512() + { + var publicKey = _fixture.Create(); + var alg = new RS512Algorithm(publicKey); + + alg.Name.Should() + .Be(JwtAlgorithmName.RS512.ToString()); + } + + [TestMethod] + public void HashAlgorithm_Should_Be_SHA512() + { + var publicKey = _fixture.Create(); + var alg = new RS512Algorithm(publicKey); + + alg.HashAlgorithm.Should() + .Be("SHA512"); + } + } +} diff --git a/tests/JWT.Tests.Common/Algorithms/RSAlgorithmFactoryTests.cs b/tests/JWT.Tests.Common/Algorithms/RSAlgorithmFactoryTests.cs new file mode 100644 index 000000000..8a4ec59c8 --- /dev/null +++ b/tests/JWT.Tests.Common/Algorithms/RSAlgorithmFactoryTests.cs @@ -0,0 +1,69 @@ +using System.Security.Cryptography; +using AutoFixture; +using FluentAssertions; +using JWT.Algorithms; +using JWT.Builder; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JWT.Tests.Algorithms +{ + [TestClass] + public class RSAlgorithmFactoryTests + { + private static readonly Fixture _fixture = new Fixture(); + + [TestMethod] + public void Create_Should_Return_Instance_Of_RS256Algorithm_When_Algorithm_Specified_In_Jwt_Header_Is_RS256() + { + var publicKey = _fixture.Create(); + var factory = new RSAlgorithmFactory(publicKey); + var context = new JwtDecoderContext + { + Header = new JwtHeader + { + Algorithm = JwtAlgorithmName.RS256.ToString() + } + }; + var alg = factory.Create(context); + + alg.Should() + .BeOfType("because Create should return an instance of RS256Algorithm when the algorithm name in the header is 'RS256'"); + } + + [TestMethod] + public void Create_Should_Return_Instance_Of_RS384Algorithm_When_Algorithm_Specified_In_Jwt_Header_Is_RS384() + { + var publicKey = _fixture.Create(); + var factory = new RSAlgorithmFactory(publicKey); + var context = new JwtDecoderContext + { + Header = new JwtHeader + { + Algorithm = JwtAlgorithmName.RS384.ToString() + } + }; + var alg = factory.Create(context); + + alg.Should() + .BeOfType("because Create should return an instance of RS384Algorithm when the algorithm name in the header is 'RS256'"); + } + + [TestMethod] + public void Create_Should_Return_Instance_Of_RS512Algorithm_When_Algorithm_Specified_In_Jwt_Header_Is_RS512() + { + var publicKey = _fixture.Create(); + var factory = new RSAlgorithmFactory(publicKey); + var context = new JwtDecoderContext + { + Header = new JwtHeader + { + Algorithm = JwtAlgorithmName.RS512.ToString() + } + }; + var alg = factory.Create(context); + + alg.Should() + .BeOfType("because Create should return an instance of RS384Algorithm when the algorithm name in the header is 'RS256'"); + } + } +}