mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 11:33:06 +00:00
Require cert pinning for HTTPS
This commit is contained in:
parent
67f01fbdca
commit
564e7c71a6
@ -66,27 +66,36 @@ public class NvHTTP {
|
|||||||
|
|
||||||
private TrustManager[] trustManager;
|
private TrustManager[] trustManager;
|
||||||
private KeyManager[] keyManager;
|
private KeyManager[] keyManager;
|
||||||
|
private X509Certificate serverCert;
|
||||||
|
|
||||||
|
void setServerCert(final X509Certificate serverCert) {
|
||||||
|
this.serverCert = serverCert;
|
||||||
|
|
||||||
|
trustManager = new TrustManager[] {
|
||||||
|
new X509TrustManager() {
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||||
|
throw new IllegalStateException("Should never be called");
|
||||||
|
}
|
||||||
|
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
|
||||||
|
if (certs.length != 1) {
|
||||||
|
throw new CertificateException("Invalid certificate chain length: "+certs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the server certificate if we've paired to this host
|
||||||
|
if (!certs[0].equals(serverCert)) {
|
||||||
|
throw new CertificateException("Certificate mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeHttpState(final X509Certificate serverCert, final LimelightCryptoProvider cryptoProvider) {
|
private void initializeHttpState(final X509Certificate serverCert, final LimelightCryptoProvider cryptoProvider) {
|
||||||
trustManager = new TrustManager[] {
|
// Set up TrustManager
|
||||||
new X509TrustManager() {
|
setServerCert(serverCert);
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return new X509Certificate[0];
|
|
||||||
}
|
|
||||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
|
||||||
throw new IllegalStateException("Should never be called");
|
|
||||||
}
|
|
||||||
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
|
|
||||||
if (certs.length != 1) {
|
|
||||||
throw new CertificateException("Invalid certificate chain length: "+certs.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the server certificate if we've paired to this host
|
|
||||||
if (serverCert != null && !certs[0].equals(serverCert)) {
|
|
||||||
throw new CertificateException("Certificate mismatch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
|
|
||||||
keyManager = new KeyManager[] {
|
keyManager = new KeyManager[] {
|
||||||
new X509KeyManager() {
|
new X509KeyManager() {
|
||||||
@ -187,7 +196,7 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getServerInfo() throws MalformedURLException, IOException, XmlPullParserException {
|
public String getServerInfo() throws IOException, XmlPullParserException {
|
||||||
String resp;
|
String resp;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -196,38 +205,46 @@ public class NvHTTP {
|
|||||||
// like there are extra request headers required to make this stuff work over HTTP.
|
// like there are extra request headers required to make this stuff work over HTTP.
|
||||||
//
|
//
|
||||||
|
|
||||||
try {
|
// When we have a pinned cert, use HTTPS to fetch serverinfo and fall back on cert mismatch
|
||||||
|
if (serverCert != null) {
|
||||||
try {
|
try {
|
||||||
resp = openHttpConnectionToString(baseUrlHttps + "/serverinfo?"+buildUniqueIdUuidString(), true);
|
try {
|
||||||
} catch (SSLHandshakeException e) {
|
resp = openHttpConnectionToString(baseUrlHttps + "/serverinfo?"+buildUniqueIdUuidString(), true);
|
||||||
// Detect if we failed due to a server cert mismatch
|
} catch (SSLHandshakeException e) {
|
||||||
if (e.getCause() instanceof CertificateException) {
|
// Detect if we failed due to a server cert mismatch
|
||||||
// Jump to the GfeHttpResponseException exception handler to retry
|
if (e.getCause() instanceof CertificateException) {
|
||||||
// over HTTP which will allow us to pair again to update the cert
|
// Jump to the GfeHttpResponseException exception handler to retry
|
||||||
throw new GfeHttpResponseException(401, "Server certificate mismatch");
|
// over HTTP which will allow us to pair again to update the cert
|
||||||
}
|
throw new GfeHttpResponseException(401, "Server certificate mismatch");
|
||||||
else {
|
}
|
||||||
throw e;
|
else {
|
||||||
}
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will throw an exception if the request came back with a failure status.
|
||||||
|
// We want this because it will throw us into the HTTP case if the client is unpaired.
|
||||||
|
getServerVersion(resp);
|
||||||
|
}
|
||||||
|
catch (GfeHttpResponseException e) {
|
||||||
|
if (e.getErrorCode() == 401) {
|
||||||
|
// Cert validation error - fall back to HTTP
|
||||||
|
return openHttpConnectionToString(baseUrlHttp + "/serverinfo", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not a cert validation error, throw it
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will throw an exception if the request came back with a failure status.
|
return resp;
|
||||||
// We want this because it will throw us into the HTTP case if the client is unpaired.
|
|
||||||
getServerVersion(resp);
|
|
||||||
}
|
}
|
||||||
catch (GfeHttpResponseException e) {
|
else {
|
||||||
if (e.getErrorCode() == 401) {
|
// No pinned cert, so use HTTP
|
||||||
// Cert validation error - fall back to HTTP
|
return openHttpConnectionToString(baseUrlHttp + "/serverinfo", true);
|
||||||
return openHttpConnectionToString(baseUrlHttp + "/serverinfo", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's not a cert validation error, throw it
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
return resp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComputerDetails getComputerDetails() throws MalformedURLException, IOException, XmlPullParserException {
|
public ComputerDetails getComputerDetails() throws IOException, XmlPullParserException {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
String serverInfo = getServerInfo();
|
String serverInfo = getServerInfo();
|
||||||
|
|
||||||
@ -285,6 +302,10 @@ public class NvHTTP {
|
|||||||
Request request = new Request.Builder().url(url).build();
|
Request request = new Request.Builder().url(url).build();
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
|
if (serverCert == null && !url.startsWith(baseUrlHttp)) {
|
||||||
|
throw new IllegalStateException("Attempted HTTPS fetch without pinned cert");
|
||||||
|
}
|
||||||
|
|
||||||
if (enableReadTimeout) {
|
if (enableReadTimeout) {
|
||||||
performAndroidTlsHack(httpClientWithReadTimeout);
|
performAndroidTlsHack(httpClientWithReadTimeout);
|
||||||
response = httpClientWithReadTimeout.newCall(request).execute();
|
response = httpClientWithReadTimeout.newCall(request).execute();
|
||||||
@ -552,7 +573,7 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void unpair() throws IOException {
|
public void unpair() throws IOException {
|
||||||
openHttpConnectionToString(baseUrlHttps + "/unpair?"+buildUniqueIdUuidString(), true);
|
openHttpConnectionToString(baseUrlHttp + "/unpair?"+buildUniqueIdUuidString(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getBoxArt(NvApp app) throws IOException {
|
public InputStream getBoxArt(NvApp app) throws IOException {
|
||||||
|
@ -198,7 +198,7 @@ public class PairingManager {
|
|||||||
return PairState.FAILED;
|
return PairState.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save this cert for retrieval later for pinning
|
// Save this cert for retrieval later
|
||||||
serverCert = extractPlainCert(getCert);
|
serverCert = extractPlainCert(getCert);
|
||||||
if (serverCert == null) {
|
if (serverCert == null) {
|
||||||
// Attempting to pair while another device is pairing will cause GFE
|
// Attempting to pair while another device is pairing will cause GFE
|
||||||
@ -206,6 +206,9 @@ public class PairingManager {
|
|||||||
return PairState.ALREADY_IN_PROGRESS;
|
return PairState.ALREADY_IN_PROGRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Require this cert for TLS to this host
|
||||||
|
http.setServerCert(serverCert);
|
||||||
|
|
||||||
// Generate a random challenge and encrypt it with our AES key
|
// Generate a random challenge and encrypt it with our AES key
|
||||||
byte[] randomChallenge = generateRandomBytes(16);
|
byte[] randomChallenge = generateRandomBytes(16);
|
||||||
byte[] encryptedChallenge = encryptAes(randomChallenge, aesKey);
|
byte[] encryptedChallenge = encryptAes(randomChallenge, aesKey);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user