package com.nutomic.syncthingandroid.http;
import android.annotation.SuppressLint;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import com.nutomic.syncthingandroid.syncthing.RestApi;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public abstract class RestTask<A, B, C> extends AsyncTask<A, B, C> {
private static final String TAG = "RestTask";
private final URL mUrl;
protected final String mPath;
private final String mHttpsCertPath;
private final String mApiKey;
public RestTask(URL url, String path, String httpsCertPath, String apiKey) {
mUrl = url;
mPath = path;
mHttpsCertPath = httpsCertPath;
mApiKey = apiKey;
}
protected HttpsURLConnection openConnection(String... params) throws IOException {
Uri.Builder uriBuilder = Uri.parse(mUrl.toString())
.buildUpon()
.path(mPath);
for (int paramCounter = 0; paramCounter + 1 < params.length; ) {
uriBuilder.appendQueryParameter(params[paramCounter++], params[paramCounter++]);
}
URL url = new URL(uriBuilder.build().toString());
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setRequestProperty(RestApi.HEADER_API_KEY, mApiKey);
connection.setHostnameVerifier((h, s) -> true);
connection.setSSLSocketFactory(getSslSocketFactory());
return connection;
}
private SSLSocketFactory getSslSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new SyncthingTrustManager()},
new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
Log.w(TAG, e);
return null;
}
}
/*
* TrustManager checking against the local Syncthing instance's https public key.
*
* Based on http://stackoverflow.com/questions/16719959#16759793
*/
private class SyncthingTrustManager implements X509TrustManager {
private static final String TAG = "SyncthingTrustManager";
@Override
@SuppressLint("TrustAllX509TrustManager")
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
/**
* Verifies certs against public key of the local syncthing instance
*/
@Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) throws CertificateException {
InputStream is = null;
try {
is = new FileInputStream(mHttpsCertPath);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) cf.generateCertificate(is);
for (X509Certificate cert : certs) {
cert.verify(ca.getPublicKey());
}
} catch (FileNotFoundException | NoSuchAlgorithmException | InvalidKeyException |
NoSuchProviderException | SignatureException e) {
throw new CertificateException("Untrusted Certificate!", e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}