Verify that keystore has certificate with alias inside it

Jetty was silently failing, setting up HTTPS and then failing handshake.
If the alias in the config was not in the keystore https would enable,
but the certificate would be missing.

Added validation steps from old code to ensure the keystore includes given alias.
This commit is contained in:
Aurora Lahtela 2022-08-20 08:56:27 +03:00
parent aee3988f77
commit eaae1456d6
5 changed files with 66 additions and 1 deletions

View File

@ -110,4 +110,26 @@ public class WebserverLogMessages {
logger.warn(locale.getString(PluginLang.WEB_SERVER_NOTIFY_CERT_EXPIRE_DATE_PASSED));
}
}
public void invalidCertificateMissingAlias(String alias, String keystorePath) {
logger.error(locale.getString(PluginLang.WEB_SERVER_NOTIFY_CERT_NO_SUCH_ALIAS, alias, keystorePath));
}
public void unableToLoadKeystore(Exception e, String keystorePath) {
logger.error(locale.getString(PluginLang.WEB_SERVER_FAIL_STORE_LOAD));
errorLogger.error(e, ErrorContext.builder()
.whatToDo("Make sure the Certificate settings are correct / You can try remaking the keystore without -passin or -passout parameters.")
.related(keystorePath).build());
}
public void wrongCertFileFormat() {
logger.error(locale.getString(PluginLang.WEB_SERVER_FAIL_EMPTY_FILE));
}
public void keystoreLoadingError(Exception e) {
errorLogger.error(e, ErrorContext.builder()
.logErrorMessage()
.whatToDo("Make sure the Certificate settings are correct / You can try remaking the keystore without -passin or -passout parameters.")
.build());
}
}

View File

@ -32,10 +32,15 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@ -197,6 +202,10 @@ public class JettyWebserver implements WebServer {
return legacyJettySSLContextLoader.load(keyStorePath, storepass, keypass, alias);
}
if (!verifyAliasIsInKeystore(keyStorePath, storepass, alias)) {
return Optional.empty();
}
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setSniRequired(false);
@ -207,6 +216,29 @@ public class JettyWebserver implements WebServer {
return Optional.of(sslContextFactory);
}
private boolean verifyAliasIsInKeystore(String keyStorePath, String storepass, String alias) {
String keyStoreKind = keyStorePath.endsWith(".p12") ? "PKCS12" : "JKS";
try (FileInputStream fIn = new FileInputStream(keyStorePath)) {
KeyStore keystore = KeyStore.getInstance(keyStoreKind);
keystore.load(fIn, storepass.toCharArray());
Certificate cert = keystore.getCertificate(alias);
if (cert == null) {
webserverLogMessages.invalidCertificateMissingAlias(alias, keyStorePath);
return false;
}
return true;
} catch (KeyStoreException | CertificateException e) {
webserverLogMessages.unableToLoadKeystore(e, keyStorePath);
} catch (EOFException e) {
webserverLogMessages.wrongCertFileFormat();
} catch (NoSuchAlgorithmException | IOException e) {
webserverLogMessages.keystoreLoadingError(e);
}
return false;
}
@Override
public boolean isEnabled() {
return webserver != null && (webserver.isStarting() || webserver.isStarted());

View File

@ -54,6 +54,7 @@ public enum PluginLang implements Lang {
WEB_SERVER_NOTIFY_CERT_EXPIRE_DATE("plugin.webserver.notify.certificateExpiresOn", "Webserver notify - Cert expiry", "Webserver: Loaded certificate is valid until ${0}."),
WEB_SERVER_NOTIFY_CERT_EXPIRE_DATE_SOON("plugin.webserver.notify.certificateExpiresSoon", "Webserver notify - Cert expiry soon", "Webserver: Certificate expires in ${0}, consider renewing the certificate."),
WEB_SERVER_NOTIFY_CERT_EXPIRE_DATE_PASSED("plugin.webserver.notify.certificateExpiresPassed", "Webserver notify - Cert expiry passed", "Webserver: Certificate has expired, consider renewing the certificate."),
WEB_SERVER_NOTIFY_CERT_NO_SUCH_ALIAS("plugin.webserver.notify.certificateNoSuchAlias", "Webserver notify - Cert no alias", "Webserver: Certificate with alias '${0}' was not found inside the keystore file '${1}'."),
DISABLED("plugin.disable.disabled", "Disable", "Player Analytics Disabled."),
DISABLED_WEB_SERVER("plugin.disable.webserver", "Disable - WebServer", "Webserver has been disabled."),

View File

@ -28,6 +28,7 @@ public class ErrorContext implements Serializable {
private final transient List<Object> related;
private String whatToDo;
private boolean logErrorMessage = false;
private ErrorContext() {
related = new ArrayList<>();
@ -41,6 +42,10 @@ public class ErrorContext implements Serializable {
return Optional.ofNullable(whatToDo);
}
public boolean shouldLogErrorMessage() {
return logErrorMessage;
}
public Collection<String> toLines() {
List<String> lines = new ArrayList<>();
getWhatToDo().ifPresent(lines::add);
@ -71,6 +76,11 @@ public class ErrorContext implements Serializable {
return this;
}
public Builder logErrorMessage() {
context.logErrorMessage = true;
return this;
}
public Builder related(Object related) {
context.related.add(related);
return this;

View File

@ -205,7 +205,7 @@ public class PluginErrorLogger implements ErrorLogger {
String errorMsg = throwable.getMessage();
String errorLocation = errorLog.toString();
return new String[]{
"Ran into " + errorName + " - logged to " + errorLocation,
"Ran into " + errorName + (context.shouldLogErrorMessage() ? ": " + throwable.getMessage() : "") + " - logged to " + errorLocation,
"(INCLUDE CONTENTS OF THE FILE IN ANY REPORTS)",
context.getWhatToDo().map(td -> "What to do: " + td).orElse("Error msg: \"" + errorMsg + "\"")
};