Skip to content

Commit

Permalink
Merge pull request #840 from AzureAD/avdunn/update-sha-method
Browse files Browse the repository at this point in the history
Use SHA256 thumbprints in non-ADFS cert flows
  • Loading branch information
Avery-Dunn authored Jul 23, 2024
2 parents d3faa28 + 08a5189 commit c83185c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ private ClientAssertion getClientAssertion(String clientId) {
clientId,
(ClientCertificate) certificate,
"https://login.microsoftonline.com/common/oauth2/v2.0/token",
true);
true, false);
}

private void assertAcquireTokenCommon(String clientId, IClientCredential credential, String authority) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
Expand All @@ -21,7 +19,6 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
Expand Down Expand Up @@ -53,7 +50,14 @@ public String publicCertificateHash()
throws CertificateEncodingException, NoSuchAlgorithmException {

return Base64.getEncoder().encodeToString(ClientCertificate
.getHash(publicKeyCertificateChain.get(0).getEncoded()));
.getHashSha256(publicKeyCertificateChain.get(0).getEncoded()));
}

public String publicCertificateHashSha1()
throws CertificateEncodingException, NoSuchAlgorithmException {

return Base64.getEncoder().encodeToString(ClientCertificate
.getHashSha1(publicKeyCertificateChain.get(0).getEncoded()));
}

public List<String> getEncodedPublicKeyCertificateChain() throws CertificateEncodingException {
Expand Down Expand Up @@ -119,9 +123,15 @@ static ClientCertificate create(final PrivateKey key, final X509Certificate publ
return new ClientCertificate(key, Arrays.asList(publicKeyCertificate));
}

private static byte[] getHash(final byte[] inputBytes) throws NoSuchAlgorithmException {
private static byte[] getHashSha1(final byte[] inputBytes) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(inputBytes);
return md.digest();
}

private static byte[] getHashSha256(final byte[] inputBytes) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(inputBytes);
return md.digest();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ private void initClientAuthentication(IClientCredential clientCredential) {
} else if (clientCredential instanceof ClientCertificate) {
this.clientCertAuthentication = true;
this.clientCertificate = (ClientCertificate) clientCredential;
clientAuthentication = buildValidClientCertificateAuthority();
if (Authority.detectAuthorityType(this.authenticationAuthority.canonicalAuthorityUrl()) == AuthorityType.ADFS) {
clientAuthentication = buildValidClientCertificateAuthoritySha1();
} else {
clientAuthentication = buildValidClientCertificateAuthority();
}
} else if (clientCredential instanceof ClientAssertion) {
clientAuthentication = createClientAuthFromClientAssertion((ClientAssertion) clientCredential);
} else {
Expand All @@ -127,7 +131,20 @@ private ClientAuthentication buildValidClientCertificateAuthority() {
clientId(),
clientCertificate,
this.authenticationAuthority.selfSignedJwtAudience(),
sendX5c);
sendX5c,
false);
return createClientAuthFromClientAssertion(clientAssertion);
}

//The library originally used SHA-1 for thumbprints as other algorithms were not supported server-side,
// and while support for SHA-256 has been added certain flows still only allow SHA-1
private ClientAuthentication buildValidClientCertificateAuthoritySha1() {
ClientAssertion clientAssertion = JwtHelper.buildJwt(
clientId(),
clientCertificate,
this.authenticationAuthority.selfSignedJwtAudience(),
sendX5c,
true);
return createClientAuthFromClientAssertion(clientAssertion);
}

Expand Down
10 changes: 8 additions & 2 deletions msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/JwtHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
final class JwtHelper {

static ClientAssertion buildJwt(String clientId, final ClientCertificate credential,
final String jwtAudience, boolean sendX5c) throws MsalClientException {
final String jwtAudience, boolean sendX5c,
boolean useSha1) throws MsalClientException {
if (StringHelper.isBlank(clientId)) {
throw new IllegalArgumentException("clientId is null or empty");
}
Expand Down Expand Up @@ -55,7 +56,12 @@ static ClientAssertion buildJwt(String clientId, final ClientCertificate credent
builder.x509CertChain(certs);
}

builder.x509CertThumbprint(new Base64URL(credential.publicCertificateHash()));
//SHA-256 is preferred, however certain flows still require SHA-1 due to what is supported server-side
if (useSha1) {
builder.x509CertThumbprint(new Base64URL(credential.publicCertificateHashSha1()));
} else {
builder.x509CertSHA256Thumbprint(new Base64URL(credential.publicCertificateHash()));
}

jwt = new SignedJWT(builder.build(), claimsSet);
final RSASSASigner signer = new RSASSASigner(credential.privateKey());
Expand Down

0 comments on commit c83185c

Please sign in to comment.