Repository: SAP/cloud-security-xsuaa-integration
Pull request #644 (2021-08-26 10:03:39)

Bump mockito-core from 3.12.3 to 3.12.4
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.12.3 to 3.12.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/mockito/mockito/releases">mockito-core's releases</a>.</em></p>
<blockquote>
<h2>v3.12.4</h2>
<p><!-- raw HTML omitted --><!-- raw HTML omitted --><em>Changelog generated by <a href="https://github.com/shipkit/shipkit-changelog">Shipkit Changelog Gradle Plugin</a></em><!-- raw HTML omitted --><!-- raw HTML omitted --></p>
<h4>3.12.4</h4>
<ul>
<li>2021-08-25 - <a href="https://github.com/mockito/mockito/compare/v3.12.3...v3.12.4">1 commit(s)</a> by Rafael Winterhalter</li>
<li>No notable improvements. No pull requests (issues) were referenced from commits.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/mockito/mockito/commit/963a87202115ddab2334f634f0adbe985609bcca"><code>963a872</code></a> Properly resolve ProxyMockMaker by alias and handle null value as argument fo...</li>
<li>See full diff in <a href="https://github.com/mockito/mockito/compare/v3.12.3...v3.12.4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.mockito:mockito-core&package-manager=maven&previous-version=3.12.3&new-version=3.12.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe1c93d8..69d2222f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,8 @@
 All notable changes to this project will be documented in this file.
 
 ## 2.10.5
-- [token-client] 
+- [token-client]
+  - new method `SSLContextFactory.createKeyStore(ClientIdentity)`
   - `XsuaaTokenFlows` constructor accepts `com.sap.cloud.security.xsuaa.client.ClientCredentials` as argument.
   
 #### Dependency upgrades
diff --git a/env/pom.xml b/env/pom.xml
index 0f7f4019..5f6e09c3 100644
--- a/env/pom.xml
+++ b/env/pom.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-
+<!-- SPDX-FileCopyrightText: 2018-2021 SAP SE or an SAP affiliate company and Cloud Security Client Java contributors -->
+<!-- SPDX-License-Identifier: Apache-2.0 -->
 <project
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
diff --git a/pom.xml b/pom.xml
index 846a322f..9929a891 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,7 +77,7 @@
 		<junit-jupiter.version>5.7.2</junit-jupiter.version>
 		<maven-surefire-plugin>3.0.0-M5</maven-surefire-plugin>
 		<hamcrest.version>1.3</hamcrest.version>
-		<mockito.version>3.12.3</mockito.version>
+		<mockito.version>3.12.4</mockito.version>
 		<assertj.version>3.20.2</assertj.version>
 		<reactor.test.version>3.4.9</reactor.test.version>
 		<skipTests>false</skipTests>
diff --git a/token-client/src/main/java/com/sap/cloud/security/client/DefaultHttpClientFactory.java b/token-client/src/main/java/com/sap/cloud/security/client/DefaultHttpClientFactory.java
index e63fb252..17b55f27 100644
--- a/token-client/src/main/java/com/sap/cloud/security/client/DefaultHttpClientFactory.java
+++ b/token-client/src/main/java/com/sap/cloud/security/client/DefaultHttpClientFactory.java
@@ -28,13 +28,10 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
 	public CloseableHttpClient createClient(ClientIdentity clientIdentity) throws HttpClientException {
 		LOGGER.warn("In productive environment provide well configured HttpClientFactory service");
 		if (clientIdentity != null && clientIdentity.isCertificateBased()) {
-			LOGGER.debug("Setting up HTTPS client with: certificate: {}\nprivate key: {}\n",
-					clientIdentity.getCertificate(), clientIdentity.getKey());
-
+			LOGGER.debug("Setting up HTTPS client with: certificate: {}\n", clientIdentity.getCertificate());
 			SSLContext sslContext;
 			try {
-				sslContext = SSLContextFactory.getInstance().create(clientIdentity.getCertificate(),
-						clientIdentity.getKey());
+				sslContext = SSLContextFactory.getInstance().create(clientIdentity);
 			} catch (IOException | GeneralSecurityException e) {
 				throw new HttpClientException(
 						String.format("Couldn't set up https client for service provider. %s.",
diff --git a/token-client/src/main/java/com/sap/cloud/security/mtls/SSLContextFactory.java b/token-client/src/main/java/com/sap/cloud/security/mtls/SSLContextFactory.java
index ac502008..6d91b9cf 100644
--- a/token-client/src/main/java/com/sap/cloud/security/mtls/SSLContextFactory.java
+++ b/token-client/src/main/java/com/sap/cloud/security/mtls/SSLContextFactory.java
@@ -5,6 +5,9 @@
  */
 package com.sap.cloud.security.mtls;
 
+import com.sap.cloud.security.config.ClientCertificate;
+import com.sap.cloud.security.config.ClientIdentity;
+import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -23,6 +26,7 @@ import java.util.Base64;
 import java.util.Collection;
 
 import static com.sap.cloud.security.xsuaa.Assertions.assertHasText;
+import static com.sap.cloud.security.xsuaa.Assertions.assertNotNull;
 
 /**
  * Creates a SSLContext (without Bouncy Castle crypto lib).
@@ -46,9 +50,9 @@ public class SSLContextFactory {
 	 * order to support mutual TLS.
 	 *
 	 * @param x509Certificates
-	 *            you can get from your Service Configuration
+	 *            you can get from your Service Configuration {@link OAuth2ServiceConfiguration#getClientIdentity()}
 	 * @param rsaPrivateKey
-	 *            you can get from your Service Configuration
+	 *            you can get from your Service Configuration{@link OAuth2ServiceConfiguration#getClientIdentity()}
 	 * @return a new SSLContext instance
 	 * @throws GeneralSecurityException
 	 *             in case of key parsing errors
@@ -60,21 +64,57 @@ public class SSLContextFactory {
 		assertHasText(x509Certificates, "x509Certificate is required");
 		assertHasText(rsaPrivateKey, "rsaPrivateKey is required");
 
-		SSLContext sslContext = createDefaultSSLContext();
-
-		PrivateKey privateKey = getPrivateKeyFromString(rsaPrivateKey);
-		Certificate[] certificateChain = getCertificatesFromString(x509Certificates);
+		return create(new ClientCertificate(x509Certificates, rsaPrivateKey, null));
+	}
 
-		KeyStore keystore = initializeKeyStore(privateKey, certificateChain);
+	/**
+	 * Creates a SSLContext which can be used to parameterize your Rest client, in
+	 * order to support mutual TLS.
+	 *
+	 * @param clientIdentity
+	 *            you can get from your Service Configuration {@link OAuth2ServiceConfiguration#getClientIdentity()}
+	 * @return a new SSLContext instance
+	 * @throws GeneralSecurityException
+	 *             in case of key parsing errors
+	 * @throws IOException
+	 *             in case of KeyStore initialization errors
+	 */
+	public SSLContext create(ClientIdentity clientIdentity) throws GeneralSecurityException, IOException {
+		assertNotNull(clientIdentity, "clientIdentity must not be null");
+		assertHasText(clientIdentity.getCertificate(), "clientIdentity.getCertificate() must not return null");
+		assertHasText(clientIdentity.getKey(), "clientIdentity.getKey() must not return null");
 
+		KeyStore keystore = createKeyStore(clientIdentity);
 		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
 		keyManagerFactory.init(keystore, noPassword);
-
+		SSLContext sslContext = createDefaultSSLContext();
 		sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
-
 		return sslContext;
 	}
 
+	/**
+	 * Initializes a KeyStore which can be used to parameterize your Rest client, in
+	 * order to support mutual TLS.
+	 *
+	 * @param clientIdentity
+	 *            you can get from your Service Configuration {@link OAuth2ServiceConfiguration#getClientIdentity()}
+	 * @return a new KeyStore instance
+	 * @throws GeneralSecurityException
+	 *             in case of key parsing errors
+	 * @throws IOException
+	 *             in case of KeyStore initialization errors
+	 */
+	public KeyStore createKeyStore(ClientIdentity clientIdentity) throws GeneralSecurityException, IOException {
+		assertNotNull(clientIdentity, "clientIdentity must not be null");
+		assertHasText(clientIdentity.getCertificate(), "clientIdentity.getCertificate() must not return null");
+		assertHasText(clientIdentity.getKey(), "clientIdentity.getKey() must not return null");
+
+		PrivateKey privateKey = getPrivateKeyFromString(clientIdentity.getKey());
+		Certificate[] certificateChain = getCertificatesFromString(clientIdentity.getCertificate());
+
+		return initializeKeyStore(privateKey, certificateChain);
+	}
+
 	private KeyStore initializeKeyStore(PrivateKey privateKey, Certificate[] certificateChain)
 			throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
 		KeyStore keystore = KeyStore.getInstance("jks");
@@ -146,7 +186,6 @@ public class SSLContextFactory {
 					primeExponentQ,
 					crtCoefficient);
 		} catch (IOException e) {
-			logger.error("Exception during parsing DER encoded private key ({})", e.getMessage());
 			throw new GeneralSecurityException("Exception during parsing DER encoded private key", e);
 		}
 		return keySpec;


Test patch:
diff --git a/token-client/src/test/java/com/sap/cloud/security/mtls/SSLContextFactoryTest.java b/token-client/src/test/java/com/sap/cloud/security/mtls/SSLContextFactoryTest.java
index 59b13302..b141e868 100644
--- a/token-client/src/test/java/com/sap/cloud/security/mtls/SSLContextFactoryTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/mtls/SSLContextFactoryTest.java
@@ -14,6 +14,8 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 
+import com.sap.cloud.security.config.ClientCertificate;
+import com.sap.cloud.security.config.ClientCredentials;
 import com.sap.cloud.security.mtls.SSLContextFactory;
 import org.apache.commons.io.IOUtils;
 import org.junit.Before;
@@ -43,6 +45,18 @@ public class SSLContextFactoryTest {
 		assertThatThrownBy(() -> {
 			cut.create(certificates, null);
 		}).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("rsaPrivateKey");
+
+		assertThatThrownBy(() -> {
+			cut.create(null);
+		}).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity");
+
+		assertThatThrownBy(() -> {
+			cut.create(new ClientCredentials("clientId", "clientSecret"));
+		}).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity.getCertificate()");
+
+		assertThatThrownBy(() -> {
+			cut.create(new ClientCertificate("certificate", null, null));
+		}).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity.getKey()");
 	}
 
 	@Test
@@ -54,7 +68,23 @@ public class SSLContextFactoryTest {
 		assertThat(cut.create(certificates, rsaPrivateKey), is(notNullValue()));
 	}
 
-	public String readFromFile(String file) throws IOException {
+	@Test
+	public void createKeyStore() throws GeneralSecurityException, IOException {
+		assertThat(cut.createKeyStore(new ClientCertificate(certificates, rsaPrivateKey, null)), is(notNullValue()));
+	}
+
+	@Test
+	public void createKeyStore_throwsOnNullValues() {
+		assertThatThrownBy(() -> {
+			cut.createKeyStore(new ClientCredentials("clientId", "clientSecret"));
+		}).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity.getCertificate()");
+
+		assertThatThrownBy(() -> {
+			cut.createKeyStore(new ClientCertificate("certificate", null, null));
+		}).isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity.getKey()");
+	}
+
+	private String readFromFile(String file) throws IOException {
 		return IOUtils.resourceToString(file, StandardCharsets.UTF_8);
 	}