From 17914ae50938d67745cf288c28651e67009178d6 Mon Sep 17 00:00:00 2001 From: oxmc <67136658+oxmc@users.noreply.github.com> Date: Mon, 31 Mar 2025 05:57:33 -0400 Subject: [PATCH] SSL Fix --- build.gradle | 2 +- src/main/java/net/montoyo/mcef/MCEF.java | 3 +- .../net/montoyo/mcef/SSLCertificateAdder.java | 87 ++++++++++-------- .../resources/assets/cas/google/roots.jks | Bin 29113 -> 0 bytes 4 files changed, 53 insertions(+), 39 deletions(-) delete mode 100644 src/main/resources/assets/cas/google/roots.jks diff --git a/build.gradle b/build.gradle index e28d998..8288f63 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ apply plugin: 'net.minecraftforge.gradle' apply plugin: 'eclipse' apply plugin: 'maven-publish' -version = '1.12.2-1.42' +version = '1.12.2-1.43' group = 'net.montoyo.mcef' archivesBaseName = 'mcef-legacy' diff --git a/src/main/java/net/montoyo/mcef/MCEF.java b/src/main/java/net/montoyo/mcef/MCEF.java index e64a041..9b24e69 100644 --- a/src/main/java/net/montoyo/mcef/MCEF.java +++ b/src/main/java/net/montoyo/mcef/MCEF.java @@ -15,7 +15,7 @@ import java.util.List; @Mod(modid = "mcef", name = "MCEF", version = MCEF.VERSION) public class MCEF { - public static final String VERSION = "1.42"; + public static final String VERSION = "1.43"; public static boolean ENABLE_EXAMPLE; public static boolean SKIP_UPDATES; public static boolean WARN_UPDATES; @@ -76,6 +76,7 @@ public class MCEF { // Add certificates if needed // This is a workaround for Java 7u111 and 8u101, which have issues with Let's Encrypt certificates or google trust services try { + Log.info("PREINT, Adding certificates..."); SSLCertificateAdder.validateAndInstall(); } catch (Exception e) { Log.error("Failed to add a certificate: " + e.getMessage()); diff --git a/src/main/java/net/montoyo/mcef/SSLCertificateAdder.java b/src/main/java/net/montoyo/mcef/SSLCertificateAdder.java index c2c93ce..00a4cde 100644 --- a/src/main/java/net/montoyo/mcef/SSLCertificateAdder.java +++ b/src/main/java/net/montoyo/mcef/SSLCertificateAdder.java @@ -11,6 +11,7 @@ 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.cert.*; import java.util.*; import java.util.regex.Matcher; @@ -41,53 +42,45 @@ public class SSLCertificateAdder { private static final List CERTIFICATES = Arrays.asList( new CertificateInfo("lets-encrypt-x3-cross-signed", "X.509", "/assets/cas/letsencrypt/lets-encrypt-x3-cross-signed.der", 7, 110, "https://helloworld.letsencrypt.org"), new CertificateInfo("lets-encrypt-isrgrootx1", "X.509", "/assets/cas/letsencrypt/isrgrootx1.der", 7, 110, "https://helloworld.letsencrypt.org"), - new CertificateInfo("google-trust-services", "X.509", "/assets/google/root.pem", 7, 110, "https://www.google.com"), - new CertificateInfo("google-trust-jks", "JKS", "/assets/cas/google/roots.jks", 7, 110, "https://www.google.com") + new CertificateInfo("google-trust-services", "X.509", "/assets/cas/google/roots.pem", 7, 110, "https://www.google.com") ); - public static void addCertificates() { - for (CertificateInfo certInfo : CERTIFICATES) { - try { - Log.info("[SSLCert] Adding certificate: " + certInfo.name); - addCertificate(certInfo); - Log.info("[SSLCert] Certificate added successfully. Checking connection..."); - checkConnection(certInfo.testUrl, "[" + certInfo.name + " SSL]"); - } catch (Exception e) { - Log.error("[SSLCert] Failed to add certificate: " + certInfo.name, e); - } - } - } - - private static void addCertificate(CertificateInfo certInfo) throws Exception { + private static void addCertificate(CertificateInfo certInfo, KeyStore keyStore) throws Exception { try (InputStream certStream = SSLCertificateAdder.class.getResourceAsStream(certInfo.filePath)) { if (certStream == null) { throw new FileNotFoundException("Certificate file not found: " + certInfo.filePath); } - - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream ksInputStream = Files.newInputStream(ksPath)) { - keyStore.load(ksInputStream, KEYSTORE_PASSWORD.toCharArray()); - } - + CertificateFactory cf = CertificateFactory.getInstance(certInfo.type); Collection certificates; + try (BufferedInputStream caInput = new BufferedInputStream(certStream)) { - certificates = cf.generateCertificates(caInput); + if (certInfo.filePath.endsWith(".pem")) { + // Handle PEM format + certificates = cf.generateCertificates(caInput); + } else { + // Handle DER format + Certificate cert = cf.generateCertificate(caInput); + certificates = Collections.singletonList(cert); + } } - + int count = 0; for (Certificate cert : certificates) { String alias = certInfo.name + "-" + count++; keyStore.setCertificateEntry(alias, cert); Log.info("[SSLCert] Added certificate with alias: " + alias); } - - TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(keyStore); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, tmf.getTrustManagers(), null); - SSLContext.setDefault(sslContext); + } catch (FileNotFoundException e) { + Log.error("[SSLCert] Certificate file not found: " + certInfo.filePath, e); + } catch (CertificateException e) { + Log.error("[SSLCert] Failed to parse certificate from file: " + certInfo.filePath, e); + } catch (IOException e) { + Log.error("[SSLCert] I/O error while processing the certificate file: " + certInfo.filePath, e); + } catch (KeyStoreException e) { + Log.error("[SSLCert] KeyStore error while adding the certificate: " + certInfo.name, e); + } catch (Exception e) { + Log.error("[SSLCert] Unexpected error while adding the certificate: " + certInfo.name, e); } } @@ -130,17 +123,37 @@ public class SSLCertificateAdder { Log.info("[SSLCert] Failed to parse Java version. Applying fix anyway."); } - for (CertificateInfo certInfo : CERTIFICATES) { - if ((majorVersion >= certInfo.minVersion && majorVersion <= certInfo.maxVersion)) { + KeyStore keyStore = null; + + try { + // Initialize the KeyStore once + keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (InputStream ksInputStream = Files.newInputStream(ksPath)) { + keyStore.load(ksInputStream, KEYSTORE_PASSWORD.toCharArray()); + } + + // Add each certificate to the keystore + for (CertificateInfo certInfo : CERTIFICATES) { try { - Log.info("[SSLCert] Adding " + certInfo.name + "..."); - addCertificate(certInfo); - Log.info("[SSLCert] " + certInfo.name + " added successfully. Checking connection..."); + Log.info("[SSLCert] Adding certificate: " + certInfo.name); + addCertificate(certInfo, keyStore); + Log.info("[SSLCert] Certificate added successfully. Checking connection..."); checkConnection(certInfo.testUrl, "[" + certInfo.name + " SSL]"); } catch (Exception e) { - Log.error("[SSLCert] Error adding " + certInfo.name, e); + Log.error("[SSLCert] Failed to add certificate: " + certInfo.name, e); } } + + // Now set the SSLContext globally after all certificates have been added + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keyStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tmf.getTrustManagers(), null); + SSLContext.setDefault(sslContext); + + } catch (Exception e) { + Log.error("[SSLCert] Error adding certificates", e); } } } diff --git a/src/main/resources/assets/cas/google/roots.jks b/src/main/resources/assets/cas/google/roots.jks deleted file mode 100644 index 667111de50db25de7e971461a7c05c9a42ac4f1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29113 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