Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for JSON Web key sets #489

Open
wants to merge 49 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9c40aa9
add JSON Web Key definitions
CADBIMDeveloper Jan 27, 2024
79ea39a
add Json Web keys collection
CADBIMDeveloper Jan 27, 2024
923f90c
add a new algorithm factory
CADBIMDeveloper Jan 27, 2024
72613d2
add JWKS support on .net framework
CADBIMDeveloper Feb 4, 2024
0f1d3bb
add a test
CADBIMDeveloper Feb 4, 2024
bcade65
add a test
CADBIMDeveloper Feb 4, 2024
9a76799
incapsulate creating algorithm from specific JWK
CADBIMDeveloper Feb 4, 2024
3b6b542
add EC algorithm specific properties
CADBIMDeveloper Feb 4, 2024
c64bd7b
implement elliptic curve algorithms creation
CADBIMDeveloper Feb 4, 2024
06be06c
add a test
CADBIMDeveloper Feb 4, 2024
fc30b3f
add symmetric key property of the JWK
CADBIMDeveloper Feb 4, 2024
20e3d51
add ISymmetircAlgorithm interface
CADBIMDeveloper Feb 4, 2024
528bc73
move symmetric algorithm validation
CADBIMDeveloper Feb 4, 2024
31ea350
hmacs sha algorithms could have built-in key
CADBIMDeveloper Feb 4, 2024
f74489f
algorithm factory reads the key from Json Web Key
CADBIMDeveloper Feb 4, 2024
e8d36db
add a property to symetrical algorithm interface
CADBIMDeveloper Feb 4, 2024
173a5aa
use symmetric key from the JWKS
CADBIMDeveloper Feb 4, 2024
28afdb6
add new Jwt Builder methods
CADBIMDeveloper Feb 4, 2024
be352ca
cleanup the test
CADBIMDeveloper Feb 4, 2024
4ad94e9
extend Jwt Builder with an ability to set
CADBIMDeveloper Feb 4, 2024
61cfe50
JWT can encode with symmetrical key from the JWKS
CADBIMDeveloper Feb 4, 2024
25ce0e1
add JwtWebKey properies summaries
CADBIMDeveloper Feb 4, 2024
ab3b341
add elliptic curve private key property of the JWK
CADBIMDeveloper Feb 4, 2024
67a88fa
allow to encode with a key from Json Web Key set
CADBIMDeveloper Feb 4, 2024
de808ee
adjust the test and found a bug
CADBIMDeveloper Feb 4, 2024
5b3c309
rename the property
CADBIMDeveloper Feb 4, 2024
eac7e11
extend JWK definition
CADBIMDeveloper Feb 4, 2024
564d4e1
allow to encode with a key from Json Web Key set with RSA algorithm
CADBIMDeveloper Feb 4, 2024
ea878af
polish - remove code duplications
CADBIMDeveloper Feb 4, 2024
8848445
add a note to the changelog
CADBIMDeveloper Feb 4, 2024
3f08528
bump version
CADBIMDeveloper Feb 4, 2024
47e9234
Merge branch 'main' into jwks
abatishchev Apr 29, 2024
7e09d4b
fix build
CADBIMDeveloper May 1, 2024
1e8cd93
Merge branch 'main' into jwks
CADBIMDeveloper May 1, 2024
e5f9f61
Update JwtWebKeyPropertyValuesEncoder.cs
abatishchev May 2, 2024
a8c1651
Update JwtBuilderEncodeTests.cs
abatishchev May 2, 2024
90f02d8
Update JwtDecoderTests.cs
abatishchev May 2, 2024
80eb529
Update JwtValidator.cs
abatishchev May 2, 2024
d48dee1
Update JwtWebKeySet.cs
abatishchev May 2, 2024
1a3a717
Update JwtWebKeySet.cs
abatishchev May 2, 2024
89cc32f
Update JwtValidator.cs
abatishchev May 2, 2024
37aafbb
Update JwtWebKeySet.cs
abatishchev May 2, 2024
a66322b
explicit namespace
CADBIMDeveloper May 3, 2024
6dd5743
to expression body
CADBIMDeveloper May 3, 2024
e7189c1
remove blank lines
CADBIMDeveloper May 3, 2024
c57a9a0
pattern matching switch/case
CADBIMDeveloper May 3, 2024
58e9a99
formatting
CADBIMDeveloper May 3, 2024
a80e41e
seal the class
CADBIMDeveloper May 3, 2024
0112fef
rename method
CADBIMDeveloper May 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Unreleased

# JWT 11.0.0-beta3

- Added support ofr JSON web keys

# JWT.Extensions.AspNetCore 11.0.0-beta3

- Converted to use the event model to allow dependency injection with custom event classes.
Expand Down Expand Up @@ -47,4 +51,4 @@
- Renamed default IdentityFactory in Jwt.Extensions.AspNetCore, opened up for inheritance, extension (#428)
- Added Encode(T) and Encode(Type, object) to JwtBuilder (#415)
- Updated Newtonsoft.Json to version 13.0.1
- Fixed typos in exception messages
- Fixed typos in exception messages
10 changes: 10 additions & 0 deletions src/JWT/Algorithms/HMACSHA256Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ namespace JWT.Algorithms
/// </summary>
public sealed class HMACSHA256Algorithm : HMACSHAAlgorithm
{
public HMACSHA256Algorithm()
{

}

internal HMACSHA256Algorithm(byte[] key) : base(key)
{

}

/// <inheritdoc />
public override string Name => nameof(JwtAlgorithmName.HS256);

Expand Down
10 changes: 10 additions & 0 deletions src/JWT/Algorithms/HMACSHA384Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ namespace JWT.Algorithms
/// </summary>
public sealed class HMACSHA384Algorithm : HMACSHAAlgorithm
{
public HMACSHA384Algorithm()
{

}

internal HMACSHA384Algorithm(byte[] key) : base(key)
{

}

/// <inheritdoc />
public override string Name => nameof(JwtAlgorithmName.HS384);

Expand Down
12 changes: 11 additions & 1 deletion src/JWT/Algorithms/HMACSHA512Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ namespace JWT.Algorithms
/// HMAC using SHA-512
/// </summary>
public sealed class HMACSHA512Algorithm : HMACSHAAlgorithm
{
{
public HMACSHA512Algorithm()
{

}

internal HMACSHA512Algorithm(byte[] key) : base(key)
{

}

/// <inheritdoc />
public override string Name => nameof(JwtAlgorithmName.HS512);

Expand Down
16 changes: 14 additions & 2 deletions src/JWT/Algorithms/HMACSHAAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@

namespace JWT.Algorithms
{
public abstract class HMACSHAAlgorithm : IJwtAlgorithm
public abstract class HMACSHAAlgorithm : ISymmetricAlgorithm
{
protected HMACSHAAlgorithm()
{

}

protected HMACSHAAlgorithm(byte[] key)
{
this.Key = key;
}

/// <inheritdoc />
public abstract string Name { get; }

Expand All @@ -13,10 +23,12 @@ public abstract class HMACSHAAlgorithm : IJwtAlgorithm
/// <inheritdoc />
public byte[] Sign(byte[] key, byte[] bytesToSign)
{
using var sha = CreateAlgorithm(key);
using var sha = CreateAlgorithm(key ?? this.Key);
return sha.ComputeHash(bytesToSign);
}

public byte[] Key { get; }

protected abstract HMAC CreateAlgorithm(byte[] key);
}
}
18 changes: 15 additions & 3 deletions src/JWT/Algorithms/HMACSHAAlgorithmFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,28 @@ namespace JWT.Algorithms
/// <inheritdoc />
public class HMACSHAAlgorithmFactory : JwtAlgorithmFactory
{
private readonly byte[] _key;

public HMACSHAAlgorithmFactory()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do all classes need an empty ctor? Both the algos and the factories?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to introduce breaking changes for the user code like:

JwtBuilder.Create().WithAlgorithm(...)...;
JwtBuilder.Create().WithAlgorithm(...).WithSecret(...).Encode(...);
JwtBuilder.Create().WithAlgorithmFactory(...)...;

I can remove empty ctors if you want to

{

}

public HMACSHAAlgorithmFactory(byte[] key)
{
_key = key;
}

protected override IJwtAlgorithm Create(JwtAlgorithmName algorithm)
{
switch (algorithm)
{
case JwtAlgorithmName.HS256:
return new HMACSHA256Algorithm();
return new HMACSHA256Algorithm(_key);
case JwtAlgorithmName.HS384:
return new HMACSHA384Algorithm();
return new HMACSHA384Algorithm(_key);
case JwtAlgorithmName.HS512:
return new HMACSHA512Algorithm();
return new HMACSHA512Algorithm(_key);
case JwtAlgorithmName.RS256:
case JwtAlgorithmName.RS384:
case JwtAlgorithmName.RS512:
Expand Down
10 changes: 10 additions & 0 deletions src/JWT/Algorithms/ISymmetricAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace JWT.Algorithms
{
/// <summary>
/// Represents a symmetric algorithm to generate or validate JWT signature.
/// </summary>
public interface ISymmetricAlgorithm : IJwtAlgorithm
{
byte[] Key { get; }
}
}
93 changes: 93 additions & 0 deletions src/JWT/Builder/JwtBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Reflection;
using JWT.Algorithms;
using JWT.Jwk;
using JWT.Serializers;
using Newtonsoft.Json;

Expand Down Expand Up @@ -34,6 +35,8 @@ public sealed class JwtBuilder
private IAlgorithmFactory _algFactory;
private byte[][] _secrets;

private JwtWebKeysCollection _webKeysCollection;

/// <summary>
/// Creates a new instance of instance <see cref="JwtBuilder" />
/// </summary>
Expand Down Expand Up @@ -170,6 +173,96 @@ public JwtBuilder WithAlgorithmFactory(IAlgorithmFactory algFactory)
return this;
}

/// <summary>
/// Sets Json Web Key Set
/// </summary>
/// <param name="webKeysCollection"></param>
/// <returns>Current builder instance.</returns>
public JwtBuilder WithJsonWebKeySet(JwtWebKeysCollection webKeysCollection)
{
_webKeysCollection = webKeysCollection;
_algFactory = new JwtJsonWebKeySetAlgorithmFactory(webKeysCollection);
return this;
}

/// <summary>
/// Sets Json Web Key Set
/// </summary>
/// <param name="getJsonWebKeys"></param>
/// <returns>Current builder instance.</returns>
public JwtBuilder WithJsonWebKeySet(Func<JwtWebKeysCollection> getJsonWebKeys)
{
return WithJsonWebKeySet(getJsonWebKeys());
}

/// <summary>
/// Sets Json Web Key Set
/// </summary>
/// <param name="webKeysCollectionFactory"></param>
/// <returns>Current builder instance.</returns>
public JwtBuilder WithJsonWebKeySet(IJwtWebKeysCollectionFactory webKeysCollectionFactory)
{
return WithJsonWebKeySet(webKeysCollectionFactory.CreateKeys());
}

/// <summary>
/// Sets Json Web Key Set
/// </summary>
/// <param name="keySet"></param>
/// <returns>Current builder instance.</returns>
public JwtBuilder WithJsonWebKeySet(string keySet)
{
return WithJsonWebKeySet(new JwtWebKeysCollection(keySet, _jsonSerializerFactory));
}

/// <summary>
/// Sets Json Web Key
/// </summary>
/// <param name="keyId">Key id in the JSON Web Key Set</param>
/// <param name="algorithmName">JWT algorithm name</param>
/// <returns>Current builder instance</returns>
/// <exception cref="InvalidOperationException"></exception>
public JwtBuilder WithJsonWebKey(string keyId, JwtAlgorithmName algorithmName)
{
if (_webKeysCollection == null)
throw new InvalidOperationException("JSON Web Key Set collection has not been initialized yet");

var key = _webKeysCollection.Find(keyId);

if (key == null)
throw new InvalidOperationException("The key id is not presented in the JSON Web key set");

return WithJsonWebKey(key, algorithmName);
}

/// <summary>
/// Sets Json Web Key
/// </summary>
/// <param name="key">JSON Web Key</param>
/// <param name="algorithmName">JWT algorithm name</param>
/// <returns>Current builder instance</returns>
/// <exception cref="ArgumentNullException"></exception>
public JwtBuilder WithJsonWebKey(JwtWebKey key, JwtAlgorithmName algorithmName)
{
if (key == null)
throw new ArgumentNullException(nameof(key), "JSON Web Key has not been provided");

var factory = new JwtJsonWebKeyAlgorithmFactory(key);

var context = new JwtDecoderContext
{
Header = new JwtHeader
{
Algorithm = algorithmName.ToString(),
KeyId = key.KeyId
}
};

_algorithm = factory.Create(context);

return AddHeader(HeaderName.KeyId, key.KeyId);
}

/// <summary>
/// Sets JWT algorithm.
/// </summary>
Expand Down
13 changes: 13 additions & 0 deletions src/JWT/Exceptions/InvalidJsonWebKeyEllipticCurveTypeException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace JWT.Exceptions
{
public class InvalidJsonWebKeyEllipticCurveTypeException : ArgumentOutOfRangeException
{
public InvalidJsonWebKeyEllipticCurveTypeException(string ellipticCurveType)
: base($"{ellipticCurveType} is not defined in RFC751")
{

}
}
}
13 changes: 13 additions & 0 deletions src/JWT/Exceptions/InvalidJsonWebKeyTypeException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace JWT.Exceptions
{
public class InvalidJsonWebKeyTypeException : ArgumentOutOfRangeException
{
public InvalidJsonWebKeyTypeException(string keyType)
: base($"{keyType} is not defined in RFC7518")
{

}
}
}
13 changes: 13 additions & 0 deletions src/JWT/IJwtValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ public interface IJwtValidator
/// <param name="decodedSignature">The signature to validate with</param>
void Validate(string decodedPayload, IAsymmetricAlgorithm alg, byte[] bytesToSign, byte[] decodedSignature);

/// <summary>
/// Given the JWT, verifies its signature correctness.
/// </summary>
/// <remarks>
/// Used by the symmetric algorithms only.
/// </remarks>
/// <param name="keys">The keys provided which one of them was used to sign the JWT</param>
/// <param name="decodedPayload"></param>
/// <param name="alg"></param>
/// <param name="bytesToSign"></param>
/// <param name="decodedSignature"></param>
void Validate(byte[][] keys, string decodedPayload, ISymmetricAlgorithm alg, byte[] bytesToSign, byte[] decodedSignature);

/// <summary>
/// Given the JWT, verifies its signature correctness without throwing an exception but returning it instead.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/JWT/JWT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</PropertyGroup>

<PropertyGroup>
<Version>11.0.0-beta2</Version>
<Version>11.0.0-beta3</Version>
<FileVersion>11.0.0.0</FileVersion>
<AssemblyVersion>11.0.0.0</AssemblyVersion>
</PropertyGroup>
Expand Down
7 changes: 7 additions & 0 deletions src/JWT/Jwk/IJwtWebKeysCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace JWT.Jwk
{
public interface IJwtWebKeysCollection
{
JwtWebKey Find(string keyId);
}
}
6 changes: 6 additions & 0 deletions src/JWT/Jwk/IJwtWebKeysCollectionFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace JWT.Jwk;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please convert implicit namespace into explicit (here and elsewhere too), for the consistency of the codebase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. sorry. This is the only place with implicit namespace (my bad, miss it). Fixed


public interface IJwtWebKeysCollectionFactory
{
JwtWebKeysCollection CreateKeys();
}
Loading