diff --git a/src/CsrValidation/java/example/pom.xml b/src/CsrValidation/java/example/pom.xml
index ef8595b..9e69e82 100644
--- a/src/CsrValidation/java/example/pom.xml
+++ b/src/CsrValidation/java/example/pom.xml
@@ -28,7 +28,7 @@
com.microsoft.intune.scep
csr-validation
- 0.0.1-SNAPSHOT
+ 20210505-SNAPSHOT
org.slf4j
diff --git a/src/CsrValidation/java/lib/pom.xml b/src/CsrValidation/java/lib/pom.xml
index 890e099..b6fb1e8 100644
--- a/src/CsrValidation/java/lib/pom.xml
+++ b/src/CsrValidation/java/lib/pom.xml
@@ -2,7 +2,7 @@
4.0.0
com.microsoft.intune.scep
csr-validation
- 0.0.1-SNAPSHOT
+ 20210505-SNAPSHOT
diff --git a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/ADALClientWrapper.java b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/ADALClientWrapper.java
index 86d0616..1706e9d 100644
--- a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/ADALClientWrapper.java
+++ b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/ADALClientWrapper.java
@@ -28,12 +28,12 @@
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.naming.ServiceUnavailableException;
import javax.net.ssl.SSLSocketFactory;
+import com.microsoft.aad.adal4j.AsymmetricKeyCredential;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
@@ -41,107 +41,132 @@
/**
* Azure Active Directory Authentication Client
*/
-public class ADALClientWrapper
-{
+public class ADALClientWrapper {
private String authority = "https://login.microsoftonline.com/";
private ClientCredential credential = null;
+ public AsymmetricKeyCredential asymmetricCredential = null;
private ExecutorService service = null;
private AuthenticationContext context = null;
-
+
/**
* Azure Active Directory Authentication Client
- * @param aadTenant - Azure Active Directory tenant
- * @param credential - Credential to use for authentication
+ *
+ * @param aadTenant
+ * - Azure Active Directory tenant
+ * @param credential
+ * - Credential to use for authentication
* @throws IllegalArgumentException
*/
- public ADALClientWrapper(String aadTenant, ClientCredential credential, Properties props) throws IllegalArgumentException
- {
- if(aadTenant == null || aadTenant.isEmpty())
- {
+ public ADALClientWrapper(String aadTenant, ClientCredential credential, Properties props) throws IllegalArgumentException {
+ if (aadTenant == null || aadTenant.isEmpty()) {
throw new IllegalArgumentException("The argument 'aadTenant' is missing");
}
-
- if(credential == null)
- {
- throw new IllegalArgumentException("The argument 'credential' is missing");
+
+ if (credential == null) {
+ throw new IllegalArgumentException("The argument 'credential' is missing");
}
-
- if(props != null)
- {
- this.authority = props.getProperty("AUTH_AUTHORITY",this.authority);
+
+ if (props != null) {
+ this.authority = props.getProperty("AUTH_AUTHORITY", this.authority);
}
-
+
this.credential = credential;
- this.service = Executors.newFixedThreadPool(1);
-
- try
- {
+ this.service = new CurrentThreadExecutor();
+
+ try {
context = new AuthenticationContext(this.authority + aadTenant, false, service);
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("AUTH_AUTHORITY parameter was not formatted correctly which resulted in a MalformedURLException", e);
+ }
+ }
+
+ /**
+ * Azure Active Directory Authentication Client
+ *
+ * @param aadTenant
+ * - Azure Active Directory tenant
+ * @param credential
+ * - Credential to use for authentication
+ * @throws IllegalArgumentException
+ */
+ public ADALClientWrapper(String aadTenant, AsymmetricKeyCredential credential, Properties props) throws IllegalArgumentException {
+ if (aadTenant == null || aadTenant.isEmpty()) {
+ throw new IllegalArgumentException("The argument 'aadTenant' is missing");
+ }
+
+ if (credential == null) {
+ throw new IllegalArgumentException("The argument 'credential' is missing");
+ }
+
+ if (props != null) {
+ this.authority = props.getProperty("AUTH_AUTHORITY", this.authority);
}
- catch(MalformedURLException e)
- {
+
+ this.asymmetricCredential = credential;
+ this.service = new CurrentThreadExecutor();
+
+ try {
+ context = new AuthenticationContext(this.authority + aadTenant, false, service);
+ } catch (MalformedURLException e) {
throw new IllegalArgumentException("AUTH_AUTHORITY parameter was not formatted correctly which resulted in a MalformedURLException", e);
}
}
-
+
/**
* Sets the SSL factory to be used on the HTTP client for authentication.
+ *
* @param factory
*/
- public void SetSslSocketFactory(SSLSocketFactory factory) throws IllegalArgumentException
- {
- if(factory == null)
- {
+ public void SetSslSocketFactory(SSLSocketFactory factory) throws IllegalArgumentException {
+ if (factory == null) {
throw new IllegalArgumentException("The argument 'factory' is missing.");
}
-
+
this.context.setSslSocketFactory(factory);
}
-
+
/**
* Sets the proxy to be used by the ADAL library for any HTTP or HTTPS calls
+ *
* @param proxy
*/
- public void SetProxy(Proxy proxy)
- {
+ public void SetProxy(Proxy proxy) {
this.context.setProxy(proxy);
}
-
+
/**
- * Gets an access token from AAD for the specified resource using the ClientCredential passed in.
- * @param resource Resource to get token for.
- * @param credential Credential to use to acquire token.
+ * Gets an access token from AAD for the specified resource using the
+ * ClientCredential passed in.
+ *
+ * @param resource
+ * Resource to get token for.
* @return
- * @throws ExecutionException
+ * @throws ExecutionException
* @throws IllegalArgumentException
- * @throws InterruptedException
- * @throws ServiceUnavailableException
+ * @throws InterruptedException
+ * @throws ServiceUnavailableException
*/
- public AuthenticationResult getAccessTokenFromCredential(String resource)
- throws ServiceUnavailableException, InterruptedException, ExecutionException, IllegalArgumentException
- {
- if(resource == null || resource.isEmpty())
- {
+ public AuthenticationResult getAccessTokenFromCredential(String resource)
+ throws ServiceUnavailableException, InterruptedException, ExecutionException, IllegalArgumentException {
+ if (resource == null || resource.isEmpty()) {
throw new IllegalArgumentException("The argument 'resource' is missing");
}
-
+
AuthenticationResult result = null;
-
- Future future = context.acquireToken(resource, credential, null);
+
+ Future future;
+ if (credential != null) {
+ future = context.acquireToken(resource, credential, null);
+ } else {
+ future = context.acquireToken(resource, asymmetricCredential, null);
+ }
result = future.get();
- if (result == null)
- {
+ if (result == null) {
throw new ServiceUnavailableException("Authentication result was null");
}
-
+
return result;
}
-
- @Override
- public void finalize()
- {
- service.shutdown();
- }
}
\ No newline at end of file
diff --git a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/CurrentThreadExecutor.java b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/CurrentThreadExecutor.java
new file mode 100644
index 0000000..7544e78
--- /dev/null
+++ b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/CurrentThreadExecutor.java
@@ -0,0 +1,49 @@
+package com.microsoft.intune.scepvalidation;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This is a non-thread safe ExecutorService implementation that processes all
+ * submit() calls immediately rather than running on them on a separate thread.
+ * Since ADALClientWrapper doesn't take advantage of concurrency, this should be
+ * more efficient, since it doesn't create a new thread with every new
+ * ADALClientWrapper instance.
+ */
+class CurrentThreadExecutor extends AbstractExecutorService {
+
+ boolean isShutdown = false;
+
+ @Override
+ public void shutdown() {
+ isShutdown = true;
+ }
+
+ @Override
+ public List shutdownNow() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return isShutdown;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return isShutdown;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return false;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+
+}
diff --git a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneClient.java b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneClient.java
index fcf095e..39a8356 100644
--- a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneClient.java
+++ b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneClient.java
@@ -29,6 +29,8 @@
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.UnknownHostException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -69,6 +71,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.microsoft.aad.adal4j.AsymmetricKeyCredential;
import com.microsoft.aad.adal4j.AuthenticationException;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
@@ -86,6 +89,7 @@ class IntuneClient
protected String intuneTenant;
protected ClientCredential aadCredential;
+ protected AsymmetricKeyCredential asymmetricAadCredential;
protected ADALClientWrapper authClient;
protected SSLSocketFactory sslSocketFactory = null;
@@ -110,6 +114,44 @@ public IntuneClient(Properties configProperties) throws IllegalArgumentException
this(configProperties, null, null);
}
+ /**
+ * Construct an Intune client which uses asymmetric key authentication.
+ *
+ * @param certificate client certificate
+ * @param privateKey client private key
+ * @param configProperties configuration that contains the usual fields,
+ * but without AAD_APP_KEY, since this instance uses the private key instead
+ * @param authClient previously established wrapper around client credentials.
+ * May be NULL.
+ * @param httpClientBuilder alternative http client builder. May be NULL.
+ */
+ public IntuneClient(X509Certificate certificate, PrivateKey privateKey, Properties configProperties, ADALClientWrapper authClient, HttpClientBuilder httpClientBuilder) {
+
+ if(configProperties == null)
+ {
+ throw new IllegalArgumentException("The argument 'configProperties' is missing");
+ }
+
+ // Read required properties
+ String azureAppId = configProperties.getProperty("AAD_APP_ID");
+ if(azureAppId == null || azureAppId.isEmpty())
+ {
+ throw new IllegalArgumentException("The argument 'AAD_APP_ID' is missing");
+ }
+
+ this.intuneTenant = configProperties.getProperty("TENANT");
+ if(this.intuneTenant == null || this.intuneTenant.isEmpty())
+ {
+ throw new IllegalArgumentException("The argument 'TENANT' is missing");
+ }
+
+ // Instantiate asymmetric ADAL Client
+ this.asymmetricAadCredential = AsymmetricKeyCredential.create(azureAppId, privateKey, certificate);
+ this.authClient = authClient == null ? new ADALClientWrapper(this.intuneTenant, this.asymmetricAadCredential, configProperties) : authClient;
+
+ commonConfiguration(configProperties, httpClientBuilder);
+ }
+
/**
* Constructs an IntuneClient object. This is meant to be used for unit tests for dependency injection.
* @param configProperties
@@ -142,17 +184,20 @@ public IntuneClient(Properties configProperties, ADALClientWrapper authClient, H
{
throw new IllegalArgumentException("The argument 'TENANT' is missing");
}
+ // Instantiate ADAL Client
+ this.aadCredential = new ClientCredential(azureAppId, azureAppKey);
+ this.authClient = authClient == null ? new ADALClientWrapper(this.intuneTenant, this.aadCredential, configProperties) : authClient;
+ commonConfiguration(configProperties, httpClientBuilder);
+ }
+
+ private void commonConfiguration(Properties configProperties, HttpClientBuilder httpClientBuilder) {
// Read optional properties
this.intuneAppId = configProperties.getProperty("INTUNE_APP_ID", this.intuneAppId);
this.intuneResourceUrl = configProperties.getProperty("INTUNE_RESOURCE_URL", this.intuneResourceUrl);
this.graphApiVersion = configProperties.getProperty("GRAPH_API_VERSION", this.graphApiVersion);
this.graphResourceUrl = configProperties.getProperty("GRAPH_RESOURCE_URL", this.graphResourceUrl);
- // Instantiate ADAL Client
- this.aadCredential = new ClientCredential(azureAppId, azureAppKey);
-
- this.authClient = authClient == null ? new ADALClientWrapper(this.intuneTenant, this.aadCredential, configProperties) : authClient;
this.httpClientBuilder = httpClientBuilder == null ? this.httpClientBuilder : httpClientBuilder;
proxyHost = configProperties.getProperty("PROXY_HOST");
diff --git a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneRevocationClient.java b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneRevocationClient.java
index d6e0a62..6458484 100644
--- a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneRevocationClient.java
+++ b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneRevocationClient.java
@@ -23,6 +23,8 @@
package com.microsoft.intune.scepvalidation;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
@@ -57,6 +59,16 @@ public class IntuneRevocationClient extends IntuneClient
final Logger log = LoggerFactory.getLogger(IntuneRevocationClient.class);
+
+ public IntuneRevocationClient(X509Certificate certificate, PrivateKey privateKey, Properties configProperties) {
+ this(certificate, privateKey, configProperties, null, null);
+ }
+
+ public IntuneRevocationClient(X509Certificate certificate, PrivateKey privateKey, Properties configProperties, ADALClientWrapper adalClient, HttpClientBuilder httpClientBuilder) throws IllegalArgumentException {
+ super(certificate, privateKey, configProperties, adalClient, httpClientBuilder);
+ commonInitialization(configProperties);
+ }
+
/**
* IntuneScepService Client constructor
* @param configProperties Properties object containing client configuration information.
@@ -77,7 +89,10 @@ public IntuneRevocationClient(Properties configProperties) throws IllegalArgumen
public IntuneRevocationClient(Properties configProperties, ADALClientWrapper adalClient, HttpClientBuilder httpClientBuilder) throws IllegalArgumentException
{
super(configProperties, adalClient, httpClientBuilder);
-
+ commonInitialization(configProperties);
+ }
+
+ private void commonInitialization(Properties configProperties) {
if(configProperties == null)
{
throw new IllegalArgumentException("The argument 'configProperties' is missing");
diff --git a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneScepServiceClient.java b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneScepServiceClient.java
index 783df88..e7df581 100644
--- a/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneScepServiceClient.java
+++ b/src/CsrValidation/java/lib/src/main/java/com/microsoft/intune/scepvalidation/IntuneScepServiceClient.java
@@ -23,6 +23,8 @@
package com.microsoft.intune.scepvalidation;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Properties;
import java.util.UUID;
@@ -61,6 +63,16 @@ public IntuneScepServiceClient(Properties configProperties) throws IllegalArgume
{
this(configProperties, null, null);
}
+
+ /**
+ * IntuneScepService Client constructor for asymmetric key authentcation
+ * @param configProperties Properties object containing client configuration information.
+ * @throws IllegalArgumentException
+ */
+ public IntuneScepServiceClient(X509Certificate certificate, PrivateKey key, Properties configProperties) throws IllegalArgumentException
+ {
+ this(certificate, key, configProperties, null, null);
+ }
/**
* IntuneScepService Client constructor meant for dependency injection
@@ -72,7 +84,23 @@ public IntuneScepServiceClient(Properties configProperties) throws IllegalArgume
public IntuneScepServiceClient(Properties configProperties, ADALClientWrapper adalClient, HttpClientBuilder httpClientBuilder) throws IllegalArgumentException
{
super(configProperties, adalClient, httpClientBuilder);
-
+ commonInitialization(configProperties);
+ }
+
+ /**
+ * IntuneScepService Client constructor meant for dependency injection
+ * @param configProperties
+ * @param adalClient
+ * @param httpClientBuilder
+ * @throws IllegalArgumentException
+ */
+ public IntuneScepServiceClient(X509Certificate certificate, PrivateKey key, Properties configProperties, ADALClientWrapper adalClient, HttpClientBuilder httpClientBuilder) throws IllegalArgumentException
+ {
+ super(certificate, key, configProperties, adalClient, httpClientBuilder);
+ commonInitialization(configProperties);
+ }
+
+ private void commonInitialization(Properties configProperties) {
if(configProperties == null)
{
throw new IllegalArgumentException("The argument 'configProperties' is missing");
diff --git a/src/CsrValidation/java/revocationexample/pom.xml b/src/CsrValidation/java/revocationexample/pom.xml
index 8a1c089..722913f 100644
--- a/src/CsrValidation/java/revocationexample/pom.xml
+++ b/src/CsrValidation/java/revocationexample/pom.xml
@@ -28,7 +28,7 @@
com.microsoft.intune.scep
csr-validation
- 0.0.1-SNAPSHOT
+ 20210505-SNAPSHOT
org.slf4j