diff --git a/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java b/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java
index 99be01f..916e0bd 100644
--- a/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java
+++ b/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java
@@ -25,6 +25,7 @@ package com.falsepattern.lib.internal.asm;
import com.falsepattern.lib.internal.FPLog;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.impl.dependencies.DependencyLoaderImpl;
+import com.falsepattern.lib.internal.impl.dependencies.LetsEncryptHelper;
import com.falsepattern.lib.internal.logging.CrashImprover;
import com.falsepattern.lib.internal.logging.NotEnoughVerbosity;
import com.falsepattern.lib.mapping.MappingManager;
@@ -62,6 +63,7 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
private static boolean obfuscated;
static {
+ LetsEncryptHelper.replaceSSLContext();
FPLog.LOG.info("Removing skill issues...");
try {
Class.forName("thermos.Thermos");
diff --git a/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java b/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java
index 4c66254..0f4db86 100644
--- a/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java
+++ b/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java
@@ -25,10 +25,12 @@ package com.falsepattern.lib.internal.asm;
import com.falsepattern.lib.internal.core.LowLevelCallMultiplexer;
import com.falsepattern.lib.internal.impl.dependencies.DependencyLoaderImpl;
+import com.falsepattern.lib.internal.impl.dependencies.LetsEncryptHelper;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbPlugin;
public class RFBLoadingPlugin implements RfbPlugin {
static {
+ LetsEncryptHelper.replaceSSLContext();
LowLevelCallMultiplexer.rfbDetected();
DependencyLoaderImpl.executeDependencyLoading(false);
}
diff --git a/src/main/java/com/falsepattern/lib/internal/config/EarlyConfig.java b/src/main/java/com/falsepattern/lib/internal/config/EarlyConfig.java
index 8fdb4d3..0c21a6f 100644
--- a/src/main/java/com/falsepattern/lib/internal/config/EarlyConfig.java
+++ b/src/main/java/com/falsepattern/lib/internal/config/EarlyConfig.java
@@ -50,6 +50,10 @@ public class EarlyConfig {
@StableAPI.Expose(since = "__INTERNAL__")
private boolean enableLibraryDownloads;
+ @Expose
+ @StableAPI.Expose(since = "__INTERNAL__")
+ private boolean enableLetsEncryptRoot;
+
private static volatile EarlyConfig instance = null;
private static final Logger LOG = LogManager.getLogger(Tags.MODNAME + " Early Config");
@@ -83,6 +87,7 @@ public class EarlyConfig {
if (config == null) {
config = new EarlyConfig();
config.enableLibraryDownloads(true);
+ config.enableLetsEncryptRoot(true);
try {
Files.write(configFile, gson.toJson(config).getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
diff --git a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/LetsEncryptHelper.java b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/LetsEncryptHelper.java
new file mode 100644
index 0000000..087a5d2
--- /dev/null
+++ b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/LetsEncryptHelper.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of FalsePatternLib.
+ *
+ * Copyright (C) 2022-2024 FalsePattern
+ * All Rights Reserved
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * FalsePatternLib is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FalsePatternLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FalsePatternLib. If not, see .
+ */
+
+package com.falsepattern.lib.internal.impl.dependencies;
+
+import com.falsepattern.lib.internal.FPLog;
+import com.falsepattern.lib.internal.config.EarlyConfig;
+import lombok.val;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertificateFactory;
+
+/**
+ * Add Let's Encrypt root certificates to the default SSLContext.
+ * CurseForge launcher uses the vanilla JDK for 1.7.10, which is version 8u51.
+ * This version does not include these certificates, support for ISRG Root X1 was added in 8u141.
+ * Based on LetsEncryptCraft by Cloudhunter (MIT)
+ */
+public class LetsEncryptHelper {
+ private static volatile boolean patched = false;
+ private LetsEncryptHelper() {}
+ @SuppressWarnings("java:S6437")
+ public static void replaceSSLContext() {
+ if (!EarlyConfig.getInstance().enableLetsEncryptRoot()) {
+ return;
+ }
+ if (patched) {
+ return;
+ }
+ patched = true;
+
+ try (val x1 = LetsEncryptHelper.class.getResourceAsStream("/letsencrypt/isrgrootx1.pem");
+ val x2 = LetsEncryptHelper.class.getResourceAsStream("/letsencrypt/isrg-root-x2.pem")) {
+ val merged = KeyStore.getInstance(KeyStore.getDefaultType());
+ val cacerts = Paths.get(System.getProperty("java.home"),"lib", "security", "cacerts");
+ merged.load(Files.newInputStream(cacerts), "changeit".toCharArray());
+
+ val cf = CertificateFactory.getInstance("X.509");
+
+ val cx1 = cf.generateCertificate(x1);
+ merged.setCertificateEntry("archaicfix-isrgx1", cx1);
+
+ val cx2 = cf.generateCertificate(x2);
+ merged.setCertificateEntry("archaicfix-isrgx2", cx2);
+
+ val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(merged);
+ val sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, tmf.getTrustManagers(), null);
+ SSLContext.setDefault(sslContext);
+ FPLog.LOG.info("[LetsEncryptHelper] Added certificates to trust store.");
+ } catch (IOException e) {
+ FPLog.LOG.error("[LetsEncryptHelper] Failed to load certificates from classpath.", e);
+ } catch (GeneralSecurityException e) {
+ FPLog.LOG.error("[LetsEncryptHelper] Failed to load default keystore.", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/letsencrypt/isrg-root-x2.pem b/src/main/resources/letsencrypt/isrg-root-x2.pem
new file mode 100644
index 0000000..7d903ed
--- /dev/null
+++ b/src/main/resources/letsencrypt/isrg-root-x2.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
+CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
+R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
+MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
+ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
+EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
+ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
+zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
+tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
+/q4AaOeMSQ+2b1tbFfLn
+-----END CERTIFICATE-----
diff --git a/src/main/resources/letsencrypt/isrgrootx1.pem b/src/main/resources/letsencrypt/isrgrootx1.pem
new file mode 100644
index 0000000..b85c803
--- /dev/null
+++ b/src/main/resources/letsencrypt/isrgrootx1.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
+WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
+ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
+MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
+h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
+0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
+A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
+T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
+B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
+B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
+KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
+OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
+jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
+qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
+rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
+hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
+3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
+NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
+ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
+TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
+jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
+oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
+4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
+mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
+emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----