Manually inject root cert for cross-signed LetsEncrypt cert in requests
JREs prior to 8u101 do not ship with the root certificate that cross-signed
the LetsEncrypt X3 CA used to sign server certificates.
As the standard native Minecraft launcher for Windows and OSX ship with an
older Java version, this commit imports the required root cert and uses it
wherever communication with the replaymod.com server is established.
Effectively reverts 682fb4b
This commit is contained in:
@@ -27,8 +27,20 @@ import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyManagementException;
|
||||
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.CertificateFactory;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
@@ -51,6 +63,42 @@ public class Utils {
|
||||
DEFAULT_THUMBNAIL = thumbnail;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Neither the root certificate of LetsEncrypt nor the root that cross-signed it is included in the default
|
||||
* Java keystore prior to 8u101.
|
||||
* Therefore whenever a connection to the replaymod.com site is made, this SSLContext has to be used instead.
|
||||
* It has been constructed to include the necessary root certificates.
|
||||
* @see #SSL_SOCKET_FACTORY
|
||||
*/
|
||||
public static final SSLContext SSL_CONTEXT;
|
||||
|
||||
/**
|
||||
* @see #SSL_CONTEXT
|
||||
*/
|
||||
public static final SSLSocketFactory SSL_SOCKET_FACTORY;
|
||||
|
||||
static {
|
||||
// Largely from https://community.letsencrypt.org/t/134/37
|
||||
try (InputStream in = Utils.class.getResourceAsStream("/dst_root_ca_x3.pem")){
|
||||
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(in);
|
||||
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(null, null);
|
||||
keyStore.setCertificateEntry("1", certificate);
|
||||
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(keyStore);
|
||||
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||
SSL_CONTEXT = ctx;
|
||||
SSL_SOCKET_FACTORY = ctx.getSocketFactory();
|
||||
} catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertSecondsToShortString(int seconds) {
|
||||
int hours = seconds/(60*60);
|
||||
int min = seconds/60 - hours*60;
|
||||
|
||||
@@ -17,6 +17,7 @@ import de.johni0702.minecraft.gui.utils.Colors;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.URL;
|
||||
@@ -24,8 +25,10 @@ import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
|
||||
|
||||
public class OpenEyeExtra implements Extra {
|
||||
private static final String DOWNLOAD_URL = "http://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION;
|
||||
private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION;
|
||||
private static final Setting<Boolean> ASK_FOR_OPEN_EYE = new Setting<>("advanced", "askForOpenEye", null, true);
|
||||
|
||||
private ReplayMod mod;
|
||||
@@ -69,7 +72,9 @@ public class OpenEyeExtra implements Extra {
|
||||
File targetFile = new File("mods/" + Loader.MC_VERSION, "OpenEye.jar");
|
||||
FileUtils.forceMkdir(targetFile.getParentFile());
|
||||
|
||||
ReadableByteChannel in = Channels.newChannel(new URL(DOWNLOAD_URL).openStream());
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
|
||||
connection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
|
||||
ReadableByteChannel in = Channels.newChannel(connection.getInputStream());
|
||||
FileChannel out = new FileOutputStream(targetFile).getChannel();
|
||||
out.transferFrom(in, 0, Long.MAX_VALUE);
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -15,13 +15,19 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
|
||||
|
||||
public class ApiClient {
|
||||
|
||||
private static final Minecraft mc = Minecraft.getMinecraft();
|
||||
@@ -130,7 +136,11 @@ public class ApiClient {
|
||||
QueryBuilder builder = new QueryBuilder(ReplayModApiMethods.get_thumbnail);
|
||||
builder.put("id", file);
|
||||
URL url = new URL(builder.toString());
|
||||
return ImageIO.read(url);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
return ImageIO.read(in);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean cancelDownload = false;
|
||||
@@ -143,7 +153,8 @@ public class ApiClient {
|
||||
builder.put("id", file);
|
||||
String url = builder.toString();
|
||||
URL website = new URL(url);
|
||||
HttpURLConnection con = (HttpURLConnection) website.openConnection();
|
||||
HttpsURLConnection con = (HttpsURLConnection) website.openConnection();
|
||||
con.setSSLSocketFactory(SSL_SOCKET_FACTORY);
|
||||
|
||||
int fileSize = con.getContentLength();
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ import com.google.gson.JsonParser;
|
||||
import com.replaymod.online.api.replay.holders.ApiError;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
|
||||
|
||||
public class SimpleApiClient {
|
||||
|
||||
private static final JsonParser jsonParser = new JsonParser();
|
||||
@@ -72,10 +74,11 @@ public class SimpleApiClient {
|
||||
// read response
|
||||
String responseContent = null;
|
||||
InputStream is = null;
|
||||
HttpURLConnection httpUrlConnection = null;
|
||||
HttpsURLConnection httpUrlConnection = null;
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
httpUrlConnection = (HttpURLConnection) url.openConnection();
|
||||
httpUrlConnection = (HttpsURLConnection) url.openConnection();
|
||||
httpUrlConnection.setSSLSocketFactory(SSL_SOCKET_FACTORY);
|
||||
|
||||
httpUrlConnection.setRequestMethod("GET");
|
||||
|
||||
|
||||
@@ -9,16 +9,18 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class FileUploader {
|
||||
private static final Gson gson = new Gson();
|
||||
@@ -48,7 +50,8 @@ public class FileUploader {
|
||||
postData += "&name=" + URLEncoder.encode(filename, "UTF-8");
|
||||
|
||||
String url = ReplayModApiMethods.upload_file + postData;
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
|
||||
HttpsURLConnection con = (HttpsURLConnection) new URL(url).openConnection();
|
||||
con.setSSLSocketFactory(SSL_SOCKET_FACTORY);
|
||||
con.setUseCaches(false);
|
||||
con.setDoOutput(true);
|
||||
con.setRequestMethod("POST");
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.replaymod.online.api.replay;
|
||||
|
||||
public class ReplayModApiMethods {
|
||||
|
||||
public static final String REPLAYMOD_BASE_URL = "http://ReplayMod.com/api/";
|
||||
public static final String REPLAYMOD_BASE_URL = "https://ReplayMod.com/api/";
|
||||
|
||||
public static final String register = REPLAYMOD_BASE_URL+"register";
|
||||
public static final String check_authkey = REPLAYMOD_BASE_URL+"check_authkey";
|
||||
|
||||
20
src/main/resources/dst_root_ca_x3.pem
Normal file
20
src/main/resources/dst_root_ca_x3.pem
Normal file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
|
||||
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
|
||||
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
|
||||
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
|
||||
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
|
||||
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
|
||||
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
|
||||
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
|
||||
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
|
||||
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
|
||||
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
|
||||
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
|
||||
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
|
||||
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
|
||||
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
|
||||
-----END CERTIFICATE-----
|
||||
Reference in New Issue
Block a user