Skip to content

Commit

Permalink
Use SHA2 and PSS for client assertion (#4616)
Browse files Browse the repository at this point in the history
* Fix tests

* Remove other JSON operations

* Perf tests

* codeql

* PR comments

* fix
  • Loading branch information
bgavrilMS authored Feb 6, 2024
1 parent 8b49739 commit d2f015a
Show file tree
Hide file tree
Showing 30 changed files with 523 additions and 384 deletions.
43 changes: 39 additions & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style

dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
Expand All @@ -69,7 +69,7 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# static fields should have s_ prefix
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style

dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
Expand All @@ -80,7 +80,7 @@ dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style

dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
Expand Down Expand Up @@ -140,6 +140,23 @@ csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion

# C++ Files

Expand Down Expand Up @@ -826,7 +843,7 @@ dotnet_diagnostic.CA1832.severity = none
dotnet_diagnostic.CA1833.severity = none

# Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1834.severity = none
dotnet_diagnostic.CA1834.severity = warning

# Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
dotnet_diagnostic.CA1835.severity = none
Expand Down Expand Up @@ -1284,3 +1301,21 @@ dotnet_diagnostic.RS1012.severity = none
dotnet_diagnostic.RS1013.severity = none

dotnet_diagnostic.RS1014.severity = none
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_prefer_collection_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ private AuthorityInfo(
AuthorityType == AuthorityType.B2C ||
AuthorityType == AuthorityType.Ciam;

/// <summary>
/// True if SHA2 and PSS can be used for creating the client credential from a certificate
/// </summary>
internal bool IsSha2CredentialSupported =>
AuthorityType != AuthorityType.Dsts &&
AuthorityType != AuthorityType.Generic &&
AuthorityType != AuthorityType.Adfs;

#region Builders
internal static AuthorityInfo FromAuthorityUri(string authorityUri, bool validateAuthority)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ public string FormatAccessToken(MsalAccessTokenCacheItem msalAccessTokenCacheIte
}

var header = new JObject();
header[JsonWebTokenConstants.ReservedHeaderParameters.Algorithm] = _popCryptoProvider.CryptographicAlgorithm;
header[JsonWebTokenConstants.ReservedHeaderParameters.KeyId] = KeyId;
header[JsonWebTokenConstants.ReservedHeaderParameters.Type] = Constants.PoPTokenType;
header[JsonWebTokenConstants.Algorithm] = _popCryptoProvider.CryptographicAlgorithm;
header[JsonWebTokenConstants.KeyId] = KeyId;
header[JsonWebTokenConstants.Type] = Constants.PoPTokenType;

var body = CreateBody(msalAccessTokenCacheItem);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -17,17 +20,20 @@ internal class CertificateAndClaimsClientCredential : IClientCredential
{
private readonly IDictionary<string, string> _claimsToSign;
private readonly bool _appendDefaultClaims;
private readonly string _base64EncodedThumbprint; // x5t

public X509Certificate2 Certificate { get; }

public AssertionType AssertionType => AssertionType.CertificateWithoutSni;

public CertificateAndClaimsClientCredential(X509Certificate2 certificate, IDictionary<string, string> claimsToSign, bool appendDefaultClaims)
public CertificateAndClaimsClientCredential(
X509Certificate2 certificate,
IDictionary<string, string> claimsToSign,
bool appendDefaultClaims)
{
Certificate = certificate;
_claimsToSign = claimsToSign;
_appendDefaultClaims = appendDefaultClaims;
_base64EncodedThumbprint = Base64UrlHelpers.Encode(certificate.GetCertHash());

}

public Task AddConfidentialClientParametersAsync(
Expand All @@ -37,6 +43,7 @@ public Task AddConfidentialClientParametersAsync(
string clientId,
string tokenEndpoint,
bool sendX5C,
bool useSha2AndPss,
CancellationToken cancellationToken)
{
var jwtToken = new JsonWebToken(
Expand All @@ -46,7 +53,7 @@ public Task AddConfidentialClientParametersAsync(
_claimsToSign,
_appendDefaultClaims);

string assertion = jwtToken.Sign(Certificate, _base64EncodedThumbprint, sendX5C);
string assertion = jwtToken.Sign(Certificate, sendX5C, useSha2AndPss);

oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertionType, OAuth2AssertionType.JwtBearer);
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertion, assertion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Task AddConfidentialClientParametersAsync(
string clientId,
string tokenEndpoint,
bool sendX5C,
bool useSha2,
CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ public SecretStringClientCredential(string secret)
Secret = secret;
}

public Task AddConfidentialClientParametersAsync(OAuth2Client oAuth2Client, ILoggerAdapter logger, ICryptographyManager cryptographyManager, string clientId, string tokenEndpoint, bool sendX5C, CancellationToken cancellationToken)
public Task AddConfidentialClientParametersAsync(
OAuth2Client oAuth2Client,
ILoggerAdapter logger,
ICryptographyManager cryptographyManager,
string clientId,
string tokenEndpoint,
bool sendX5C,
bool useSha2,
CancellationToken cancellationToken)
{
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientSecret, Secret);
return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ public SignedAssertionClientCredential(string signedAssertion)
_signedAssertion = signedAssertion;
}

public Task AddConfidentialClientParametersAsync(OAuth2Client oAuth2Client, ILoggerAdapter logger, ICryptographyManager cryptographyManager, string clientId, string tokenEndpoint, bool sendX5C, CancellationToken cancellationToken)
public Task AddConfidentialClientParametersAsync(
OAuth2Client oAuth2Client,
ILoggerAdapter logger,
ICryptographyManager cryptographyManager,
string clientId,
string tokenEndpoint,
bool sendX5C,
bool useSha2,
CancellationToken cancellationToken)
{
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertionType, OAuth2AssertionType.JwtBearer);
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertion, _signedAssertion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ public SignedAssertionDelegateClientCredential(Func<AssertionRequestOptions, Tas
_signedAssertionWithInfoDelegate = signedAssertionDelegate;
}

public async Task AddConfidentialClientParametersAsync(OAuth2Client oAuth2Client, ILoggerAdapter logger, ICryptographyManager cryptographyManager, string clientId, string tokenEndpoint, bool sendX5C, CancellationToken cancellationToken)
public async Task AddConfidentialClientParametersAsync(
OAuth2Client oAuth2Client,
ILoggerAdapter logger,
ICryptographyManager cryptographyManager,
string clientId,
string tokenEndpoint,
bool sendX5C,
bool useSha2,
CancellationToken cancellationToken)
{
string signedAssertion = await (_signedAssertionDelegate != null
? _signedAssertionDelegate(cancellationToken).ConfigureAwait(false)
Expand All @@ -41,5 +49,6 @@ public async Task AddConfidentialClientParametersAsync(OAuth2Client oAuth2Client
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertionType, OAuth2AssertionType.JwtBearer);
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertion, signedAssertion);
}

}
}
Loading

1 comment on commit d2f015a

@haha1903
Copy link

Choose a reason for hiding this comment

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

Using the SHA2 feature will cause the following errors:

Microsoft.Identity.Client.MsalServiceException: AADSTS5002730: Invalid JWT token. Unsupported key for the signing algorithm. Trace ID: c985e53a-e233-4b56-88f0-bfcd91e20c00 Correlation ID: ef66443e-1b8d-43f8-9d6e-af2a090818ee Timestamp: 2024-03-29 01:38:20Z

Please sign in to comment.