mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-26 22:43:05 +00:00
Use the default X509TrustManager to validate non-pinned certificates
This allows the certificate to be rotated without re-adding the PC.
This commit is contained in:
parent
429c32477c
commit
536496184e
@ -11,6 +11,8 @@ import java.net.Socket;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
@ -29,6 +31,7 @@ import javax.net.ssl.SSLContext;
|
|||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import javax.net.ssl.X509KeyManager;
|
import javax.net.ssl.X509KeyManager;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
@ -42,7 +45,6 @@ import com.limelight.nvstream.ConnectionContext;
|
|||||||
import com.limelight.nvstream.http.PairingManager.PairState;
|
import com.limelight.nvstream.http.PairingManager.PairState;
|
||||||
|
|
||||||
import okhttp3.ConnectionPool;
|
import okhttp3.ConnectionPool;
|
||||||
import okhttp3.Handshake;
|
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
@ -67,6 +69,7 @@ public class NvHTTP {
|
|||||||
private OkHttpClient httpClient;
|
private OkHttpClient httpClient;
|
||||||
private OkHttpClient httpClientWithReadTimeout;
|
private OkHttpClient httpClientWithReadTimeout;
|
||||||
|
|
||||||
|
private X509TrustManager defaultTrustManager;
|
||||||
private X509TrustManager trustManager;
|
private X509TrustManager trustManager;
|
||||||
private X509KeyManager keyManager;
|
private X509KeyManager keyManager;
|
||||||
private X509Certificate serverCert;
|
private X509Certificate serverCert;
|
||||||
@ -82,18 +85,46 @@ public class NvHTTP {
|
|||||||
throw new IllegalStateException("Should never be called");
|
throw new IllegalStateException("Should never be called");
|
||||||
}
|
}
|
||||||
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
|
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
|
||||||
// Check the server certificate if we've paired to this host
|
try {
|
||||||
if (!certs[0].equals(NvHTTP.this.serverCert)) {
|
// Try the default trust manager first to allow pairing with certificates
|
||||||
throw new CertificateException("Certificate mismatch");
|
// that chain up to a trusted root CA. This will raise CertificateException
|
||||||
|
// if the certificate is not trusted (expected for GFE's self-signed certs).
|
||||||
|
defaultTrustManager.checkServerTrusted(certs, authType);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
// Check the server certificate if we've paired to this host
|
||||||
|
if (certs.length != 1 || !certs[0].equals(NvHTTP.this.serverCert)) {
|
||||||
|
throw new CertificateException("Certificate mismatch");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static X509TrustManager getDefaultTrustManager() {
|
||||||
|
try {
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init((KeyStore) null);
|
||||||
|
|
||||||
|
for (TrustManager tm : tmf.getTrustManagers()) {
|
||||||
|
if (tm instanceof X509TrustManager) {
|
||||||
|
return (X509TrustManager) tm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("No X509 trust manager found");
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeHttpState(final X509Certificate serverCert, final LimelightCryptoProvider cryptoProvider) {
|
private void initializeHttpState(final X509Certificate serverCert, final LimelightCryptoProvider cryptoProvider) {
|
||||||
// Set up TrustManager
|
// Set up TrustManager
|
||||||
setServerCert(serverCert);
|
setServerCert(serverCert);
|
||||||
|
|
||||||
|
defaultTrustManager = getDefaultTrustManager();
|
||||||
|
|
||||||
keyManager = new X509KeyManager() {
|
keyManager = new X509KeyManager() {
|
||||||
public String chooseClientAlias(String[] keyTypes,
|
public String chooseClientAlias(String[] keyTypes,
|
||||||
Principal[] issuers, Socket socket) { return "Limelight-RSA"; }
|
Principal[] issuers, Socket socket) { return "Limelight-RSA"; }
|
||||||
@ -296,18 +327,6 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public X509Certificate getCertificateIfTrusted() {
|
|
||||||
try {
|
|
||||||
Response resp = httpClient.newCall(new Request.Builder().url(baseUrlHttps).get().build()).execute();
|
|
||||||
Handshake handshake = resp.handshake();
|
|
||||||
if (handshake != null) {
|
|
||||||
return (X509Certificate)handshake.peerCertificates().get(0);
|
|
||||||
}
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read timeout should be enabled for any HTTP query that requires no outside action
|
// Read timeout should be enabled for any HTTP query that requires no outside action
|
||||||
// on the GFE server. Examples of queries that DO require outside action are launch, resume, and quit.
|
// on the GFE server. Examples of queries that DO require outside action are launch, resume, and quit.
|
||||||
// The initial pair query does require outside action (user entering a PIN) but subsequent pairing
|
// The initial pair query does require outside action (user entering a PIN) but subsequent pairing
|
||||||
@ -316,10 +335,6 @@ public class NvHTTP {
|
|||||||
Request request = new Request.Builder().url(url).get().build();
|
Request request = new Request.Builder().url(url).get().build();
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
if (serverCert == null && !url.startsWith(baseUrlHttp)) {
|
|
||||||
throw new IllegalStateException("Attempted HTTPS fetch without pinned cert");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enableReadTimeout) {
|
if (enableReadTimeout) {
|
||||||
response = performAndroidTlsHack(httpClientWithReadTimeout).newCall(request).execute();
|
response = performAndroidTlsHack(httpClientWithReadTimeout).newCall(request).execute();
|
||||||
}
|
}
|
||||||
@ -379,11 +394,6 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PairingManager.PairState getPairState(String serverInfo) throws IOException, XmlPullParserException {
|
public PairingManager.PairState getPairState(String serverInfo) throws IOException, XmlPullParserException {
|
||||||
// If we don't have a server cert, we can't be paired even if the host thinks we are
|
|
||||||
if (serverCert == null) {
|
|
||||||
return PairState.NOT_PAIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NvHTTP.getXmlString(serverInfo, "PairStatus").equals("1")) {
|
if (!NvHTTP.getXmlString(serverInfo, "PairStatus").equals("1")) {
|
||||||
return PairState.NOT_PAIRED;
|
return PairState.NOT_PAIRED;
|
||||||
}
|
}
|
||||||
|
@ -104,12 +104,6 @@ public class AddComputerManually extends Activity {
|
|||||||
try {
|
try {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
details.manualAddress = host;
|
details.manualAddress = host;
|
||||||
|
|
||||||
try {
|
|
||||||
NvHTTP http = new NvHTTP(host, managerBinder.getUniqueId(), null, PlatformBinding.getCryptoProvider(this));
|
|
||||||
details.serverCert = http.getCertificateIfTrusted();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
|
|
||||||
success = managerBinder.addComputerBlocking(details);
|
success = managerBinder.addComputerBlocking(details);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// This can be thrown from OkHttp if the host fails to canonicalize to a valid name.
|
// This can be thrown from OkHttp if the host fails to canonicalize to a valid name.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user