Skip to content

Conversation

Avery-Dunn
Copy link
Contributor

Fixes the issue described in #958

Microsoft recommends PS256 for assertions and other MSALs follow this, however MSAL Java still used RS256. It seems like PSS support was only added in Java 11, but it was thankfully backported to Java 8.

This PR refactors JwtHelper to use the recommended algorithm and adjusts/expands tests in order to cover the new behavior.

In addition, while making the changes I discovered a weird edge case:

  • To create a Signature instance to sign the assertion, we need to provide a PrivateKey
  • When retrieving a PrivateKey from the Windows-MY keystore the underlying type is "sun.security.mscapi.CPrivateKey"
  • The sun.security.mscapi.CPrivateKey type was accepted by the Signature instance created with "SHA256withRSA", but for some reason the Signature created with "RSASSA-PSS" throws an error about it not specifically being an RSAPrivateKey instance

Since our integration tests use a cert stored in Windows-MY simply changing the assertion to use PS256 would've broken many of our tests. I'm sure plenty of our customers retrieve certs in the same way, and I'm not sure what other key types would cause this error.

So to avoid any breaking changes, this PR also implements a mechanism to fallback to the old RS256 style when we get a certain exception, and has extra tests covering this fallback behavior.

@Avery-Dunn Avery-Dunn requested a review from a team as a code owner September 10, 2025 23:48
* @return A ClientAssertion containing the signed JWT
* @throws Exception If JWT creation or signing fails
*/
private static ClientAssertion generateRs256Jwt(String clientId, ClientCertificate credential,
Copy link
Member

Choose a reason for hiding this comment

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

There seem to be a lot of code dup between thjis method and the ps256 version

}

@Test
void JwtHelper_buildJwt_UsesPSS256WhenSupported() throws Exception {
Copy link
Member

@bgavrilMS bgavrilMS Sep 22, 2025

Choose a reason for hiding this comment

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

The way it's implemented in MSAL .NET is:

  • for AAD, use SHA2 and PSS
  • for ADFS, use SHA1 and PKCS1

I see that the way to specify a certificate in MSAL Java is similar. So why not have some higher level tests, i.e. start from the public API and assert what gets put on the wire?

Copy link
Member

@bgavrilMS bgavrilMS left a comment

Choose a reason for hiding this comment

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

RSA padding should be universal, but I understand that Java8 uses CAPI instead of CNG.

However, for backwards compat, in places where we use SHA1, we should continue to use PKCS1. Does this happen? Can we have better E2E tests?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants