diff --git a/README.md b/README.md index d4c631a..524fd7e 100644 --- a/README.md +++ b/README.md @@ -987,7 +987,7 @@ using System.Security.Cryptography; using MaksIT.Core.Security.JWK; using var rsa = RSA.Create(2048); -var result = JwkGenerator.TryGenerateFromRCA(rsa, out var jwk, out var errorMessage); +var result = JwkGenerator.TryGenerateFromRSA(rsa, out var jwk, out var errorMessage); if (result) { // jwk contains KeyType, RsaExponent, RsaModulus @@ -1004,9 +1004,9 @@ else #### API ```csharp -public static bool TryGenerateFromRCA( +public static bool TryGenerateFromRSA( RSA rsa, - out Jwk? jwk, + [NotNullWhen(true)] out Jwk? jwk, [NotNullWhen(false)] out string? errorMessage ) ``` @@ -1068,7 +1068,7 @@ public static bool TryEncode( RSA rsa, Jwk jwk, JwsHeader protectedHeader, - out JwsMessage? message, + [NotNullWhen(true)]out JwsMessage? message, [NotNullWhen(false)] out string? errorMessage ) ``` @@ -1080,7 +1080,7 @@ public static bool TryEncode( Jwk jwk, JwsHeader protectedHeader, T? payload, - out JwsMessage? message, + [NotNullWhen(true)] out JwsMessage? message, [NotNullWhen(false)] out string? errorMessage ) ``` @@ -1153,7 +1153,7 @@ else ```csharp public static bool TryGetSha256Thumbprint( Jwk jwk, - out string? thumbprint, + [NotNullWhen(true)] out string? thumbprint, [NotNullWhen(false)] out string? errorMessage ) ``` @@ -1163,7 +1163,7 @@ public static bool TryGetSha256Thumbprint( public static bool TryGetKeyAuthorization( Jwk jwk, string token, - out string? keyAuthorization, + [NotNullWhen(true)] out string? keyAuthorization, [NotNullWhen(false)] out string? errorMessage ) ``` diff --git a/src/MaksIT.Core.Tests/Security/JWK/JwkGeneratorTests.cs b/src/MaksIT.Core.Tests/Security/JWK/JwkGeneratorTests.cs index 4afb469..8143902 100644 --- a/src/MaksIT.Core.Tests/Security/JWK/JwkGeneratorTests.cs +++ b/src/MaksIT.Core.Tests/Security/JWK/JwkGeneratorTests.cs @@ -4,67 +4,61 @@ using MaksIT.Core.Security.JWK; namespace MaksIT.Core.Tests.Security.JWK; -public class JwkGeneratorTests -{ - [Fact] - public void TryGenerateFromRCA_ValidRsa_ReturnsTrueAndJwk() - { - using var rsa = RSA.Create(2048); - var result = JwkGenerator.TryGenerateFromRCA(rsa, out var jwk, out var errorMessage); - Assert.True(result); - Assert.NotNull(jwk); - Assert.Null(errorMessage); - Assert.Equal(JwkKeyType.Rsa.Name, jwk!.KeyType); - Assert.False(string.IsNullOrEmpty(jwk.RsaExponent)); - Assert.False(string.IsNullOrEmpty(jwk.RsaModulus)); - } +public class JwkGeneratorTests { + [Fact] + public void TryGenerateFromRSA_ValidRsa_ReturnsTrueAndJwk() { + using var rsa = RSA.Create(2048); + var result = JwkGenerator.TryGenerateFromRSA(rsa, out var jwk, out var errorMessage); + Assert.True(result); + Assert.NotNull(jwk); + Assert.Null(errorMessage); + Assert.Equal(JwkKeyType.Rsa.Name, jwk!.KeyType); + Assert.False(string.IsNullOrEmpty(jwk.RsaExponent)); + Assert.False(string.IsNullOrEmpty(jwk.RsaModulus)); + } - [Fact] - public void TryGenerateFromRCA_MissingExponentOrModulus_ReturnsFalseAndError() - { - using var rsa = RSA.Create(); - // ExportParameters returns valid values, so we simulate missing exponent/modulus by mocking - // Instead, test with a custom RSA implementation that throws - var fakeRsa = new FakeRsaMissingParams(); - var result = JwkGenerator.TryGenerateFromRCA(fakeRsa, out var jwk, out var errorMessage); - Assert.False(result); - Assert.Null(jwk); - Assert.Contains("missing exponent or modulus", errorMessage); - } + [Fact] + public void TryGenerateFromRSA_MissingExponentOrModulus_ReturnsFalseAndError() { + using var rsa = RSA.Create(); + // ExportParameters returns valid values, so we simulate missing exponent/modulus by mocking + // Instead, test with a custom RSA implementation that throws + var fakeRsa = new FakeRsaMissingParams(); + var result = JwkGenerator.TryGenerateFromRSA(fakeRsa, out var jwk, out var errorMessage); + Assert.False(result); + Assert.Null(jwk); + Assert.Contains("missing exponent or modulus", errorMessage); + } - [Fact] - public void TryGenerateFromRCA_ExportParametersThrows_ReturnsFalseAndError() - { - var fakeRsa = new FakeRsaThrows(); - var result = JwkGenerator.TryGenerateFromRCA(fakeRsa, out var jwk, out var errorMessage); - Assert.False(result); - Assert.Null(jwk); - Assert.Contains("ExportParameters failed", errorMessage); - } + [Fact] + public void TryGenerateFromRSA_ExportParametersThrows_ReturnsFalseAndError() { + var fakeRsa = new FakeRsaThrows(); + var result = JwkGenerator.TryGenerateFromRSA(fakeRsa, out var jwk, out var errorMessage); + Assert.False(result); + Assert.Null(jwk); + Assert.Contains("ExportParameters failed", errorMessage); + } - private class FakeRsaMissingParams : RSA - { - public override RSAParameters ExportParameters(bool includePrivateParameters) - => new RSAParameters { Exponent = null, Modulus = null }; - // ...other abstract members throw NotImplementedException - public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); - public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); - public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); - public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); - public override void ImportParameters(RSAParameters parameters) => throw new NotImplementedException(); - protected override void Dispose(bool disposing) { } - } + private class FakeRsaMissingParams : RSA { + public override RSAParameters ExportParameters(bool includePrivateParameters) + => new RSAParameters { Exponent = null, Modulus = null }; + // ...other abstract members throw NotImplementedException + public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); + public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); + public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); + public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); + public override void ImportParameters(RSAParameters parameters) => throw new NotImplementedException(); + protected override void Dispose(bool disposing) { } + } - private class FakeRsaThrows : RSA - { - public override RSAParameters ExportParameters(bool includePrivateParameters) - => throw new Exception("ExportParameters failed"); - // ...other abstract members throw NotImplementedException - public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); - public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); - public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); - public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); - public override void ImportParameters(RSAParameters parameters) => throw new NotImplementedException(); - protected override void Dispose(bool disposing) { } - } + private class FakeRsaThrows : RSA { + public override RSAParameters ExportParameters(bool includePrivateParameters) + => throw new Exception("ExportParameters failed"); + // ...other abstract members throw NotImplementedException + public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); + public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); + public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); + public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) => throw new NotImplementedException(); + public override void ImportParameters(RSAParameters parameters) => throw new NotImplementedException(); + protected override void Dispose(bool disposing) { } + } } diff --git a/src/MaksIT.Core.Tests/Security/JWK/JwkThumbprintUtilityTests.cs b/src/MaksIT.Core.Tests/Security/JWK/JwkThumbprintUtilityTests.cs index eccf924..fd99ed2 100644 --- a/src/MaksIT.Core.Tests/Security/JWK/JwkThumbprintUtilityTests.cs +++ b/src/MaksIT.Core.Tests/Security/JWK/JwkThumbprintUtilityTests.cs @@ -5,59 +5,54 @@ using MaksIT.Core.Security.JWK; namespace MaksIT.Core.Tests.Security.JWK; -public class JwkThumbprintUtilityTests -{ - [Fact] - public void TryGetSha256Thumbprint_ValidRsaJwk_ReturnsTrueAndThumbprint() - { - using var rsa = RSA.Create(2048); - var genResult = JwkGenerator.TryGenerateFromRCA(rsa, out var jwk, out var genError); - Assert.True(genResult); - Assert.NotNull(jwk); - var result = JwkThumbprintUtility.TryGetSha256Thumbprint(jwk!, out var thumbprint, out var error); - Assert.True(result); - Assert.False(string.IsNullOrEmpty(thumbprint)); - Assert.Null(error); - // Should be base64url encoded and of expected length (SHA256 =32 bytes) - var decoded = Base64UrlUtility.Decode(thumbprint!); - Assert.Equal(32, decoded.Length); - } +public class JwkThumbprintUtilityTests { + [Fact] + public void TryGetSha256Thumbprint_ValidRsaJwk_ReturnsTrueAndThumbprint() { + using var rsa = RSA.Create(2048); + var genResult = JwkGenerator.TryGenerateFromRSA(rsa, out var jwk, out var genError); + Assert.True(genResult); + Assert.NotNull(jwk); + var result = JwkThumbprintUtility.TryGetSha256Thumbprint(jwk!, out var thumbprint, out var error); + Assert.True(result); + Assert.False(string.IsNullOrEmpty(thumbprint)); + Assert.Null(error); + // Should be base64url encoded and of expected length (SHA256 =32 bytes) + var decoded = Base64UrlUtility.Decode(thumbprint!); + Assert.Equal(32, decoded.Length); + } - [Fact] - public void TryGetSha256Thumbprint_NullExponentOrModulus_ReturnsFalseAndError() - { - var jwk = new Jwk { RsaExponent = null, RsaModulus = null }; - var result = JwkThumbprintUtility.TryGetSha256Thumbprint(jwk, out var thumbprint, out var error); - Assert.False(result); - Assert.Null(thumbprint); - Assert.Contains("exponent or modulus", error); - } + [Fact] + public void TryGetSha256Thumbprint_NullExponentOrModulus_ReturnsFalseAndError() { + var jwk = new Jwk { RsaExponent = null, RsaModulus = null }; + var result = JwkThumbprintUtility.TryGetSha256Thumbprint(jwk, out var thumbprint, out var error); + Assert.False(result); + Assert.Null(thumbprint); + Assert.Contains("exponent or modulus", error); + } - [Fact] - public void TryGetKeyAuthorization_ValidJwk_ReturnsTrueAndKeyAuthorization() - { - using var rsa = RSA.Create(2048); - var genResult = JwkGenerator.TryGenerateFromRCA(rsa, out var jwk, out var genError); - Assert.True(genResult); - Assert.NotNull(jwk); - var token = "test-token"; - var result = JwkThumbprintUtility.TryGetKeyAuthorization(jwk!, token, out var keyAuth, out var error); - Assert.True(result); - Assert.Null(error); - Assert.StartsWith(token + ".", keyAuth); - var parts = keyAuth!.Split('.'); - Assert.Equal(2, parts.Length); - Assert.False(string.IsNullOrEmpty(parts[1])); - } + [Fact] + public void TryGetKeyAuthorization_ValidJwk_ReturnsTrueAndKeyAuthorization() { + using var rsa = RSA.Create(2048); + var genResult = JwkGenerator.TryGenerateFromRSA(rsa, out var jwk, out var genError); + Assert.True(genResult); + Assert.NotNull(jwk); + var token = "test-token"; + var result = JwkThumbprintUtility.TryGetKeyAuthorization(jwk!, token, out var keyAuth, out var error); + Assert.True(result); + Assert.Null(error); + Assert.StartsWith(token + ".", keyAuth); + var parts = keyAuth!.Split('.'); + Assert.Equal(2, parts.Length); + Assert.False(string.IsNullOrEmpty(parts[1])); + } - [Fact] - public void TryGetKeyAuthorization_NullExponentOrModulus_ReturnsFalseAndError() - { - var jwk = new Jwk { RsaExponent = null, RsaModulus = null }; - var token = "test-token"; - var result = JwkThumbprintUtility.TryGetKeyAuthorization(jwk, token, out var keyAuth, out var error); - Assert.False(result); - Assert.Null(keyAuth); - Assert.Contains("Failed to compute thumbprint", error); - } + [Fact] + public void TryGetKeyAuthorization_NullExponentOrModulus_ReturnsFalseAndError() { + var jwk = new Jwk { RsaExponent = null, RsaModulus = null }; + var token = "test-token"; + var result = JwkThumbprintUtility.TryGetKeyAuthorization(jwk, token, out var keyAuth, out var error); + Assert.False(result); + Assert.Null(keyAuth); + Assert.Contains("Failed to compute thumbprint", error); + } } diff --git a/src/MaksIT.Core.Tests/Security/JwsGeneratorTests.cs b/src/MaksIT.Core.Tests/Security/JwsGeneratorTests.cs index a47783e..90b4a19 100644 --- a/src/MaksIT.Core.Tests/Security/JwsGeneratorTests.cs +++ b/src/MaksIT.Core.Tests/Security/JwsGeneratorTests.cs @@ -8,10 +8,9 @@ namespace MaksIT.Core.Tests.Security; public class JwsGeneratorTests { [Fact] - public void TryEncode_ValidRsaAndJwk_ReturnsTrueAndMessage() - { + public void TryEncode_ValidRsaAndJwk_ReturnsTrueAndMessage() { using var rsa = RSA.Create(2048); - var jwkResult = JwkGenerator.TryGenerateFromRCA(rsa, out var jwk, out var jwkError); + var jwkResult = JwkGenerator.TryGenerateFromRSA(rsa, out var jwk, out var jwkError); Assert.True(jwkResult); Assert.NotNull(jwk); var header = new JwsHeader(); @@ -24,10 +23,9 @@ public class JwsGeneratorTests { } [Fact] - public void TryEncode_WithPayload_ReturnsEncodedPayload() - { + public void TryEncode_WithPayload_ReturnsEncodedPayload() { using var rsa = RSA.Create(2048); - var jwkResult = JwkGenerator.TryGenerateFromRCA(rsa, out var jwk, out var jwkError); + var jwkResult = JwkGenerator.TryGenerateFromRSA(rsa, out var jwk, out var jwkError); Assert.True(jwkResult); Assert.NotNull(jwk); var header = new JwsHeader(); @@ -43,8 +41,7 @@ public class JwsGeneratorTests { } [Fact] - public void TryEncode_InvalidRsa_ReturnsFalseAndError() - { + public void TryEncode_InvalidRsa_ReturnsFalseAndError() { var fakeRsa = new FakeRsaThrows(); var jwk = new Jwk { KeyType = JwkKeyType.Rsa.Name }; var header = new JwsHeader(); @@ -55,8 +52,7 @@ public class JwsGeneratorTests { } [Fact] - public void TryEncode_JwkWithKeyId_SetsHeaderKid() - { + public void TryEncode_JwkWithKeyId_SetsHeaderKid() { using var rsa = RSA.Create(2048); var jwk = new Jwk { KeyType = JwkKeyType.Rsa.Name, KeyId = "my-key-id" }; var header = new JwsHeader(); @@ -70,8 +66,7 @@ public class JwsGeneratorTests { } [Fact] - public void TryEncode_JwkWithoutKeyId_SetsHeaderJwk() - { + public void TryEncode_JwkWithoutKeyId_SetsHeaderJwk() { using var rsa = RSA.Create(2048); var jwk = new Jwk { KeyType = JwkKeyType.Rsa.Name }; var header = new JwsHeader(); @@ -83,8 +78,7 @@ public class JwsGeneratorTests { Assert.Contains("jwk", protectedJson); } - private class FakeRsaThrows : RSA - { + private class FakeRsaThrows : RSA { public override RSAParameters ExportParameters(bool includePrivateParameters) => throw new Exception("ExportParameters failed"); public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) => throw new NotImplementedException(); diff --git a/src/MaksIT.Core/MaksIT.Core.csproj b/src/MaksIT.Core/MaksIT.Core.csproj index 4a924ee..3f168fd 100644 --- a/src/MaksIT.Core/MaksIT.Core.csproj +++ b/src/MaksIT.Core/MaksIT.Core.csproj @@ -8,7 +8,7 @@ MaksIT.Core - 1.5.7 + 1.5.8 Maksym Sadovnychyy MAKS-IT MaksIT.Core diff --git a/src/MaksIT.Core/Security/JWK/JwkGenerator.cs b/src/MaksIT.Core/Security/JWK/JwkGenerator.cs index 87bb223..5731e60 100644 --- a/src/MaksIT.Core/Security/JWK/JwkGenerator.cs +++ b/src/MaksIT.Core/Security/JWK/JwkGenerator.cs @@ -8,8 +8,9 @@ namespace MaksIT.Core.Security.JWK; /// Provides utilities for JWK (JSON Web Key) operations, including RFC7638 thumbprint computation and key generation. /// public static class JwkGenerator { - public static bool TryGenerateFromRCA( + public static bool TryGenerateFromRSA( RSA rsa, + [NotNullWhen(true)] out Jwk? jwk, [NotNullWhen(false)] out string? errorMessage ) { diff --git a/src/MaksIT.Core/Security/JWK/JwkThumbprintUtility.cs b/src/MaksIT.Core/Security/JWK/JwkThumbprintUtility.cs index 3e1892b..16907d8 100644 --- a/src/MaksIT.Core/Security/JWK/JwkThumbprintUtility.cs +++ b/src/MaksIT.Core/Security/JWK/JwkThumbprintUtility.cs @@ -13,7 +13,7 @@ public static class JwkThumbprintUtility { public static bool TryGetKeyAuthorization( Jwk jwk, string token, - out string? keyAuthorization, + [NotNullWhen(true)] out string? keyAuthorization, [NotNullWhen(false)] out string? errorMessage ) { keyAuthorization = null; @@ -32,7 +32,7 @@ public static class JwkThumbprintUtility { /// public static bool TryGetSha256Thumbprint( Jwk jwk, - out string? thumbprint, + [NotNullWhen(true)] out string? thumbprint, [NotNullWhen(false)] out string? errorMessage ) { thumbprint = null; diff --git a/src/MaksIT.Core/Security/JWS/JwsGenerator.cs b/src/MaksIT.Core/Security/JWS/JwsGenerator.cs index 06818b7..1159c0f 100644 --- a/src/MaksIT.Core/Security/JWS/JwsGenerator.cs +++ b/src/MaksIT.Core/Security/JWS/JwsGenerator.cs @@ -12,7 +12,7 @@ public static class JwsGenerator { RSA rsa, Jwk jwk, JwsHeader protectedHeader, - out JwsMessage? message, + [NotNullWhen(true)] out JwsMessage? message, [NotNullWhen(false)] out string? errorMessage ) => TryEncode(rsa, jwk, protectedHeader, null, out message, out errorMessage); @@ -22,7 +22,7 @@ public static class JwsGenerator { Jwk jwk, JwsHeader protectedHeader, T? payload, - out JwsMessage? message, + [NotNullWhen(true)] out JwsMessage? message, [NotNullWhen(false)] out string? errorMessage ) { try {