From e57c1b5013266e1a0d38ba91342fdff7c882e935 Mon Sep 17 00:00:00 2001 From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com> Date: Tue, 19 Mar 2024 20:46:54 +0100 Subject: [PATCH] Added option to ignore invalid SSL certificates --- .../executor/HttpClientExecutor.java | 4 +- .../commons/httpclient/HttpClient.java | 19 ++++++ .../httpclient/executor/RequestExecutor.java | 4 ++ .../executor/URLConnectionExecutor.java | 6 ++ .../httpclient/requests/HttpRequest.java | 68 ++++++++++++++----- .../utils/IgnoringTrustManager.java | 54 +++++++++++++++ .../httpclient/utils/ResettableStorage.java | 34 ++++++++++ 7 files changed, 170 insertions(+), 19 deletions(-) create mode 100644 commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/IgnoringTrustManager.java create mode 100644 commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/ResettableStorage.java diff --git a/commons-httpclient/src/j11/java/net/lenni0451/commons/httpclient/executor/HttpClientExecutor.java b/commons-httpclient/src/j11/java/net/lenni0451/commons/httpclient/executor/HttpClientExecutor.java index 396f234..f4cc900 100644 --- a/commons-httpclient/src/j11/java/net/lenni0451/commons/httpclient/executor/HttpClientExecutor.java +++ b/commons-httpclient/src/j11/java/net/lenni0451/commons/httpclient/executor/HttpClientExecutor.java @@ -7,6 +7,7 @@ import net.lenni0451.commons.httpclient.proxy.ProxyType; import net.lenni0451.commons.httpclient.requests.HttpContentRequest; import net.lenni0451.commons.httpclient.requests.HttpRequest; +import net.lenni0451.commons.httpclient.utils.IgnoringTrustManager; import net.lenni0451.commons.httpclient.utils.URLWrapper; import javax.annotation.Nonnull; @@ -44,10 +45,11 @@ public HttpResponse execute(@Nonnull final HttpRequest request) throws IOExcepti return new HttpResponse(new URLWrapper(response.uri()).toURL(), response.statusCode(), response.body(), response.headers().map()); } - private java.net.http.HttpClient buildClient(final HttpRequest request) { + private java.net.http.HttpClient buildClient(final HttpRequest request) throws IOException { java.net.http.HttpClient.Builder builder = java.net.http.HttpClient.newBuilder(); CookieManager cookieManager = this.getCookieManager(request); if (cookieManager != null) builder.cookieHandler(cookieManager); + if (this.isIgnoreInvalidSSL(request)) builder.sslContext(IgnoringTrustManager.makeIgnoringSSLContext()); builder.connectTimeout(Duration.ofMillis(this.client.getConnectTimeout())); switch (request.getFollowRedirects()) { case NOT_SET: diff --git a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/HttpClient.java b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/HttpClient.java index 00811ee..e55b6e8 100644 --- a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/HttpClient.java +++ b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/HttpClient.java @@ -29,6 +29,7 @@ public class HttpClient extends HeaderStore implements HttpRequestBu private int readTimeout = 10_000; private RetryHandler retryHandler = new RetryHandler(); private ProxyHandler proxyHandler = new ProxyHandler(); + private boolean ignoreInvalidSSL = false; /** * Create a new http client with the default executor. @@ -178,6 +179,24 @@ public void setProxyHandler(@Nonnull final ProxyHandler proxyHandler) { this.proxyHandler = proxyHandler; } + /** + * @return Whether invalid SSL certificates should be ignored + */ + public boolean isIgnoreInvalidSSL() { + return this.ignoreInvalidSSL; + } + + /** + * Set whether invalid SSL certificates should be ignored. + * + * @param ignoreInvalidSSL Whether invalid SSL certificates should be ignored + * @return This instance for chaining + */ + public HttpClient setIgnoreInvalidSSL(final boolean ignoreInvalidSSL) { + this.ignoreInvalidSSL = ignoreInvalidSSL; + return this; + } + /** * Execute a request and pass the response to the response handler.
* The return value of the response handler will be returned. diff --git a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/RequestExecutor.java b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/RequestExecutor.java index 534df58..76fc8a2 100644 --- a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/RequestExecutor.java +++ b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/RequestExecutor.java @@ -34,6 +34,10 @@ protected final CookieManager getCookieManager(@Nonnull final HttpRequest reques return request.isCookieManagerSet() ? request.getCookieManager() : this.client.getCookieManager(); } + protected final boolean isIgnoreInvalidSSL(@Nonnull final HttpRequest request) { + return request.isIgnoreInvalidSSLSet() ? request.getIgnoreInvalidSSL() : this.client.isIgnoreInvalidSSL(); + } + protected final Map> getHeaders(@Nonnull final HttpRequest request, @Nullable final CookieManager cookieManager) throws IOException { Map> headers = new HashMap<>(); if (request instanceof HttpContentRequest) { diff --git a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/URLConnectionExecutor.java b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/URLConnectionExecutor.java index f00d404..c87b10d 100644 --- a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/URLConnectionExecutor.java +++ b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/executor/URLConnectionExecutor.java @@ -6,9 +6,11 @@ import net.lenni0451.commons.httpclient.requests.HttpContentRequest; import net.lenni0451.commons.httpclient.requests.HttpRequest; import net.lenni0451.commons.httpclient.utils.HttpRequestUtils; +import net.lenni0451.commons.httpclient.utils.IgnoringTrustManager; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.OutputStream; import java.net.CookieManager; @@ -38,6 +40,10 @@ private HttpURLConnection openConnection(final HttpRequest request, final Cookie if (proxySelector != null) proxySelector.set(); URL url = request.getURL(); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + if (this.isIgnoreInvalidSSL(request) && connection instanceof HttpsURLConnection) { + HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; + httpsConnection.setSSLSocketFactory(IgnoringTrustManager.makeIgnoringSSLContext().getSocketFactory()); + } this.setupConnection(connection, cookieManager, request); connection.connect(); return connection; diff --git a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/requests/HttpRequest.java b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/requests/HttpRequest.java index e326bd9..cf2e1d5 100644 --- a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/requests/HttpRequest.java +++ b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/requests/HttpRequest.java @@ -2,6 +2,7 @@ import net.lenni0451.commons.httpclient.HeaderStore; import net.lenni0451.commons.httpclient.RetryHandler; +import net.lenni0451.commons.httpclient.utils.ResettableStorage; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -14,12 +15,9 @@ public class HttpRequest extends HeaderStore { private final String method; private final URL url; private FollowRedirects followRedirects = FollowRedirects.NOT_SET; - @Nullable - private CookieManager cookieManager; - private boolean cookieManagerSet = false; - @Nonnull - private RetryHandler retryHandler = new RetryHandler(); - private boolean retryHandlerSet = false; + private final ResettableStorage cookieManager = new ResettableStorage<>(); + private final ResettableStorage retryHandler = new ResettableStorage<>(); + private final ResettableStorage ignoreInvalidSSL = new ResettableStorage<>(); public HttpRequest(final String method, final String url) throws MalformedURLException { this(method, new URL(url)); @@ -76,7 +74,7 @@ public HttpRequest setFollowRedirects(@Nonnull final FollowRedirects followRedir * @return If the cookie manager is set */ public boolean isCookieManagerSet() { - return this.cookieManagerSet; + return this.cookieManager.isSet(); } /** @@ -85,17 +83,17 @@ public boolean isCookieManagerSet() { * @return This instance for chaining */ public HttpRequest unsetCookieManager() { - this.cookieManager = null; - this.cookieManagerSet = false; + this.cookieManager.unset(); return this; } /** * @return The set cookie manager + * @throws IllegalStateException If the cookie manager is not set */ @Nullable public CookieManager getCookieManager() { - return this.cookieManager; + return this.cookieManager.get(); } /** @@ -105,8 +103,7 @@ public CookieManager getCookieManager() { * @return This instance for chaining */ public HttpRequest setCookieManager(@Nullable final CookieManager cookieManager) { - this.cookieManager = cookieManager; - this.cookieManagerSet = true; + this.cookieManager.set(cookieManager); return this; } @@ -114,7 +111,7 @@ public HttpRequest setCookieManager(@Nullable final CookieManager cookieManager) * @return If the retry handler is set */ public boolean isRetryHandlerSet() { - return this.retryHandlerSet; + return this.retryHandler.isSet(); } /** @@ -123,17 +120,17 @@ public boolean isRetryHandlerSet() { * @return This instance for chaining */ public HttpRequest unsetRetryHandler() { - this.retryHandler = new RetryHandler(); - this.retryHandlerSet = false; + this.retryHandler.unset(); return this; } /** * @return The set retry handler + * @throws IllegalStateException If the retry handler is not set */ @Nonnull public RetryHandler getRetryHandler() { - return this.retryHandler; + return this.retryHandler.get(); } /** @@ -143,8 +140,43 @@ public RetryHandler getRetryHandler() { * @return This instance for chaining */ public HttpRequest setRetryHandler(@Nonnull final RetryHandler retryHandler) { - this.retryHandler = retryHandler; - this.retryHandlerSet = true; + this.retryHandler.set(retryHandler); + return this; + } + + /** + * @return If the ignore invalid SSL flag is set + */ + public boolean isIgnoreInvalidSSLSet() { + return this.ignoreInvalidSSL.isSet(); + } + + /** + * Unset the ignore invalid SSL flag. + * + * @return This instance for chaining + */ + public HttpRequest unsetIgnoreInvalidSSL() { + this.ignoreInvalidSSL.unset(); + return this; + } + + /** + * @return The set ignore invalid SSL flag + * @throws IllegalStateException If the ignore invalid SSL flag is not set + */ + public boolean getIgnoreInvalidSSL() { + return this.ignoreInvalidSSL.get(); + } + + /** + * Set the ignore invalid SSL flag to use for this request. + * + * @param ignoreInvalidSSL The ignore invalid SSL flag to use + * @return This instance for chaining + */ + public HttpRequest setIgnoreInvalidSSL(final boolean ignoreInvalidSSL) { + this.ignoreInvalidSSL.set(ignoreInvalidSSL); return this; } diff --git a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/IgnoringTrustManager.java b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/IgnoringTrustManager.java new file mode 100644 index 0000000..2ac6ea7 --- /dev/null +++ b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/IgnoringTrustManager.java @@ -0,0 +1,54 @@ +package net.lenni0451.commons.httpclient.utils; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; +import java.io.IOException; +import java.net.Socket; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; + +public class IgnoringTrustManager extends X509ExtendedTrustManager { + + public static SSLContext makeIgnoringSSLContext() throws IOException { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{new IgnoringTrustManager()}, new SecureRandom()); + return sslContext; + } catch (Throwable t) { + throw new IOException("Failed to create ignoring SSL socket factory", t); + } + } + + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + +} diff --git a/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/ResettableStorage.java b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/ResettableStorage.java new file mode 100644 index 0000000..ce362e5 --- /dev/null +++ b/commons-httpclient/src/main/java/net/lenni0451/commons/httpclient/utils/ResettableStorage.java @@ -0,0 +1,34 @@ +package net.lenni0451.commons.httpclient.utils; + +public class ResettableStorage { + + private boolean set = false; + private T value; + + public ResettableStorage() { + } + + public ResettableStorage(final T defaultValue) { + this.set(defaultValue); + } + + public boolean isSet() { + return this.set; + } + + public void unset() { + this.set = false; + this.value = null; + } + + public T get() { + if (!this.set) throw new IllegalStateException("Value is not set"); + return this.value; + } + + public void set(final T value) { + this.value = value; + this.set = true; + } + +}