import java.security.cert.CertificateException;
import java.time.Duration;
import java.time.format.DateTimeParseException;
+import javax.annotation.Nullable;
import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.exception.GenericKeyStoreException;
+import nl.altindag.ssl.util.KeyStoreUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static SslConfig parseSslConfig(ScannerProperties scannerProperties, SonarUserHome sonarUserHome) {
var keyStorePath = defaultIfBlank(scannerProperties.property("sonar.scanner.keystorePath"), sonarUserHome.getPath().resolve("ssl/keystore.p12").toString());
- var keyStorePassword = defaultIfBlank(scannerProperties.property("sonar.scanner.keystorePassword"), CertificateStore.DEFAULT_PASSWORD);
- var trustStorePath = defaultIfBlank(scannerProperties.property("sonar.scanner.truststorePath"), sonarUserHome.getPath().resolve("ssl/truststore.p12").toString());
- var trustStorePassword = defaultIfBlank(scannerProperties.property("sonar.scanner.truststorePassword"), CertificateStore.DEFAULT_PASSWORD);
+ var keyStorePassword = scannerProperties.property("sonar.scanner.keystorePassword");
var keyStore = new CertificateStore(Path.of(keyStorePath), keyStorePassword);
+ var trustStorePath = defaultIfBlank(scannerProperties.property("sonar.scanner.truststorePath"), sonarUserHome.getPath().resolve("ssl/truststore.p12").toString());
+ var trustStorePassword = scannerProperties.property("sonar.scanner.truststorePassword");
var trustStore = new CertificateStore(Path.of(trustStorePath), trustStorePassword);
return new SslConfig(keyStore, trustStore);
}
}
var keyStoreConfig = sslConfig.getKeyStore();
if (keyStoreConfig != null && Files.exists(keyStoreConfig.getPath())) {
- sslFactoryBuilder.withIdentityMaterial(keyStoreConfig.getPath(), keyStoreConfig.getKeyStorePassword().toCharArray(), keyStoreConfig.getKeyStoreType());
+ keyStoreConfig.getKeyStorePassword()
+ .ifPresentOrElse(
+ password -> sslFactoryBuilder.withIdentityMaterial(keyStoreConfig.getPath(), password.toCharArray(), keyStoreConfig.getKeyStoreType()),
+ () -> loadIdentityMaterialWithDefaultPassword(sslFactoryBuilder, keyStoreConfig.getPath()));
}
var trustStoreConfig = sslConfig.getTrustStore();
if (trustStoreConfig != null && Files.exists(trustStoreConfig.getPath())) {
KeyStore trustStore;
try {
- trustStore = loadKeyStoreWithBouncyCastle(
+ trustStore = loadTrustStoreWithBouncyCastle(
trustStoreConfig.getPath(),
- trustStoreConfig.getKeyStorePassword().toCharArray(),
+ trustStoreConfig.getKeyStorePassword().orElse(null),
trustStoreConfig.getKeyStoreType());
LOG.debug("Loaded truststore from '{}' containing {} certificates", trustStoreConfig.getPath(), trustStore.size());
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException e) {
return sslFactoryBuilder.build();
}
- static KeyStore loadKeyStoreWithBouncyCastle(Path keystorePath, char[] keystorePassword, String keystoreType) throws IOException,
+ private static void loadIdentityMaterialWithDefaultPassword(SSLFactory.Builder sslFactoryBuilder, Path path) {
+ try {
+ var keystore = KeyStoreUtils.loadKeyStore(path, CertificateStore.DEFAULT_PASSWORD.toCharArray(), CertificateStore.DEFAULT_STORE_TYPE);
+ sslFactoryBuilder.withIdentityMaterial(keystore, CertificateStore.DEFAULT_PASSWORD.toCharArray());
+ } catch (GenericKeyStoreException e) {
+ var keystore = KeyStoreUtils.loadKeyStore(path, CertificateStore.OLD_DEFAULT_PASSWORD.toCharArray(), CertificateStore.DEFAULT_STORE_TYPE);
+ LOG.warn("Using deprecated default password for keystore '{}'.", path);
+ sslFactoryBuilder.withIdentityMaterial(keystore, CertificateStore.OLD_DEFAULT_PASSWORD.toCharArray());
+ }
+ }
+
+ static KeyStore loadTrustStoreWithBouncyCastle(Path keystorePath, @Nullable String keystorePassword, String keystoreType) throws IOException,
KeyStoreException, CertificateException, NoSuchAlgorithmException {
+ KeyStore keystore = KeyStore.getInstance(keystoreType, new BouncyCastleProvider());
+ if (keystorePassword != null) {
+ loadKeyStoreWithPassword(keystorePath, keystore, keystorePassword);
+ } else {
+ try {
+ loadKeyStoreWithPassword(keystorePath, keystore, CertificateStore.DEFAULT_PASSWORD);
+ } catch (Exception e) {
+ loadKeyStoreWithPassword(keystorePath, keystore, CertificateStore.OLD_DEFAULT_PASSWORD);
+ LOG.warn("Using deprecated default password for truststore '{}'.", keystorePath);
+ }
+ }
+ return keystore;
+ }
+
+ private static void loadKeyStoreWithPassword(Path keystorePath, KeyStore keystore, String oldDefaultPassword) throws IOException, NoSuchAlgorithmException, CertificateException {
try (InputStream keystoreInputStream = Files.newInputStream(keystorePath, StandardOpenOption.READ)) {
- KeyStore keystore = KeyStore.getInstance(keystoreType, new BouncyCastleProvider());
- keystore.load(keystoreInputStream, keystorePassword);
- return keystore;
+ keystore.load(keystoreInputStream, oldDefaultPassword.toCharArray());
}
}
package org.sonar.scanner.http.ssl;
import java.nio.file.Path;
+import java.util.Optional;
+import javax.annotation.Nullable;
public class CertificateStore {
- public static final String DEFAULT_PASSWORD = "sonar";
+ public static final String DEFAULT_PASSWORD = "changeit";
+ /**
+ * @deprecated it was a bad decision to use this value as default password, as the keytool utility requires a password to be at least 6 characters long
+ */
+ @Deprecated(since = "11.4")
+ public static final String OLD_DEFAULT_PASSWORD = "sonar";
public static final String DEFAULT_STORE_TYPE = "PKCS12";
private final Path path;
private final String keyStorePassword;
private final String keyStoreType;
- public CertificateStore(Path path, String keyStorePassword) {
+ public CertificateStore(Path path, @Nullable String keyStorePassword) {
this.path = path;
this.keyStorePassword = keyStorePassword;
this.keyStoreType = DEFAULT_STORE_TYPE;
return path;
}
- public String getKeyStorePassword() {
- return keyStorePassword;
+ public Optional<String> getKeyStorePassword() {
+ return Optional.ofNullable(keyStorePassword);
}
public String getKeyStoreType() {
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
+import javax.annotation.Nullable;
import nl.altindag.ssl.exception.GenericKeyStoreException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
import org.junitpioneer.jupiter.RestoreSystemProperties;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.System2;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatNoException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
assertThat(httpConnector.okHttpClient().proxy()).isNull();
}
+ @ParameterizedTest
+ @CsvSource({
+ "keystore_changeit.p12, wrong, false",
+ "keystore_changeit.p12, changeit, true",
+ "keystore_changeit.p12,, true",
+ "keystore_sonar.p12, wrong, false",
+ "keystore_sonar.p12, sonar, true",
+ "keystore_sonar.p12,, true",
+ "keystore_anotherpwd.p12, wrong, false",
+ "keystore_anotherpwd.p12, anotherpwd, true",
+ "keystore_anotherpwd.p12,, false"})
+ void it_should_fail_if_invalid_truststore_password(String keystore, @Nullable String password, boolean shouldSucceed) {
+ scannerProps.put("sonar.scanner.truststorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/" + keystore))).toString());
+ if (password != null) {
+ scannerProps.put("sonar.scanner.truststorePassword", password);
+ }
+
+ var scannerPropsObj = new ScannerProperties(scannerProps);
+ if (shouldSucceed) {
+ assertThatNoException().isThrownBy(() -> underTest.provide(scannerPropsObj, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome));
+ } else {
+ assertThatThrownBy(() -> underTest.provide(scannerPropsObj, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome))
+ .isInstanceOf(GenericKeyStoreException.class)
+ .hasMessageContaining("Unable to read truststore from")
+ .hasStackTraceContaining("wrong password or corrupted file");
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "keystore_changeit.p12, wrong, false",
+ "keystore_changeit.p12, changeit, true",
+ "keystore_changeit.p12,, true",
+ "keystore_sonar.p12, wrong, false",
+ "keystore_sonar.p12, sonar, true",
+ "keystore_sonar.p12,, true",
+ "keystore_anotherpwd.p12, wrong, false",
+ "keystore_anotherpwd.p12, anotherpwd, true",
+ "keystore_anotherpwd.p12,, false"})
+ void it_should_fail_if_invalid_keystore_password(String keystore, @Nullable String password, boolean shouldSucceed) {
+ scannerProps.put("sonar.scanner.keystorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/" + keystore))).toString());
+ if (password != null) {
+ scannerProps.put("sonar.scanner.keystorePassword", password);
+ }
+
+ var scannerPropsObj = new ScannerProperties(scannerProps);
+ if (shouldSucceed) {
+ assertThatNoException().isThrownBy(() -> underTest.provide(scannerPropsObj, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome));
+ } else {
+ assertThatThrownBy(() -> underTest.provide(scannerPropsObj, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome))
+ .isInstanceOf(GenericKeyStoreException.class)
+ .hasMessageContaining("keystore password was incorrect");
+ }
+ }
+
@Nested
class WithMockHttpSonarQube {
assertThat(r.content()).isEqualTo("Success");
}
}
-
- @Test
- void it_should_fail_if_invalid_truststore_password() {
- scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
- scannerProps.put("sonar.scanner.truststorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client-truststore.p12"))).toString());
- scannerProps.put("sonar.scanner.truststorePassword", "wrong_password");
-
- var scannerPropsObj = new ScannerProperties(scannerProps);
- var thrown = assertThrows(GenericKeyStoreException.class,
- () -> underTest.provide(scannerPropsObj, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome));
-
- assertThat(thrown).hasStackTraceContaining("Unable to read truststore");
- }
}
@Nested