Skip to content

Commit

Permalink
feat: use certificates for trust management (#26)
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Becker <[email protected]>
  • Loading branch information
sbckr authored Oct 8, 2024
1 parent f8b5760 commit e9e5bca
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 - for information on the respective copyright owner
* Copyright (c) 2021-2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/java-http-client.
*
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -13,10 +13,10 @@
import java.util.List;
import javax.net.ssl.X509TrustManager;

class CompositeX509TrustManager implements X509TrustManager {
public class CompositeX509TrustManager implements X509TrustManager {
private final List<X509TrustManager> managers;

CompositeX509TrustManager(List<X509TrustManager> managers) {
public CompositeX509TrustManager(List<X509TrustManager> managers) {
this.managers = new ArrayList<>(managers);
}

Expand Down
22 changes: 7 additions & 15 deletions src/main/java/io/carbynestack/httpclient/CsHttpClient.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 - for information on the respective copyright owner
* Copyright (c) 2021-2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/java-http-client.
*
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -13,10 +13,7 @@
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Stream;
import javax.net.ssl.X509TrustManager;
import org.apache.http.Header;
Expand Down Expand Up @@ -72,17 +69,12 @@ public CsHttpClient(
sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContextBuilder.build(), new NoopHostnameVerifier());
} else {
List<Optional<X509TrustManager>> custom = new ArrayList<>();
for (File f : withTrustedCertificates) {
custom.add(X509TrustManagerUtils.getX509TrustManager(f));
}
List<X509TrustManager> allTrustManagers =
Stream.concat(
custom.stream(),
Stream.of(X509TrustManagerUtils.getDefaultX509TrustManager()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
Stream.of(X509TrustManagerUtils.getX509TrustManager(withTrustedCertificates),
X509TrustManagerUtils.getDefaultX509TrustManager())
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
SSLContextBuilder sslContextBuilder =
ExtendedSSLContextBuilder.create(new CompositeX509TrustManager(allTrustManagers));
sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 - for information on the respective copyright owner
* Copyright (c) 2021-2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/java-http-client.
*
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -16,14 +16,14 @@
import javax.net.ssl.X509TrustManager;
import org.apache.http.ssl.SSLContextBuilder;

class ExtendedSSLContextBuilder extends SSLContextBuilder {
public class ExtendedSSLContextBuilder extends SSLContextBuilder {
private final X509TrustManager trustManager;

private ExtendedSSLContextBuilder(X509TrustManager trustManager) {
this.trustManager = trustManager;
}

static ExtendedSSLContextBuilder create(X509TrustManager trustManager) {
public static ExtendedSSLContextBuilder create(X509TrustManager trustManager) {
return new ExtendedSSLContextBuilder(trustManager);
}

Expand Down
31 changes: 20 additions & 11 deletions src/main/java/io/carbynestack/httpclient/X509TrustManagerUtils.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
/*
* Copyright (c) 2021 - for information on the respective copyright owner
* Copyright (c) 2021-2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/java-http-client.
*
* SPDX-License-Identifier: Apache-2.0
*/
package io.carbynestack.httpclient;

import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

final class X509TrustManagerUtils {
public final class X509TrustManagerUtils {

static Optional<X509TrustManager> getX509TrustManager(KeyStore keyStore)
public static Optional<X509TrustManager> getX509TrustManager(KeyStore keyStore)
throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
Expand All @@ -31,17 +34,23 @@ static Optional<X509TrustManager> getX509TrustManager(KeyStore keyStore)
.findFirst();
}

static Optional<X509TrustManager> getDefaultX509TrustManager()
public static Optional<X509TrustManager> getDefaultX509TrustManager()
throws NoSuchAlgorithmException, KeyStoreException {
return getX509TrustManager((KeyStore) null);
}

static Optional<X509TrustManager> getX509TrustManager(File file)
public static Optional<X509TrustManager> getX509TrustManager(List<File> certs)
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
try (FileInputStream fis = new FileInputStream(file)) {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(fis, null);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
keyStore.load(null);
for (File certificate : certs) {
X509Certificate x509Certificate =
(X509Certificate)
certificateFactory.generateCertificate(Files.newInputStream(certificate.toPath()));
keyStore.setCertificateEntry(
x509Certificate.getSubjectX500Principal().getName(), x509Certificate);
}
return getX509TrustManager(keyStore);
}
}
}
22 changes: 13 additions & 9 deletions src/test/java/io/carbynestack/httpclient/SslValidationIT.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 - for information on the respective copyright owner
* Copyright (c) 2021-2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/java-http-client.
*
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -18,6 +18,7 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.Objects;
import javax.net.ssl.SSLContext;
Expand All @@ -28,8 +29,11 @@
import org.junit.jupiter.api.extension.RegisterExtension;

public class SslValidationIT {
private static final String KEY_STORE_A_PATH =
Objects.requireNonNull(SslValidationIT.class.getClassLoader().getResource("keyStoreA.jks"))
private static final String KEY_STORE_PATH =
Objects.requireNonNull(SslValidationIT.class.getClassLoader().getResource("keyStore.jks"))
.getPath();
private static final String CERTIFICATE_PATH =
Objects.requireNonNull(SslValidationIT.class.getClassLoader().getResource("cli_test.pem"))
.getPath();
private static final String KEY_STORE_A_PASSWORD = "verysecure";
private static final String GOOGLE_DIRECTIONS_REST_URI =
Expand All @@ -44,7 +48,7 @@ public class SslValidationIT {
wireMockConfig()
.dynamicPort()
.dynamicHttpsPort()
.keystorePath(KEY_STORE_A_PATH)
.keystorePath(KEY_STORE_PATH)
.keystorePassword(KEY_STORE_A_PASSWORD)
.keyManagerPassword(KEY_STORE_A_PASSWORD))
.build();
Expand Down Expand Up @@ -82,7 +86,7 @@ public void givenTrustedCertificate_whenGettingObject_thenSucceeds()
CsHttpClient<String> csHttpClient =
CsHttpClient.<String>builder()
.withFailureType(String.class)
.withTrustedCertificates(Collections.singletonList(new File(KEY_STORE_A_PATH)))
.withTrustedCertificates(Collections.singletonList(new File(CERTIFICATE_PATH)))
.build();
assertThat(csHttpClient.getForObject(testUri, String.class)).isEqualTo(SUCCESS_RESPONSE_STRING);
}
Expand All @@ -99,7 +103,7 @@ public void givenSslValidationDisabled_whenGettingObject_thenSucceeds()
}

@Test
public void givenNonExistingTrustStore_whenBuildingClient_thenThrows() {
public void givenNonExistingCertificate_whenBuildingClient_thenThrows() {
File nonExistingFile = new File("");
assertThatThrownBy(
() ->
Expand All @@ -108,7 +112,7 @@ public void givenNonExistingTrustStore_whenBuildingClient_thenThrows() {
.withTrustedCertificates(Collections.singletonList(nonExistingFile))
.build())
.isExactlyInstanceOf(CsHttpClientException.class)
.hasCauseInstanceOf(IOException.class);
.hasCauseInstanceOf(CertificateException.class);
}

@Test
Expand Down Expand Up @@ -137,12 +141,12 @@ public void givenOfficiallyTrustedTarget_whenGettingEntity_thenSucceedWithBadReq

@Test
public void
givenOfficiallyTrustedTargetAndCustomTruststore_whenGettingEntity_thenSucceedWithBadRequest()
givenOfficiallyTrustedTargetAndCustomCertificate_whenGettingEntity_thenSucceedWithBadRequest()
throws CsHttpClientException, URISyntaxException {
CsHttpClient<GoogleDirectionsResponse> csHttpClient =
CsHttpClient.<GoogleDirectionsResponse>builder()
.withFailureType(GoogleDirectionsResponse.class)
.withTrustedCertificates(Collections.singletonList(new File(KEY_STORE_A_PATH)))
.withTrustedCertificates(Collections.singletonList(new File(CERTIFICATE_PATH)))
.build();
CsResponseEntity<GoogleDirectionsResponse, String> csResponseEntity =
csHttpClient.getForEntity(new URI(GOOGLE_DIRECTIONS_REST_URI), String.class);
Expand Down
52 changes: 52 additions & 0 deletions src/test/resources/cli_test.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
jPpR6xcsp11tDuNLwFR5oXtjlurS
-----END PRIVATE KEY-----
30 changes: 30 additions & 0 deletions src/test/resources/cli_test.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFJTCCAw2gAwIBAgIUKn+PjY2heJxokM2T9kyjvbEzNqkwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTAwMTA3MDYzMFoXDTM0MDky
OTA3MDYzMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA1LwXNTCD2hEYwNqoEK14QEEKfaTi8qjTeRiArlvfufRH
1UGWgqXtCGgxKcn6hbsCe+gKXUVaM8jV8/JOZkE7LtggMekYwUDwu36FQ469Ikbm
TjFnF+M9twq3Vte5VZaMJQidSzjzMp2zTxuDPZENCEpEecZPHNAqSQpCCofAW0TH
Vi8EwCMKFG+v2rUhJcTMmQrfY7rN1aGI4Wued3UjLkbAMthNdIZbcwPgtEkDHreG
hvQXhtJkIxBEJevHANaXq8utzusaf2GxYNA4bYNG3ryxksMJatv+fMDIu/eRTmBU
hO92tfhd8YRJGjSRdmuKdtJ2Z6QafDVay7ZsUV/ifM+Iwc3YSGEhpZuzT8kjFcld
iEMPTTPwHQ6tRTT8yRUDbFtYwYQwpuIvs56wiaf6sACi+Vf2f8Wnc4XD5TRAZrn5
n0LEzVw0qF0iCvY+fgF8bFEfLrZbGENjqhESN+Z2b5jNQjiXrcUriu47vdpcCtqs
8+U5hOFLO7uy9tyBI3w3dNIQqOHyPwt9FsgI+OdicaugjgY+z+iKQCKO7UPERiES
Lhtl9PJSlJ8fb7R6W4jfyxlXkfRcuYChtjCsWXCTXR5rp2tQO9A1OeklrYvS9hxi
w3CZGSEuZfRYBMArtfbrGO1NB+KJDXQpph3JDvE0BWHEG/e7f4k0zn554jlmfD8C
AwEAAaNvMG0wHQYDVR0OBBYEFFnntPA4FCI6uUF41u3Xf6E0CbF+MB8GA1UdIwQY
MBaAFFnntPA4FCI6uUF41u3Xf6E0CbF+MA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0R
BBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQDPitpt2wJe
jnVU+yuS7a8BO/Rcj1oahrpG9wGgUfitI+vbIymBTVXWPFJw//QkBpkLRUH/WNJi
8ukIbRy/05v3Pqm+sJW9Jqp6G7cJV+cx29XgDGq0+g1JWtGRECobcADunE53Pu7J
vpgnnAGuQXshcv2ae7V/YGJnYY0vPncfTPnBr3EWzXba/nUhbB+laVXw6epUU8K2
fru9oVwmKDpKup9Gej93nr08V6EtgjmPVMdkqUSk9ZxXOoS0a4AvNVaSv9lRjc5F
KiePdJz14x4AYFIUBRxiWCiNeMkustmvaTyJdhz/P8Y4py7nSff3kzwm/x0dF13N
fhTk+xNXBiI6C+a7pkgqvBpc+7ZjKe5kOqIujHcpIkk85YgZO9saKPZUanhuqJV8
3CKdoCfFQyGMydoXhB7CWay0puUJ4pCml7Xe5EuYQxTPN/KDZ5Isxb76qCRuuZzB
Ifo21Xaa6iJSszH7KTCtvEmDuSKdTaiAx/EA+nERg4ipQO0JKbVNf4JpxyVA6dSl
XtfzMS4Ho+2sQw2Fh1kkgY+KX0YZyopCAERVBLMgV5dbNNaOlnmYjU2GVFXy+VIX
Q2QJgBxbUm8H2Og5LQWhp14WRfNmLampVBa49wx8qVxpkkprk3DauO6F4fSyCkYf
+mkzUQn+6cmtZCYBQWeoXjlWJeYwei04cQ==
-----END CERTIFICATE-----
Binary file added src/test/resources/keyStore.jks
Binary file not shown.
Binary file removed src/test/resources/keyStoreA.jks
Binary file not shown.

0 comments on commit e9e5bca

Please sign in to comment.