Skip to content

Commit

Permalink
Add ability to create token without kid
Browse files Browse the repository at this point in the history
  • Loading branch information
msbw2 committed Nov 1, 2024
1 parent 671e2a2 commit f7c4bd9
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, System.Collections.Generic.IDictionary<string, object> additionalHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> string
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJweHeader(Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, string tokenType, System.Collections.Generic.IDictionary<string, object> jweHeaderClaims, bool includeKeyIdInHeader) -> byte[]
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJwsHeader(ref System.Text.Json.Utf8JsonWriter writer, Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary<string, object> jweHeaderClaims, System.Collections.Generic.IDictionary<string, object> jwsHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ internal static string CreateToken(
tokenDescriptor.EncryptingCredentials,
tokenDescriptor.AdditionalHeaderClaims,
tokenDescriptor.AdditionalInnerHeaderClaims,
tokenDescriptor.TokenType);
tokenDescriptor.TokenType,
tokenDescriptor.IncludeKeyIdInHeader);

// mark length of jwt header
int headerLength = (int)utf8ByteMemoryStream.Length;
Expand Down Expand Up @@ -255,7 +256,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
tokenDescriptor.EncryptingCredentials,
tokenDescriptor.CompressionAlgorithm,
tokenDescriptor.AdditionalHeaderClaims,
tokenDescriptor.TokenType);
tokenDescriptor.TokenType,
true);
}
else
{
Expand Down Expand Up @@ -586,7 +588,8 @@ internal static string CreateToken
encryptingCredentials,
additionalHeaderClaims,
additionalInnerHeaderClaims,
null);
null,
true);

// mark length of jwt header
int headerLength = (int)utf8ByteMemoryStream.Length;
Expand Down Expand Up @@ -638,7 +641,8 @@ int sizeOfEncodedHeaderAndPayloadAsciiBytes
encryptingCredentials,
compressionAlgorithm,
additionalHeaderClaims,
tokenType);
tokenType,
true);
}
else
{
Expand Down Expand Up @@ -959,7 +963,8 @@ internal static void WriteJwsHeader(
EncryptingCredentials encryptingCredentials,
IDictionary<string, object> jweHeaderClaims,
IDictionary<string, object> jwsHeaderClaims,
string tokenType)
string tokenType,
bool includeKeyIdInHeader)
{
if (jweHeaderClaims?.Count > 0 && jweHeaderClaims.Keys.Intersect(JwtTokenUtilities.DefaultHeaderParameters, StringComparer.OrdinalIgnoreCase).Any())
throw LogHelper.LogExceptionMessage(
Expand Down Expand Up @@ -991,7 +996,7 @@ internal static void WriteJwsHeader(
else
{
writer.WriteString(JwtHeaderUtf8Bytes.Alg, signingCredentials.Algorithm);
if (signingCredentials.Key.KeyId != null)
if (includeKeyIdInHeader && signingCredentials.Key.KeyId != null)
writer.WriteString(JwtHeaderUtf8Bytes.Kid, signingCredentials.Key.KeyId);

if (signingCredentials.Key is X509SecurityKey x509SecurityKey)
Expand Down Expand Up @@ -1033,7 +1038,8 @@ internal static byte[] WriteJweHeader(
EncryptingCredentials encryptingCredentials,
string compressionAlgorithm,
string tokenType,
IDictionary<string, object> jweHeaderClaims)
IDictionary<string, object> jweHeaderClaims,
bool includeKeyIdInHeader)
{
using (MemoryStream memoryStream = new())
{
Expand All @@ -1053,7 +1059,7 @@ internal static byte[] WriteJweHeader(
// needs to be maintained.
if (AppContextSwitches.UseRfcDefinitionOfEpkAndKid)
{
if (encryptingCredentials.KeyExchangePublicKey.KeyId != null)
if (includeKeyIdInHeader && encryptingCredentials.KeyExchangePublicKey.KeyId != null)
writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.KeyExchangePublicKey.KeyId);

if (SupportedAlgorithms.EcdsaWrapAlgorithms.Contains(encryptingCredentials.Alg))
Expand All @@ -1069,7 +1075,7 @@ internal static byte[] WriteJweHeader(
}
else
{
if (encryptingCredentials.Key.KeyId != null)
if (includeKeyIdInHeader && encryptingCredentials.Key.KeyId != null)
writer.WriteString(JwtHeaderUtf8Bytes.Kid, encryptingCredentials.Key.KeyId);
}

Expand Down Expand Up @@ -1254,20 +1260,22 @@ private static string EncryptTokenPrivate(
IDictionary<string, object> additionalHeaderClaims,
string tokenType)
{
return (EncryptToken(
Encoding.UTF8.GetBytes(innerJwt),
encryptingCredentials,
compressionAlgorithm,
additionalHeaderClaims,
tokenType));
return EncryptToken(
Encoding.UTF8.GetBytes(innerJwt),
encryptingCredentials,
compressionAlgorithm,
additionalHeaderClaims,
tokenType,
true);
}

internal static string EncryptToken(
byte[] innerTokenUtf8Bytes,
EncryptingCredentials encryptingCredentials,
string compressionAlgorithm,
IDictionary<string, object> additionalHeaderClaims,
string tokenType)
string tokenType,
bool includeKeyIdInHeader)
{
CryptoProviderFactory cryptoProviderFactory = encryptingCredentials.CryptoProviderFactory ?? encryptingCredentials.Key.CryptoProviderFactory;

Expand All @@ -1281,7 +1289,7 @@ internal static string EncryptToken(
if (encryptionProvider == null)
throw LogHelper.LogExceptionMessage(new SecurityTokenEncryptionFailedException(LogMessages.IDX14103));

byte[] jweHeader = WriteJweHeader(encryptingCredentials, compressionAlgorithm, tokenType, additionalHeaderClaims);
byte[] jweHeader = WriteJweHeader(encryptingCredentials, compressionAlgorithm, tokenType, additionalHeaderClaims, includeKeyIdInHeader);
byte[] plainText;
if (!string.IsNullOrEmpty(compressionAlgorithm))
{
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.get -> bool
Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor.IncludeKeyIdInHeader.set -> void
11 changes: 11 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/SecurityTokenDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Claims;
using System.Threading;

Expand Down Expand Up @@ -105,5 +106,15 @@ public class SecurityTokenDescriptor
/// values will be overridden.
/// </summary>
public ClaimsIdentity Subject { get; set; }

/// <summary>
/// Indicates if <c>kid</c> and <c>x5t</c> should be included in the header of a JSON web token (JWT)
///
/// <remarks>
/// Only applies to JWTs
/// </remarks>
/// </summary>
[DefaultValue(true)]
public bool IncludeKeyIdInHeader { get; set; } = true;
}
}
2 changes: 2 additions & 0 deletions src/System.IdentityModel.Tokens.Jwt/InternalAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
System.IdentityModel.Tokens.Jwt.JwtHeader.JwtHeader(Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary<string, string> outboundAlgorithmMap, string tokenType, System.Collections.Generic.IDictionary<string, object> additionalHeaderClaims, bool includeKeyIdInHeader) -> void
System.IdentityModel.Tokens.Jwt.JwtHeader.JwtHeader(Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, System.Collections.Generic.IDictionary<string, string> outboundAlgorithmMap, string tokenType, System.Collections.Generic.IDictionary<string, object> additionalInnerHeaderClaims, bool includeKeyIdInHeader) -> void
57 changes: 52 additions & 5 deletions src/System.IdentityModel.Tokens.Jwt/JwtHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> will be added as the value for the 'typ' claim in the header. If it is null or empty <see cref="JwtConstants.HeaderType"/> will be used as token type</param>
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, string> outboundAlgorithmMap, string tokenType, IDictionary<string, object> additionalInnerHeaderClaims)
/// <param name="includeKeyIdInHeader">Controls if key identifying information should be stored in the header</param>
internal JwtHeader(
SigningCredentials signingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalInnerHeaderClaims,
bool includeKeyIdInHeader)
: base(StringComparer.Ordinal)
{
if (signingCredentials == null)
Expand All @@ -144,7 +150,7 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
else
Alg = signingCredentials.Algorithm;

if (!string.IsNullOrEmpty(signingCredentials.Key.KeyId))
if (includeKeyIdInHeader && !string.IsNullOrEmpty(signingCredentials.Key.KeyId))
Kid = signingCredentials.Key.KeyId;

if (signingCredentials is X509SigningCredentials x509SigningCredentials)
Expand All @@ -160,6 +166,23 @@ public JwtHeader(SigningCredentials signingCredentials, IDictionary<string, stri
SigningCredentials = signingCredentials;
}

/// <summary>
/// Initializes a new instance of <see cref="JwtHeader"/>.
/// With the Header Parameters:
/// <para>{ { typ, JWT }, { alg, SigningCredentials.Algorithm } }</para>
/// </summary>
/// <param name="signingCredentials"><see cref="SigningCredentials"/> used when creating a JWS Compact JSON.</param>
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> will be added as the value for the 'typ' claim in the header. If it is null or empty <see cref="JwtConstants.HeaderType"/> will be used as token type</param>
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
public JwtHeader(
SigningCredentials signingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalInnerHeaderClaims)
: this(signingCredentials, outboundAlgorithmMap, tokenType, additionalInnerHeaderClaims, true)
{ }

/// <summary>
/// Initializes a new instance of <see cref="JwtHeader"/>.
/// With the Header Parameters:
Expand Down Expand Up @@ -196,8 +219,14 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> provides the token type</param>
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
/// <param name="includeKeyIdInHeader">Controls if key identifying information should be stored in the header</param>
/// <exception cref="ArgumentNullException">If 'encryptingCredentials' is null.</exception>
public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string, string> outboundAlgorithmMap, string tokenType, IDictionary<string, object> additionalHeaderClaims)
internal JwtHeader(
EncryptingCredentials encryptingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalHeaderClaims,
bool includeKeyIdInHeader)
: base(StringComparer.Ordinal)
{
if (encryptingCredentials == null)
Expand All @@ -221,7 +250,7 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
// needs to be maintained.
if (AppContextSwitches.UseRfcDefinitionOfEpkAndKid)
{
if (!string.IsNullOrEmpty(encryptingCredentials.KeyExchangePublicKey.KeyId))
if (includeKeyIdInHeader && !string.IsNullOrEmpty(encryptingCredentials.KeyExchangePublicKey.KeyId))
Kid = encryptingCredentials.KeyExchangePublicKey.KeyId;

// Parameter MUST be present [...] when [key agreement] algorithms are used: https://www.rfc-editor.org/rfc/rfc7518#section-4.6.1.1
Expand All @@ -230,7 +259,7 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
}
else
{
if (!string.IsNullOrEmpty(encryptingCredentials.Key.KeyId))
if (includeKeyIdInHeader && !string.IsNullOrEmpty(encryptingCredentials.Key.KeyId))
Kid = encryptingCredentials.Key.KeyId;
}

Expand All @@ -243,6 +272,24 @@ public JwtHeader(EncryptingCredentials encryptingCredentials, IDictionary<string
EncryptingCredentials = encryptingCredentials;
}

/// <summary>
/// Initializes a new instance of <see cref="JwtHeader"/>.
/// With the Header Parameters:
/// <para>{ { typ, JWT }, { alg, EncryptingCredentials.Algorithm } }</para>
/// </summary>
/// <param name="encryptingCredentials"><see cref="EncryptingCredentials"/> used when creating a JWS Compact JSON.</param>
/// <param name="outboundAlgorithmMap">provides a mapping for the 'alg' value so that values are within the JWT namespace.</param>
/// <param name="tokenType"> provides the token type</param>
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
/// <exception cref="ArgumentNullException">If 'encryptingCredentials' is null.</exception>
public JwtHeader(
EncryptingCredentials encryptingCredentials,
IDictionary<string, string> outboundAlgorithmMap,
string tokenType,
IDictionary<string, object> additionalHeaderClaims)
: this(encryptingCredentials, outboundAlgorithmMap, tokenType, additionalHeaderClaims, true)
{ }

/// <summary>
/// Gets the signature algorithm that was used to create the signature.
/// </summary>
Expand Down
Loading

0 comments on commit f7c4bd9

Please sign in to comment.