Skip to content

Commit 7266416

Browse files
committed
SCANJLIB-232 Add support for truststore generated by openssl
Using the Bouncycastle library, we can now load trusted keys from a pkcs12 keystore that has not been created by keytool.
1 parent 565fcda commit 7266416

13 files changed

Lines changed: 125 additions & 34 deletions

File tree

its/it-tests/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
<artifactId>assertj-core</artifactId>
6060
<scope>test</scope>
6161
</dependency>
62+
<dependency>
63+
<groupId>com.tngtech.java</groupId>
64+
<artifactId>junit-dataprovider</artifactId>
65+
<version>1.13.1</version>
66+
<scope>test</scope>
67+
</dependency>
6268

6369
<!-- Logging -->
6470
<dependency>

its/it-tests/src/test/java/com/sonar/scanner/lib/it/SSLTest.java

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import com.sonar.orchestrator.junit4.OrchestratorRule;
2424
import com.sonar.orchestrator.util.NetworkUtils;
2525
import com.sonar.scanner.lib.it.tools.SimpleScanner;
26+
import com.tngtech.java.junit.dataprovider.DataProvider;
27+
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
28+
import com.tngtech.java.junit.dataprovider.UseDataProvider;
2629
import java.net.InetAddress;
2730
import java.nio.file.Path;
2831
import java.nio.file.Paths;
@@ -47,17 +50,22 @@
4750
import org.junit.Before;
4851
import org.junit.ClassRule;
4952
import org.junit.Test;
53+
import org.junit.runner.RunWith;
5054

5155
import static org.assertj.core.api.Assertions.assertThat;
5256

57+
@RunWith(DataProviderRunner.class)
5358
public class SSLTest {
5459

5560
// This keystore contains only the CA used to sign the server certificate
56-
private static final String KEYSTORE_CLIENT_WITH_CA = "/SSLTest/client-with-ca.p12";
61+
private static final String KEYSTORE_CLIENT_WITH_CA_KEYTOOL = "/SSLTest/client-with-ca-keytool.p12";
62+
private static final String KEYSTORE_CLIENT_WITH_CA_OPENSSL = "/SSLTest/client-with-ca-openssl.p12";
5763
private static final String CLIENT_WITH_CA_KEYSTORE_PASSWORD = "pwdClientCAP12";
5864

5965
// This keystore contains only the server certificate
60-
private static final String KEYSTORE_CLIENT_WITH_CERTIFICATE = "/SSLTest/client-with-certificate.p12";
66+
private static final String KEYSTORE_CLIENT_WITH_CERTIFICATE_KEYTOOL = "/SSLTest/client-with-certificate-keytool.p12";
67+
private static final String KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL = "/SSLTest/client-with-certificate-openssl.p12";
68+
private static final String KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL_JDKTRUST = "/SSLTest/client-with-certificate-openssl-jdktrust.p12";
6169
private static final String CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD = "pwdClientP12";
6270

6371
private static final String SERVER_KEYSTORE = "/SSLTest/server.p12";
@@ -171,7 +179,7 @@ public void simple_analysis_with_server_and_client_certificate() throws Exceptio
171179
assertThat(buildResult.getLastStatus()).isNotZero();
172180
assertThat(buildResult.getLogs()).contains("javax.net.ssl.SSLHandshakeException");
173181

174-
Path clientTruststore = Paths.get(SSLTest.class.getResource(KEYSTORE_CLIENT_WITH_CA).toURI()).toAbsolutePath();
182+
Path clientTruststore = Paths.get(SSLTest.class.getResource(KEYSTORE_CLIENT_WITH_CA_KEYTOOL).toURI()).toAbsolutePath();
175183
assertThat(clientTruststore).exists();
176184
Path clientKeystore = Paths.get(SSLTest.class.getResource(CLIENT_KEYSTORE).toURI()).toAbsolutePath();
177185
assertThat(clientKeystore).exists();
@@ -197,7 +205,7 @@ public void simple_analysis_with_server_and_without_client_certificate_is_failin
197205
assertThat(buildResult.getLastStatus()).isNotZero();
198206
assertThat(buildResult.getLogs()).contains("javax.net.ssl.SSLHandshakeException");
199207

200-
Path clientTruststore = Paths.get(SSLTest.class.getResource(KEYSTORE_CLIENT_WITH_CA).toURI()).toAbsolutePath();
208+
Path clientTruststore = Paths.get(SSLTest.class.getResource(KEYSTORE_CLIENT_WITH_CA_KEYTOOL).toURI()).toAbsolutePath();
201209
assertThat(clientTruststore).exists();
202210
Path clientKeystore = Paths.get(SSLTest.class.getResource(CLIENT_KEYSTORE).toURI()).toAbsolutePath();
203211
assertThat(clientKeystore).exists();
@@ -227,16 +235,8 @@ private static Path project(String projectName) {
227235
}
228236

229237
@Test
230-
public void simple_analysis_with_server_certificate_using_ca_in_truststore() throws Exception {
231-
simple_analysis_with_server_certificate(KEYSTORE_CLIENT_WITH_CA, CLIENT_WITH_CA_KEYSTORE_PASSWORD);
232-
}
233-
234-
@Test
235-
public void simple_analysis_with_server_certificate_using_server_certificate_in_truststore() throws Exception {
236-
simple_analysis_with_server_certificate(KEYSTORE_CLIENT_WITH_CERTIFICATE, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD);
237-
}
238-
239-
private void simple_analysis_with_server_certificate(String clientTrustStore, String keyStorePassword) throws Exception {
238+
@UseDataProvider("variousClientTrustStores")
239+
public void simple_analysis_with_server_certificate(String clientTrustStore, String keyStorePassword, boolean useJavaSslProperties) throws Exception {
240240
startSSLTransparentReverseProxy(false);
241241
SimpleScanner scanner = new SimpleScanner();
242242

@@ -248,10 +248,28 @@ private void simple_analysis_with_server_certificate(String clientTrustStore, St
248248
assertThat(clientTruststore).exists();
249249

250250
Map<String, String> params = new HashMap<>();
251-
params.put("javax.net.ssl.trustStore", clientTruststore.toString());
252-
params.put("javax.net.ssl.trustStorePassword", keyStorePassword);
251+
if (useJavaSslProperties) {
252+
params.put("javax.net.ssl.trustStore", clientTruststore.toString());
253+
params.put("javax.net.ssl.trustStorePassword", keyStorePassword);
254+
} else {
255+
params.put("sonar.scanner.truststorePath", clientTruststore.toString());
256+
params.put("sonar.scanner.truststorePassword", keyStorePassword);
257+
}
253258

254259
buildResult = scanner.executeSimpleProject(project("js-sample"), "https://localhost:" + httpsPort, params, Map.of());
260+
System.out.println(buildResult.getLogs());
255261
assertThat(buildResult.getLastStatus()).isZero();
256262
}
263+
264+
@DataProvider()
265+
public static Object[][] variousClientTrustStores() {
266+
return new Object[][] {
267+
{KEYSTORE_CLIENT_WITH_CA_KEYTOOL, CLIENT_WITH_CA_KEYSTORE_PASSWORD, true},
268+
{KEYSTORE_CLIENT_WITH_CA_OPENSSL, CLIENT_WITH_CA_KEYSTORE_PASSWORD, false},
269+
{KEYSTORE_CLIENT_WITH_CERTIFICATE_KEYTOOL, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, true},
270+
{KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, false},
271+
{KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL_JDKTRUST, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, false},
272+
{KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL_JDKTRUST, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, true}
273+
};
274+
}
257275
}

its/it-tests/src/test/java/com/sonar/scanner/lib/it/tools/SimpleScanner.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ public class SimpleScanner {
3939

4040
public SimpleScanner() {
4141
if (!Files.exists(JAR_PATH)) {
42-
throw new IllegalStateException("Could not find simple-scanner artifact in " + JAR_PATH.toString());
42+
throw new IllegalStateException("Could not find simple-scanner artifact in " + JAR_PATH);
4343
}
4444
javaBin = Paths.get(System.getProperty("java.home"), "bin", "java");
4545
if (!Files.exists(javaBin)) {
46-
throw new IllegalStateException("Could not find java in " + javaBin.toString());
46+
throw new IllegalStateException("Could not find java in " + javaBin);
4747
}
4848

4949
exec = new CommandExecutor(javaBin);
@@ -75,7 +75,7 @@ private Map<String, String> getSimpleProjectProperties(Path baseDir, String host
7575
analysisProperties.load(Files.newInputStream(propertiesFile));
7676
analysisProperties.setProperty("sonar.projectBaseDir", baseDir.toAbsolutePath().toString());
7777
analysisProperties.setProperty("sonar.host.url", host);
78-
analysisProperties.setProperty("sonar.login", ScannerJavaLibraryTestSuite.ORCHESTRATOR.getDefaultAdminToken());
78+
analysisProperties.setProperty("sonar.token", ScannerJavaLibraryTestSuite.ORCHESTRATOR.getDefaultAdminToken());
7979
analysisProperties.putAll(extraProps);
8080
return (Map) analysisProperties;
8181
}

its/it-tests/src/test/resources/SSLTest/README.md

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -286,14 +286,14 @@ The `server.p12` file can now be used to start a TLS server.
286286

287287
#### Now let's a client to connect to our TLS server
288288

289-
Since we've created a self signed certificate. The client must either have our certificate (without the private key) or must have the CA certificate.
289+
Since we've created a self-signed certificate. The client must either have our certificate (without the private key) or must have the CA certificate.
290290

291-
##### Let's create a JKS with the server certificate
291+
##### Let's create a PKCS12 keystore with the server certificate
292292

293-
This one is more easier :
293+
Keytool is adding extra fields to the keystore, that are not supported by older versions of openssl. Waiting for openssl 3.3.0+ and the possibility to use the `-jdktrust anyExtendedKeyUsage` option, we have to use keytool to create the keystore.
294294

295295
```bash
296-
$ keytool -import -alias localhost -keystore client-with-certificate.p12 -file server.pem
296+
$ keytool -import -storetype PKCS12 -alias localhost -keystore client-with-certificate-keytool.p12 -file server.pem
297297
Enter keystore password: pwdClientP12
298298
Re-enter new password: pwdClientP12
299299
Owner: CN=localhost, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH
@@ -314,10 +314,23 @@ $ keytool -import -alias localhost -keystore client-with-certificate.p12 -file s
314314
The password is `pwdClientP12` and you have to `Trust this certificate`.
315315
This PKCS12 file must be used in a TrustedKeyStore.
316316

317-
##### Let's create a JKS with CA certificate
317+
At the moment, Java can only support openssl truststore if using the new property `-jdktrust`
318+
```bash
319+
$ openssl version
320+
OpenSSL 3.3.1 4 Jun 2024 (Library: OpenSSL 3.3.1 4 Jun 2024)
321+
$ openssl pkcs12 -export -caname localhost -nokeys -in server.pem -out client-with-certificate-openssl-jdktrust.p12 --passout pass:pwdClientP12 -jdktrust anyExtendedKeyUsage
322+
```
323+
324+
Thanks to bouncycastle addition, we can now accept openssl generated PKCS12 keystore out-of-the-box.
318325

319326
```bash
320-
$ keytool -import -trustcacerts -alias localhost -keystore client-with-ca.p12 -file ca.crt
327+
$ openssl pkcs12 -export -caname localhost -nokeys -in server.pem -out client-with-certificate-openssl.p12 --passout pass:pwdClientP12
328+
```
329+
330+
##### Let's also create a PKCS12 keystore with only the CA certificate
331+
332+
```bash
333+
$ keytool -import -storetype PKCS12 -trustcacerts -alias localhost -keystore client-with-ca-keytool.p12 -file ca.crt
321334
Enter keystore password: pwdClientCAP12
322335
Re-enter new password: pwdClientCAP12
323336
Owner: CN=SonarSource, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH
@@ -363,6 +376,12 @@ $ keytool -import -trustcacerts -alias localhost -keystore client-with-ca.p12 -f
363376
The password is `pwdClientCAP12` and you have to `Trust this certificate`.
364377
This PKCS12 file must be used in a TrustedKeyStore.
365378

379+
Thanks to bouncycastle addition, we can now accept openssl generated PKCS12 keystore out-of-the-box:
380+
381+
```bash
382+
$ openssl pkcs12 -export -caname localhost -nokeys -in ca.crt -out client-with-ca-openssl.p12 --passout pass:pwdClientCAP12
383+
```
384+
366385
### Create a certificate that will be used to authenticate a user
367386

368387
The principle is the same we'll have a CA authority signing certificates that will be send by the user to the server.
@@ -401,7 +420,7 @@ subject=C = CH, ST = Geneva, L = Geneva, O = SonarSource SA, CN = Julien Henry
401420
Getting CA Private Key
402421
```
403422

404-
Let's create the pkcs12 certificate containing the client certificate
423+
Let's create the pkcs12 keystore containing the client certificate
405424

406425
```bash
407426
$ openssl pkcs12 -export -in client.pem -inkey client.key -name julienhenry -out client.p12
@@ -413,7 +432,7 @@ This will go to client keyStore.
413432
Now we'll generate the `server-with-client-ca.p12` file that will have the CA certificate. Since we don't need to add the key of the certificate (only required to sign, not to verify), we can import it directly with keytool.
414433

415434
```bash
416-
$ keytool -import -trustcacerts -alias client-ca -keystore server-with-client-ca.p12 -file ca-client-auth.crt
435+
$ keytool -import -storetype PKCS12 -trustcacerts -alias client-ca -keystore server-with-client-ca.p12 -file ca-client-auth.crt
417436
Enter keystore password: pwdServerWithClientCA
418437
Re-enter new password: pwdServerWithClientCA
419438
Owner: CN=SonarSource, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH

its/it-tests/src/test/resources/SSLTest/client-with-ca.p12 renamed to its/it-tests/src/test/resources/SSLTest/client-with-ca-keytool.p12

File renamed without changes.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)