Skip to content

release 1.7.1 #291

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

Merged
merged 23 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Quick links:
The library supports the following Java environments:
- Java 8 (or higher)

Current version - 1.7.0
Current version - 1.7.1

You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt).

Expand All @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.7.0</version>
<version>1.7.1</version>
</dependency>
```
### Gradle

```
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.7.0'
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.7.1'
```

## Usage
Expand Down
6 changes: 6 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version 1.7.1
=============
- sendX5c API added to IConfidentialClientApplication to specify if the x5c claim
(public key of the certificate) should be sent to the STS.
Default value is true.

Version 1.7.0
=============
- Tenant profiles added to IAccount
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.7.0</version>
<version>1.7.1</version>
<packaging>jar</packaging>
<name>msal4j</name>
<description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ public void acquireTokenClientCredentials_ClientAssertion() throws Exception{
ClientAssertion clientAssertion = JwtHelper.buildJwt(
clientId,
(ClientCertificate) certificate,
"https://login.microsoftonline.com/common/oauth2/v2.0/token");
"https://login.microsoftonline.com/common/oauth2/v2.0/token",
true);

IClientCredential credential = ClientCredentialFactory.createFromClientAssertion(
clientAssertion.assertion());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ public void testClientCertificateRebuildsWhenExpired() throws Exception {
"buildJwt",
EasyMock.isA(String.class),
EasyMock.isA(ClientCertificate.class),
EasyMock.isA(String.class))
EasyMock.isA(String.class),
EasyMock.anyBoolean())
.andReturn(shortExperationJwt)
.times(2); // By this being called twice we ensure the client assertion is rebuilt once it has expired

Expand Down Expand Up @@ -184,13 +185,16 @@ private ClientAssertion buildShortJwt(String clientId,
.build();
SignedJWT jwt;
try {
JWSHeader.Builder builder = new JWSHeader.Builder(JWSAlgorithm.RS256);

List<Base64> certs = new ArrayList<>();
for (String cert: credential.getEncodedPublicKeyCertificateOrCertificateChain()) {
for (String cert : credential.getEncodedPublicKeyCertificateChain()) {
certs.add(new Base64(cert));
}
JWSHeader.Builder builder = new JWSHeader.Builder(JWSAlgorithm.RS256);
builder.x509CertChain(certs);

builder.x509CertThumbprint(new Base64URL(credential.publicCertificateHash()));

jwt = new SignedJWT(builder.build(), claimsSet);
final RSASSASigner signer = new RSASSASigner(credential.privateKey());
jwt.sign(signer);
Expand Down
31 changes: 14 additions & 17 deletions src/main/java/com/microsoft/aad/msal4j/ClientCertificate.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ final class ClientCertificate implements IClientCertificate {
@Getter
private final PrivateKey privateKey;

private final X509Certificate publicKeyCertificate;

private final List<X509Certificate> publicKeyCertificateChain;

ClientCertificate
(PrivateKey privateKey, X509Certificate publicKeyCertificate, List<X509Certificate> publicKeyCertificateChain) {
(PrivateKey privateKey, List<X509Certificate> publicKeyCertificateChain) {
if (privateKey == null) {
throw new NullPointerException("PrivateKey is null or empty");
}
Expand Down Expand Up @@ -68,26 +66,21 @@ final class ClientCertificate implements IClientCertificate {
" sun.security.mscapi.RSAPrivateKey");
}

this.publicKeyCertificate = publicKeyCertificate;
this.publicKeyCertificateChain = publicKeyCertificateChain;
}

public String publicCertificateHash()
throws CertificateEncodingException, NoSuchAlgorithmException {

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

public List<String> getEncodedPublicKeyCertificateOrCertificateChain() throws CertificateEncodingException {
public List<String> getEncodedPublicKeyCertificateChain() throws CertificateEncodingException {
List<String> result = new ArrayList<>();

if (publicKeyCertificateChain != null && publicKeyCertificateChain.size() > 0) {
for (X509Certificate cert : publicKeyCertificateChain) {
result.add(Base64.getEncoder().encodeToString(cert.getEncoded()));
}
} else {
result.add(Base64.getEncoder().encodeToString(publicKeyCertificate.getEncoded()));
for (X509Certificate cert : publicKeyCertificateChain) {
result.add(Base64.getEncoder().encodeToString(cert.getEncoded()));
}
return result;
}
Expand All @@ -108,22 +101,26 @@ static ClientCertificate create(final InputStream pkcs12Certificate, final Strin
throw new IllegalArgumentException("more than one certificate alias found in input stream");
}

ArrayList<X509Certificate> publicKeyCertificateChain = null;
ArrayList<X509Certificate> publicKeyCertificateChain = new ArrayList<>();;
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, password.toCharArray());

X509Certificate publicKeyCertificate = (X509Certificate) keystore.getCertificate(alias);
Certificate[] chain = keystore.getCertificateChain(alias);

if (chain != null) {
publicKeyCertificateChain = new ArrayList<>();
if (chain != null && chain.length > 0) {
for (Certificate c : chain) {
publicKeyCertificateChain.add((X509Certificate) c);
}
}
return new ClientCertificate(privateKey, publicKeyCertificate, publicKeyCertificateChain);
else{
publicKeyCertificateChain.add(publicKeyCertificate);
}

return new ClientCertificate(privateKey, publicKeyCertificateChain);
}

static ClientCertificate create(final PrivateKey key, final X509Certificate publicKeyCertificate) {
return new ClientCertificate(key, publicKeyCertificate, null);
return new ClientCertificate(key, Arrays.asList(publicKeyCertificate));
}

private static byte[] getHash(final byte[] inputBytes) throws NoSuchAlgorithmException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import java.security.cert.X509Certificate;
import java.util.List;

import static com.microsoft.aad.msal4j.ParameterValidationUtils.validateNotNull;

/**
* Factory for creating client credentials used in confidential client flows. For more details, see
* https://aka.ms/msal4j-client-credentials
Expand Down Expand Up @@ -50,6 +52,8 @@ public static IClientCertificate createFromCertificate(final InputStream pkcs12C
* @return {@link ClientCertificate}
*/
public static IClientCertificate createFromCertificate(final PrivateKey key, final X509Certificate publicKeyCertificate) {
validateNotNull("publicKeyCertificate", publicKeyCertificate);

return ClientCertificate.create(key, publicKeyCertificate);
}

Expand All @@ -63,7 +67,7 @@ public static IClientCertificate createFromCertificateChain(PrivateKey key, List
if(key == null || publicKeyCertificateChain == null || publicKeyCertificateChain.size() == 0){
throw new IllegalArgumentException("null or empty input parameter");
}
return new ClientCertificate(key, publicKeyCertificateChain.get(0), publicKeyCertificateChain);
return new ClientCertificate(key, publicKeyCertificateChain);
}

/**
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/com/microsoft/aad/msal4j/ClientInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@ class ClientInfo {
private String uniqueIdentifier;

@JsonProperty("utid")
private String unqiueTenantIdentifier;
private String uniqueTenantIdentifier;

public static ClientInfo createFromJson(String clientInfoJsonBase64Encoded){
if(StringHelper.isBlank(clientInfoJsonBase64Encoded)){
return null;
return null;
}
byte[] decodedInput = Base64.getDecoder().decode(clientInfoJsonBase64Encoded.getBytes(StandardCharset.UTF_8));

byte[] decodedInput = Base64.getUrlDecoder().decode(clientInfoJsonBase64Encoded.getBytes(StandardCharset.UTF_8));

return JsonHelper.convertJsonToObject(new String(decodedInput, StandardCharset.UTF_8), ClientInfo.class);
}

String toAccountIdentifier(){
return uniqueIdentifier + POINT_DELIMITER + unqiueTenantIdentifier;
return uniqueIdentifier + POINT_DELIMITER + uniqueTenantIdentifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.nimbusds.oauth2.sdk.auth.PrivateKeyJWT;
import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.id.ClientID;
import lombok.Getter;
import lombok.experimental.Accessors;
import org.slf4j.LoggerFactory;

import java.util.Collections;
Expand All @@ -33,6 +35,10 @@ public class ConfidentialClientApplication extends AbstractClientApplicationBase
private boolean clientCertAuthentication = false;
private ClientCertificate clientCertificate;

@Accessors(fluent = true)
@Getter
private boolean sendX5c;

@Override
public CompletableFuture<IAuthenticationResult> acquireToken(ClientCredentialParameters parameters) {

Expand Down Expand Up @@ -62,6 +68,7 @@ public CompletableFuture<IAuthenticationResult> acquireToken(OnBehalfOfParameter

private ConfidentialClientApplication(Builder builder) {
super(builder);
sendX5c = builder.sendX5c;

log = LoggerFactory.getLogger(ConfidentialClientApplication.class);

Expand Down Expand Up @@ -103,7 +110,8 @@ private ClientAuthentication buildValidClientCertificateAuthority() {
ClientAssertion clientAssertion = JwtHelper.buildJwt(
clientId(),
clientCertificate,
this.authenticationAuthority.selfSignedJwtAudience());
this.authenticationAuthority.selfSignedJwtAudience(),
sendX5c);
return createClientAuthFromClientAssertion(clientAssertion);
}

Expand Down Expand Up @@ -136,11 +144,26 @@ public static class Builder extends AbstractClientApplicationBase.Builder<Builde

private IClientCredential clientCredential;

private boolean sendX5c = true;

private Builder(String clientId, IClientCredential clientCredential) {
super(clientId);
this.clientCredential = clientCredential;
}

/**
* Specifies if the x5c claim (public key of the certificate) should be sent to the STS.
* Default value is true
*
* @param val true if the x5c should be sent. Otherwise false
* @return instance of the Builder on which method was called
*/
public ConfidentialClientApplication.Builder sendX5c(boolean val) {
this.sendX5c = val;

return self();
}

@Override
public ConfidentialClientApplication build() {

Expand Down
15 changes: 9 additions & 6 deletions src/main/java/com/microsoft/aad/msal4j/DefaultHttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ class DefaultHttpClient implements IHttpClient {

private final Proxy proxy;
private final SSLSocketFactory sslSocketFactory;
public int DEFAULTCONNECTIONTIMEOUT = 3000;
public int DEFAULTREADTIMEOUT = 5000;
public int DEFAULT_CONNECT_TIMEOUT = 10000;
public int DEFAULT_READ_TIMEOUT = 15000;

private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private int readTimeout = DEFAULT_READ_TIMEOUT;

DefaultHttpClient(Proxy proxy, SSLSocketFactory sslSocketFactory, Integer connectTimeout, Integer readTimeout){
this.proxy = proxy;
this.sslSocketFactory = sslSocketFactory;
if (connectTimeout != null) this.CONNECT_TIMEOUT = connectTimeout;
if (readTimeout != null) this.READ_TIMEOUT = readTimeout;
if (connectTimeout != null) this.connectTimeout = connectTimeout;
if (readTimeout != null) this.readTimeout = readTimeout;
}

public IHttpResponse send(HttpRequest httpRequest) throws Exception{
Expand Down Expand Up @@ -80,8 +83,8 @@ private HttpsURLConnection openConnection(final URL finalURL)
connection.setSSLSocketFactory(sslSocketFactory);
}

connection.setConnectTimeout(DEFAULTCONNECTIONTIMEOUT);
connection.setReadTimeout(DEFAULTREADTIMEOUT);
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);

return connection;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ public interface IClientCertificate extends IClientCredential{
* @return base64 encoded string
* @throws CertificateEncodingException if an encoding error occurs
*/
List<String> getEncodedPublicKeyCertificateOrCertificateChain() throws CertificateEncodingException;
List<String> getEncodedPublicKeyCertificateChain() throws CertificateEncodingException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
* For details see https://aka.ms/msal4jclientapplications
*/
public interface IConfidentialClientApplication extends IClientApplicationBase {
/**
* @return a boolean value which determines whether x5c claim (public key of the certificate)
* will be sent to the STS.
*/
boolean sendX5c();

/**
* Acquires tokens from the authority configured in the application, for the confidential client
Expand Down
15 changes: 9 additions & 6 deletions src/main/java/com/microsoft/aad/msal4j/JwtHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
final class JwtHelper {

static ClientAssertion buildJwt(String clientId, final ClientCertificate credential,
final String jwtAudience) throws MsalClientException {
final String jwtAudience, boolean sendX5c) throws MsalClientException {
if (StringHelper.isBlank(clientId)) {
throw new IllegalArgumentException("clientId is null or empty");
}
Expand All @@ -45,13 +45,16 @@ static ClientAssertion buildJwt(String clientId, final ClientCertificate credent

SignedJWT jwt;
try {
List<Base64> certs = new ArrayList<>();
for(String publicCertificate: credential.getEncodedPublicKeyCertificateOrCertificateChain()) {
certs.add(new Base64(publicCertificate));
JWSHeader.Builder builder = new Builder(JWSAlgorithm.RS256);

if(sendX5c){
List<Base64> certs = new ArrayList<>();
for (String cert: credential.getEncodedPublicKeyCertificateChain()) {
certs.add(new Base64(cert));
}
builder.x509CertChain(certs);
}

JWSHeader.Builder builder = new Builder(JWSAlgorithm.RS256);
builder.x509CertChain(certs);
builder.x509CertThumbprint(new Base64URL(credential.publicCertificateHash()));

jwt = new SignedJWT(builder.build(), claimsSet);
Expand Down
2 changes: 1 addition & 1 deletion src/samples/cache/sample_cache.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"realm": "contoso",
"environment": "login.windows.net",
"credential_type": "IdToken",
"secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature",
"secret": "",
"client_id": "my_client_id",
"home_account_id": "uid.utid"
}
Expand Down
2 changes: 1 addition & 1 deletion src/samples/msal-b2c-web-sample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.7.0</version>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
Expand Down
2 changes: 1 addition & 1 deletion src/samples/msal-obo-sample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.7.0</version>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
Expand Down
2 changes: 1 addition & 1 deletion src/samples/msal-web-sample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.6.2</version>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
Expand Down
Loading