Skip to content

Commit

Permalink
Added some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Hakky54 committed Jan 28, 2024
1 parent 149ffaa commit 9b4c48f
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -868,16 +868,16 @@ public SSLFactory build() {
SSLParameters baseSslParameters = createSslParameters(baseSslContext);
SSLContext sslContext = new FenixSSLContext(baseSslContext, baseSslParameters);

HostnameVerifier hostnameVerifier = Optional.ofNullable(hostnameVerifierEnhancer)
.map(enhancer -> HostnameVerifierUtils.createEnhanceable(this.hostnameVerifier, enhancer))
.orElse(this.hostnameVerifier);
HostnameVerifier resolvedHostnameVerifier = Optional.ofNullable(hostnameVerifierEnhancer)
.map(enhancer -> HostnameVerifierUtils.createEnhanceable(hostnameVerifier, enhancer))
.orElse(hostnameVerifier);

SSLMaterial sslMaterial = new SSLMaterial.Builder()
.withSslContext(sslContext)
.withKeyManager(keyManager)
.withTrustManager(trustManager)
.withSslParameters(baseSslParameters)
.withHostnameVerifier(hostnameVerifier)
.withHostnameVerifier(resolvedHostnameVerifier)
.withCiphers(Collections.unmodifiableList(Arrays.asList(baseSslParameters.getCipherSuites())))
.withProtocols(Collections.unmodifiableList(Arrays.asList(baseSslParameters.getProtocols())))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,9 @@ static boolean containsInvalidLabelLengths(String hostname) {
if (labelLength < 1 || labelLength > 63) {
return true;
}
if (dot == -1) {
if (dot == -1 || dot == hostname.length() - 1 ) {
break;
}
if (dot == hostname.length() - 1) {
break; // Trailing '.' is allowed.
}
labelStart = dot + 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
public class InflatableX509ExtendedTrustManager extends HotSwappableX509ExtendedTrustManager {

private static final Logger LOGGER = LoggerFactory.getLogger(InflatableX509ExtendedTrustManager.class);
private static final BiPredicate<KeyStore, X509Certificate> IGNORE_DUPLICATE_CHECKER = (trustStore, certificate) -> false;
private static final BiPredicate<KeyStore, X509Certificate> IGNORE_DUPLICATE_CHECKER = (t, c) -> false;

private final KeyStore trustStore;
private final Path trustStorePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import nl.altindag.log.LogCaptor;
import nl.altindag.log.model.LogEvent;
import nl.altindag.ssl.exception.GenericKeyStoreException;
import nl.altindag.ssl.model.TrustManagerParameters;
import nl.altindag.ssl.util.CertificateUtils;
import nl.altindag.ssl.util.KeyStoreUtils;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
Expand Down Expand Up @@ -46,16 +48,20 @@
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static nl.altindag.ssl.TestConstants.DER_LOCATION;
import static nl.altindag.ssl.TestConstants.HOME_DIRECTORY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

Expand Down Expand Up @@ -265,8 +271,10 @@ void addNewlyTrustedCertificatesWhileAlsoWritingToAKeyStoreOnTheFileSystem() thr

assertThat(trustedCerts).hasSizeGreaterThan(0);

Path trustStoreDestination = Paths.get(HOME_DIRECTORY, "inflatable-truststore.p12");
assertThat(Files.exists(trustStoreDestination)).isFalse();
Path destinationDirectory = Paths.get(HOME_DIRECTORY, "hakky54-ssl");
Path trustStoreDestination = destinationDirectory.resolve("inflatable-truststore.p12");
Files.createDirectories(destinationDirectory);
assertThat(Files.exists(destinationDirectory)).isTrue();

InflatableX509ExtendedTrustManager trustManager = new InflatableX509ExtendedTrustManager(trustStoreDestination, "secret".toCharArray(), "PKCS12", trustManagerParameters -> true);
trustManager.addCertificates(Arrays.asList(trustedCerts));
Expand All @@ -278,7 +286,27 @@ void addNewlyTrustedCertificatesWhileAlsoWritingToAKeyStoreOnTheFileSystem() thr
KeyStore inflatedTrustStore = KeyStoreUtils.loadKeyStore(trustStoreDestination, "secret".toCharArray(), "PKCS12");
assertThat(KeyStoreTestUtils.getTrustedX509Certificates(inflatedTrustStore)).containsExactly(trustedCerts);

Files.delete(trustStoreDestination);
Files.deleteIfExists(trustStoreDestination);
Files.deleteIfExists(destinationDirectory);
}

@Test
void addOnlyOnceNewlyTrustedCertificates() throws KeyStoreException {
LogCaptor logCaptor = LogCaptor.forClass(InflatableX509ExtendedTrustManager.class);
KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
X509Certificate[] trustedCerts = KeyStoreTestUtils.getTrustedX509Certificates(trustStore);

assertThat(trustedCerts).hasSizeGreaterThan(0);

InflatableX509ExtendedTrustManager trustManager = new InflatableX509ExtendedTrustManager(null, null, null, trustManagerParameters -> true);
trustManager.addCertificates(Arrays.asList(trustedCerts));

assertThat(trustManager.getAcceptedIssuers()).containsExactly(trustedCerts);
assertThat(logCaptor.getInfoLogs()).containsExactly("Added certificate for [cn=googlecom_o=google-llc_l=mountain-view_st=california_c=us]");
logCaptor.clearLogs();

trustManager.addCertificates(Arrays.asList(trustedCerts));
assertThat(logCaptor.getInfoLogs()).isEmpty();
}

@Test
Expand Down Expand Up @@ -310,6 +338,26 @@ void callPredicateAndAddCertificatesIfTrusted() throws KeyStoreException, IOExce
Files.delete(trustStoreDestination);
}

@Test
void notAddCertificateIfInnerTrustManagerHasTrustedTheNewCertificateFromADifferentThread() throws KeyStoreException, CertificateException {
X509ExtendedTrustManager innerTrustManager = mock(X509ExtendedTrustManager.class);
doThrow(new CertificateException("KABOOOM!"))
.doNothing()
.when(innerTrustManager)
.checkServerTrusted(any(), anyString());

KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
X509Certificate notYetTrustedCert = KeyStoreTestUtils.getTrustedX509Certificates(trustStore)[0];

AtomicBoolean shouldTrust = new AtomicBoolean(true);
Predicate<TrustManagerParameters> predicate = trustManagerParameters -> shouldTrust.get();
InflatableX509ExtendedTrustManager trustManager = new InflatableX509ExtendedTrustManager(null, null, null, predicate);
trustManager.setTrustManager(innerTrustManager);

trustManager.checkServerTrusted(new X509Certificate[] {notYetTrustedCert}, "RSA");
assertThat(trustManager.getInnerTrustManager()).isEqualTo(innerTrustManager);
}

@Test
void onlyCallPredicateOnceWhenConcurrentThreadsCheckForTheSameCertificate() throws KeyStoreException, IOException, InterruptedException, ExecutionException {
LogCaptor logCaptor = LogCaptor.forClass(InflatableX509ExtendedTrustManager.class);
Expand Down Expand Up @@ -478,4 +526,55 @@ void checkClientTrustedWithSSLEngine() throws CertificateException {
verify(innerTrustManager, times(1)).checkClientTrusted(chain, authType, sslEngine);
}

@Test
void wrapKeyStoreExceptionIntoGenericKeyStoreExceptionWhenCallingGenerateAlias() throws KeyStoreException, IOException {
LogCaptor logCaptor = LogCaptor.forClass(InflatableX509ExtendedTrustManager.class);

KeyStore trustStore = spy(KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD));
doThrow(new KeyStoreException("KABOOM!")).when(trustStore).containsAlias(anyString());

List<X509Certificate> notYetTrustedCerts = CertificateUtils.loadCertificate(DER_LOCATION + "digicert.cer")
.stream()
.filter(X509Certificate.class::isInstance)
.map(X509Certificate.class::cast)
.collect(Collectors.toList());

assertThat(notYetTrustedCerts).hasSize(1);

Path destinationDirectory = Paths.get(HOME_DIRECTORY, "hakky54-ssl");
Path trustStoreDestination = destinationDirectory.resolve("inflatable-truststore.p12");
Files.createDirectories(destinationDirectory);
assertThat(Files.exists(destinationDirectory)).isTrue();

KeyStoreUtils.write(trustStoreDestination, trustStore, TRUSTSTORE_PASSWORD);
assertThat(Files.exists(trustStoreDestination)).isTrue();

try(MockedStatic<KeyStoreUtils> mockedStatic = mockStatic(KeyStoreUtils.class, invocationOnMock -> {
Method method = invocationOnMock.getMethod();
if ("loadKeyStore".equals(method.getName())) {
return trustStore;
} else {
return invocationOnMock.callRealMethod();
}
})) {
InflatableX509ExtendedTrustManager trustManager = new InflatableX509ExtendedTrustManager(trustStoreDestination, "secret".toCharArray(), "PKCS12", trustManagerParameters -> true);
assertThat(trustManager.getAcceptedIssuers()).hasSize(1);

trustManager.addCertificates(notYetTrustedCerts);
assertThat(trustManager.getAcceptedIssuers()).hasSize(1);

List<LogEvent> logEvents = logCaptor.getLogEvents();
assertThat(logEvents).hasSize(1);

LogEvent logEvent = logEvents.get(0);
assertThat(logEvent.getLevel()).isEqualTo("ERROR");
assertThat(logEvent.getMessage()).isEqualTo("Cannot add certificate");
assertThat(logEvent.getThrowable()).isPresent();
assertThat(logEvent.getThrowable().get()).isInstanceOf(GenericKeyStoreException.class);
} finally {
Files.deleteIfExists(trustStoreDestination);
Files.deleteIfExists(destinationDirectory);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -62,6 +62,7 @@
import static nl.altindag.ssl.TestConstants.TRUSTSTORE_PASSWORD;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -535,6 +536,54 @@ void isNotSelfSigned() {
assertThat(selfSigned).isFalse();
}

@Test
void wrapCertificateExceptionIntoGenericCertificateExceptionWhenIsSelfSignedCallFails() throws CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException {
List<Certificate> certificates = CertificateUtils.loadCertificate(PEM_LOCATION + "not-self-signed.pem");
assertThat(certificates).hasSize(1);
Certificate certificate = spy(certificates.get(0));

doThrow(new CertificateException("KABOOM!")).when(certificate).verify(any());

assertThatThrownBy(() -> CertificateUtils.isSelfSigned(certificate))
.isInstanceOf(GenericCertificateException.class);
}

@Test
void wrapNoSuchAlgorithmExceptionIntoGenericCertificateExceptionWhenIsSelfSignedCallFails() throws CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException {
List<Certificate> certificates = CertificateUtils.loadCertificate(PEM_LOCATION + "not-self-signed.pem");
assertThat(certificates).hasSize(1);
Certificate certificate = spy(certificates.get(0));

doThrow(new NoSuchAlgorithmException("KABOOM!")).when(certificate).verify(any());

assertThatThrownBy(() -> CertificateUtils.isSelfSigned(certificate))
.isInstanceOf(GenericCertificateException.class);
}

@Test
void wrapInvalidKeyExceptionIntoGenericCertificateExceptionWhenIsSelfSignedCallFails() throws CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException {
List<Certificate> certificates = CertificateUtils.loadCertificate(PEM_LOCATION + "not-self-signed.pem");
assertThat(certificates).hasSize(1);
Certificate certificate = spy(certificates.get(0));

doThrow(new InvalidKeyException("KABOOM!")).when(certificate).verify(any());

assertThatThrownBy(() -> CertificateUtils.isSelfSigned(certificate))
.isInstanceOf(GenericCertificateException.class);
}

@Test
void wrapNoSuchProviderExceptionIntoGenericCertificateExceptionWhenIsSelfSignedCallFails() throws CertificateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException {
List<Certificate> certificates = CertificateUtils.loadCertificate(PEM_LOCATION + "not-self-signed.pem");
assertThat(certificates).hasSize(1);
Certificate certificate = spy(certificates.get(0));

doThrow(new NoSuchProviderException("KABOOM!")).when(certificate).verify(any());

assertThatThrownBy(() -> CertificateUtils.isSelfSigned(certificate))
.isInstanceOf(GenericCertificateException.class);
}

@Test
void notAddSubjectAndIssuerAsHeaderWhenCertificateTypeIsNotX509Certificate() throws CertificateEncodingException {
Certificate certificate = mock(Certificate.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package nl.altindag.ssl.util;

import nl.altindag.ssl.hostnameverifier.BasicHostnameVerifier;
import nl.altindag.ssl.hostnameverifier.FenixHostnameVerifier;
import nl.altindag.ssl.hostnameverifier.UnsafeHostnameVerifier;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -46,4 +47,22 @@ void createUnsafe() {
.isInstanceOf(UnsafeHostnameVerifier.class);
}

@Test
void createFenix() {
HostnameVerifier hostnameVerifier = HostnameVerifierUtils.createFenix();

assertThat(hostnameVerifier)
.isNotNull()
.isInstanceOf(FenixHostnameVerifier.class);
}

@Test
void createDefault() {
HostnameVerifier hostnameVerifier = HostnameVerifierUtils.createDefault();

assertThat(hostnameVerifier)
.isNotNull()
.isInstanceOf(FenixHostnameVerifier.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,25 @@ void createTrustStoreFromMultipleTrustManagers() throws KeyStoreException {
assertThat(trustStore.size()).isEqualTo(jdkTrustManager.getAcceptedIssuers().length + customTrustManager.getAcceptedIssuers().length);
}

@Test
void returnTrueWhenItDoesContainsCertificate() {
KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
X509ExtendedTrustManager trustManager = TrustManagerUtils.createTrustManager(trustStore);
X509Certificate[] acceptedIssuers = trustManager.getAcceptedIssuers();
assertThat(acceptedIssuers).isNotEmpty();

X509Certificate certificates = acceptedIssuers[0];
assertThat(KeyStoreUtils.containsCertificate(trustStore, certificates)).isTrue();
}

@Test
void returnFalseWhenItDoesNotContainsCertificate() {
KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);

X509Certificate certificates = mock(X509Certificate.class);
assertThat(KeyStoreUtils.containsCertificate(trustStore, certificates)).isFalse();
}

@Test
void countAmountOfTrustMaterial() {
KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
Expand Down Expand Up @@ -666,7 +685,7 @@ void getAliasToCertificate() {
void getAliasToCertificateFiltersOutKeyMaterial() throws KeyStoreException {
KeyStore keyStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, KEYSTORE_PASSWORD);

assertThat(keyStore.size()).isGreaterThan(0);
assertThat(keyStore.size()).isPositive();

Map<String, Certificate> aliasToCertificate = KeyStoreUtils.getAliasToCertificate(keyStore);
assertThat(aliasToCertificate).isEmpty();
Expand Down Expand Up @@ -700,6 +719,28 @@ void createKeyStoreIfAvailableReturnsFilledKeyStore() {
}
}

@Test
void wrapKeyStoreExceptionIntoAGenericKeyStoreExceptionWhenCallingContainsCertificateFails() throws KeyStoreException {
KeyStore keyStore = mock(KeyStore.class);
Certificate certificate = mock(Certificate.class);

doThrow((new KeyStoreException("KABOOM!"))).when(keyStore).getCertificateAlias(certificate);

assertThatThrownBy(() -> KeyStoreUtils.containsCertificate(keyStore, certificate))
.isInstanceOf(GenericKeyStoreException.class);
}

@Test
void wrapKeyStoreExceptionIntoAGenericKeyStoreExceptionWhenCallingContainsTrustMaterialFails() throws KeyStoreException {
KeyStore keyStore = mock(KeyStore.class);

when(keyStore.aliases()).thenReturn(Collections.enumeration(Collections.singletonList("some-alias")));
doThrow((new KeyStoreException("KABOOM!"))).when(keyStore).isCertificateEntry("some-alias");

assertThatThrownBy(() -> KeyStoreUtils.containsTrustMaterial(keyStore))
.isInstanceOf(GenericKeyStoreException.class);
}

@Test
void createKeyStoreIfAvailableReturnsFilledKeyStoreWithoutLoggingIfDebugIsDisabled() {
LogCaptor logCaptor = LogCaptor.forClass(KeyStoreUtils.class);
Expand Down
Loading

0 comments on commit 9b4c48f

Please sign in to comment.