Reputation: 359
I would like to know if there is any way to make my hosting only accept connections from my App, and reject the connections from any web browser.
Upvotes: 0
Views: 377
Reputation: 1337
The best approach is probably going to be to use SSL with client certificates. This will give you authentication as well as encryption.
You're probably familiar with using SSL via HTTPS to secure your web browser when you pay for something online. The process is similar except that the server has to also be configured to require a client-side certificate. In Apache, this is a setting called SSLVerifyClient. This will force the web server to require a certificate from the client and then to verify it as valid.
Assigning client-side certificates from a root authority like Verisign can be expensive, so you probably want to generate your own. You can do this using OpenSSL.
Once you have a client certificate, you have to copy the certificate into your application storage on the device. Then you can use the Keystore object to open it. Once it's open, you can associate it with your HTTP requests when you use the HttpUrlConnection object.
Since you'll probably generate your own certificate instead of using those from a root authority, you will need to setup a TrustManager in your Android app to accept your self-signed certificates and associate that with the HttpUrlConnection when you make the request.
That's the general strategy and that should be enough to get you started. If you get stuck on something specific, try asking individual questions about the separate parts. The Apache and OpenSSL part would probably be better posted on Server Fault.
Here are some links for the server-side stuff:
Client certificates with apache
Client-authenticated_TLS_handshake
Here are links for the Android development:
HTTPS with Client Certificates on Android
android-ssl
FakeX509TrustManager (useful during development)
It looks like all you need are in those links, but I'll summarize the Android code here. Note that I don't have a test environment for this, so the code is incomplete and untested. Again, this is just to get you started.
// open the certificate
keyStore = KeyStore.getInstance("PKCS12");
fis = new FileInputStream(certificateFile);
keyStore.load(fis, clientCertPassword.toCharArray());
// create the SSL context
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, clientCertPassword.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
// perform the HTTP request
String result = null;
HttpURLConnection urlConnection = null;
try {
URL requestedUrl = new URL(url);
urlConnection = (HttpURLConnection) requestedUrl.openConnection();
if(urlConnection instanceof HttpsURLConnection) {
((HttpsURLConnection)urlConnection)
.setSSLSocketFactory(sslContext.getSocketFactory());
}
urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(1500);
urlConnection.setReadTimeout(1500);
lastResponseCode = urlConnection.getResponseCode();
result = IOUtil.readFully(urlConnection.getInputStream());
lastContentType = urlConnection.getContentType();
} catch(Exception ex) {
result = ex.toString();
} finally {
if(urlConnection != null) {
urlConnection.disconnect();
}
}
Upvotes: 3