I'm just about done working on an app for a local gym, and as my testing is nearly complete, and a version 1 is nearly finished, I'm starting to think about securing the app against any MITM type attacks. While I know the chances are next to zero of someone even wanting to MITM this app (as opposed to say, a banking app), I would still like to be a little proactive in security.
While the app sends/receives no user information (data sent back and forth is stuff like weight, reps, time, the name of the class the user checks in to, etc.), I am transmitting the names of all active gym members (to be used for an auto complete text box). I would like to encrypt the names, but I've been finding it difficult to change my code from HTTP to HTTPS. I've got HTTPS and a self-signed cert on my server, but can't seem to get the android side to work (keep getting no peer cert errors in eclipse). As a work around, I've thought about using AES128 to encrypt/hash each name, then decrypt it on the phone, and then likewise do the same when sending data back through PHP to the database.
Is this a sufficient alternative to encrypting the entire session? Call it "Lazy SSL", as if someone were to get the key, they would be able to decrypt the data, but again, we are only transmitting names, no other user information.
Here is the unencrypted code I'm using (I left out unnecessary stuff to make this block smalller):
public JSONObject makeHttpRequest(String url, String method, List<NameValuePair> params) {
if (method == "POST") {
// request method is POST
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}
This is in a larger class used for parsing Json:
My entire JSONParser class
I'm calling this class in places I need to pull or send data to the server, such as the following:
final JSONParser jsonParser = new JSONParser();
final List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("tag", Globals.TAG_GETMEMBERS));
params.add(new BasicNameValuePair("LastRow", lastRow));
// getting JSON string from URL
final JSONObject json = jsonParser.makeHttpRequest(
Globals.WEBSERVICE_URL, "POST", params);
using various resources:
How to enable a self-signed certificate for SSL sockets on Android?
http://randomizedsort.blogspot.com/2010/09/step-to-step-guide-to-programming.html
I was able to get something useful, I originally tried doing the "trust all certs" method, but since that is MITM prone, I would rather not use it (plus it wasn't working. Using the 2nd link I've gotten so far as re-generating the cert, I've downloaded the bouncy castle jar (
I also used the following commands to generate a keystore, and import it into my project:
keytool -genkey -dname "cn = smashwebserver, ou=Development Team, o=Smash Gyms, L=Sunnyvale, s=California, c=US" -alias ssltest -keypass ssltest -keystore c:\dell\ssltest.keystore -storepass ssltest -validity 180
keytool -export -alias ssltest -keystore c:\dell\ssltest.keystore -file c:\dell\ssltest.cer -storepass ssltest -keypass ssltest
keytool -import -alias ssltestcert -file C:\dell\ssltest.cer -keypass ssltestcert -keystore "C:\Users\Evan Richardson\workspace\SmashGyms\res\raw\ssltestcert" -storetype BKS -storepass ssltestcert -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "C:\Users\Evan Richardson\workspace\SmashGyms\libs\bcprov-jdk15on-147.jar"
The resulting JSONParser class block looks like this:
if (method == "POST") {
// Load the self-signed server certificate
char[] passphrase = "ssltest".toCharArray();
KeyStore ksTrust = KeyStore.getInstance("BKS");
ksTrust.load(context.getResources().openRawResource(
R.raw.ssltestcert), passphrase);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
tmf.init(ksTrust);
// Create a SSLContext with the certificate
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(),
new SecureRandom());
// request method is POST
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}
however now I get the following error:
10-29 11:55:28.470: W/System.err(9561): java.io.IOException: Wrong version of key store.
I looked that error up, and a possible solution was found here:Android bouncy castle: IOException
I've downloaded the 145 version of bouncycastles Jar, and used that. This fixes the ioexception error, but now I get the following:
10-29 12:21:57.536: W/System.err(12506): Catch exception while startHandshake: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x10b9a10: Failure in SSL library, usually a protocol error
10-29 12:21:57.536: W/System.err(12506): error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:683 0x4026dced:0x00000000)
10-29 12:21:57.536: W/System.err(12506): return an invalid session with invalid cipher suite of SSL_NULL_WITH_NULL_NULL
10-29 12:21:57.586: W/System.err(12506): javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
Strangely enough, if I change my url to "https://google.com", I don't get any errors, just the following:
10-29 14:03:50.198: V/httpresponsetag:(17810): <!DOCTYPE html>
10-29 14:03:50.198: V/httpresponsetag:(17810): <html lang=en>
10-29 14:03:50.198: V/httpresponsetag:(17810): <meta charset=utf-8>
10-29 14:03:50.198: V/httpresponsetag:(17810): <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
10-29 14:03:50.198: V/httpresponsetag:(17810): <title>Error 405 (Method Not Allowed)!!1</title>
10-29 14:03:50.198: V/httpresponsetag:(17810): <style>
10-29 14:03:50.198: V/httpresponsetag:(17810): *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}#media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}
10-29 14:03:50.198: V/httpresponsetag:(17810): </style>
10-29 14:03:50.198: V/httpresponsetag:(17810): <a href=//www.google.com/><img src=//www.google.com/images/errors/logo_sm.gif alt=Google></a>
10-29 14:03:50.198: V/httpresponsetag:(17810): <p><b>405.</b> <ins>That’s an error.</ins>
10-29 14:03:50.198: V/httpresponsetag:(17810): <p>The request method <code>POST</code> is inappropriate for the URL <code>/</code>. <ins>That’s all we know.</ins>
This may indicate it's in fact my self signed cert, but if i open up https:servername, it works (of course with the default warning)
EDIT:
I was getting the same errors even with accepting all certs, so i went and looked in my browser with the hostname I'm using, same error. I then looked at my NAT settings on my router...I was forwarding to port 80, instead of 443. FAIL. changed to 443, now it looks like it's working, at least with accepting all certs and the following code:
public JSONObject makeHttpRequest(String url, String method,
List<NameValuePair> params) throws NoSuchAlgorithmException,
CertificateException, NotFoundException, KeyStoreException,
KeyManagementException {
// Making HTTP request
try {
// check for request method
if (method == "POST") {
// request method is POST
// defaultHttpClient
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs,
String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs,
String authType) {
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts,
new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc
.getSocketFactory());
} catch (Exception e) {
}
// Now you can access an https URL without having the certificate in the truststore
HttpClient client = new DefaultHttpClient();
client = this.sslClient(client);
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params));
// Log.v(TAG, EntityUtils.toString(result.getEntity()));
HttpResponse httpResponse = client.execute(httpPost);
// Log.v("httpresponsetag:", EntityUtils.toString(httpResponse
// .getEntity()));
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}
Forget about re-inventing lazy-SSL or whatever. Simply use SSL and fix your code. And do not turn off certificate verification and trust all certificates. Using a self-signed certificate is not particularly difficult, post what you have tried and people will point you in the right direction. Generally you need to:
get the certificate
put it in a raw resource in your app
read it and initialize a KeyStore with it
pass this to your SSL socket factory
initialize your HTTP client with the socket factory from 4.
This is how to do it if you are using HttpClient, the point is registering the SSLSocketFactory:
KeyStore ts = KeyStore.getInstance("BKS");
InputStream in = getResources().openRawResource(R.raw.mytruststore);
ts.load(in, TRUSTSTORE_PASSWORD.toCharArray());
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(ts);
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
HttpParams params = new BasicHttpParams();
ClientConnectionManager cm =
new ThreadSafeClientConnManager(params, schemeRegistry);
HttpClient client = new DefaultHttpClient(cm, params);
See this for more examples, a sample project and some background information: http://nelenkov.blogspot.com/2011/12/using-custom-certificate-trust-store-on.html
I don't see why you would want to work around SSL and invent your own encryption scheme. I have a feeling your self-signed cert is causing you issues, perhaps you need to turn off verifying the self-signed cert in eclipse?
I understand you are working with ssl but there is one alternative if you would like. It is using encode and decode.
function encodeString($ss,$ntime){
for($i=0;$i<$ntime;$i++){
$ss=base64_encode($ss);
}
return $ss;
}
function decodeString($ss,$ntime){
for($i=0;$i<$ntime;$i++){
$ss=base64_decode($ss);
}
return $ss;
}
You can use it like,
encodeString("$membername", 3); //3 will make the encryption more strong. Higher the value higher the encryption.
decodeString("$membername", 3); //decodes the member name.
Related
I have a simple PHP web-service which return result as JSON .
if($_SERVER["REQUEST_METHOD"]=="POST"){
$arg1=$_POST["arg1"];
processArgs($arg1);
}
processArgs($arg1){
$result=doSomething($arg1);
echo json_encode($result);
}
I could call it from Android side using HttpURLConnection. But the problem is HttpURLConnection seems to be a work in very low level. Is there any level implementation which we could avoid writing the same code for making it asynchronous and for parsing the result.
Simply use HttpClient as follows.
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://your-host/comm.php");
try {
//add your post data
List<NameValuePair> args = new ArrayList<NameValuePair>(2);
args.add(new BasicNameValuePair("arg1", "your-arg-1"));
httpPost.setEntity(new UrlEncodedFormEntity(args));
//send your request
HttpResponse response = httpClient.execute(httpPost);
} catch (ClientProtocolException | IOException e) {
e.printStackTrace();
}
HttpURLConnection (java.net.HttpURLConnection) is the default HTTP client in Android.
OkHttp, is another one which became the engine that powers HttpUrlConnection as of Android 4.4. It is offers easier method to customize each requests.
Both HttpURLConnection and OkHttp works at somewhat low level. So we need to write our own code for making it asynchronous and for parsing the result again and again.
Retrofit, on the other hand, is a high level implementation which uses OkHttp for connection and Gson for parsing result. It can be used to turn HTTP APIs into a Java interface with ease.
Therefore, Retrofit would be the best choice unless you have specific reason to go with OkHttp ( Like HTTP-based streaming).
my app is working perfect when i am saving data to web server,but when i save it on local host database it is giving
Value <!DOCTYPE of type java.lang.String cannot be converted to JSONObject error..
here is my code:
try
{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("www.website.com/exmpl/insertData.php"); //working perfectly
HttpPost httppost = new HttpPost("http://192.168.0.1/exmpl/insertData.php");
httppost.setEntity(new UrlEncodedFormEntity(params));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
Log.e("pass 1", "connection success ");
}
log cat:
01-05 19:02:53.150: E/pass 1(28604): connection success
01-05 19:02:53.410: E/Fail 3(28604): org.json.JSONException: Value <!DOCTYPE of type java.lang.String cannot be converted to JSONObject
EDIT
errors if i change the ip address to 192.168.0.123
01-06 10:00:37.560: E/Fail 1(4261): org.apache.http.conn.HttpHostConnectException: Connection to http://192.168.0.123 refused
01-06 10:00:37.570: E/AndroidRuntime(4261): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
and i am not using async task,,i am extending activity
even tried 192.168.0.123:80 followed MR.HU'NG
Error again
Value <!DOCTYPE of type java.lang.String cannot be converted to JSONObject
The return value of 192.168.0.1/exmpl/insertData.php on the ANDROID DEVICE are not the same of your PC, because, 192.168.0.1 in the DEVICE points to itself, not to you apache server.
You must use the IP of your machine, assigned by the local router, or configure your emulator to use some fixed ip.
Probably, the 192.168.0.1/exmpl/insertData.php are returning something like "page not found".
I'm attempting to connect an Android app to a web service originally designed for iOS and I'm not sure if it's going to be possible. I also did not design this web service so I am looking through this code that isn't mine and just kind of fumbling around. The current web service is written in PHP and uses SOAP and WSDL. If i can connect to this web service I will attempt to use ksoap2 unless I find a better alternative. I will be using a lot of links in this post because without them you will be looking at an obnoxious amount of code.
So anyways here is the problem in a nutshell. I have this list for the web service url's
URL List and from the looks of that list I need to make my login request to iphoneview/iphone_get_details.php which has this inside of it.
get_detailshowever when I use this code
class LogMeIn extends AsyncTask<String, Void, String> {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(
"http://www.fakesite.com/myv2/iphoneview/iphone_get_details.php");
protected String doInBackground(String... urls) {
try {
username = un.getText().toString();
password = pw.getText().toString();
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
2);
nameValuePairs
.add(new BasicNameValuePair("username", username));
nameValuePairs
.add(new BasicNameValuePair("password", password));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
String res = inputStream(response.getEntity().getContent())
.toString();
Log.v("RESPONSE", res);
// if username and password are valid, launch main activity
if (res.toString() == "1") {
Intent logIn = new Intent(getApplicationContext(),
Main.class);
startActivity(logIn);
}
// send the user a message saying the login failed
else {
runOnUiThread(new Runnable() {
public void run() {
pw.setText("");
fail.setText(R.string.fail);
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String file_url) {
}
}
all I get back is this for a response
05-25 13:57:21.292: V/RESPONSE(5871): <?xml version='1.0' encoding='UTF-8'?><!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'><plist version='1.0'><dict><key>iId</key><string>0</string><key>itemChildren</key><array></array></dict></plist>
I know where this response is coming from, there very bottom of the iphoneview/iphone_get_details.php. The problem is I don't know if it's because
I need to be wrapping this in a SOAP request
I can't use this code
I'm connecting to the wrong file (doubt it)
all of the above and/or something else
Now common sense tells me from looking at the current iphone_get_details.php file that I will not be receiving a response of "1" or "true" either way. As a matter of fact that file looks like it sends back a ton of information, when in essence all I want is a "1" or a "true" to make sure I'm properly connected with the correct login information. So if anyone has the time to look this question over I'd be grateful, I understand it's a lot of reading.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
<plist version='1.0'>
<dict>
<key>iId</key>
<string>0</string>
<key>itemChildren</key>
<array></array>
</dict>
</plist>
If you're going to examine the result, the iId is 0, it could mean that the iId is set to zero if the username and password does not exist, coz initially, ids's starts with 1 or it could be that the iId of the user in the database is 0 by then you can say the the username and password matched in the database and would mean the password and username provided by the user is correct or the other way around.
edited:::
I have checked your link here http://pastebin.com/8vv1Vvxj and based on the code and the xml returned, it signifies that the default value of iId is 0 if there are no matched username and password, otherwise if the iId is not 0 meaning the username and password is correct.
initial values in php
$vUserName = stripslashes(trim($_REQUEST["txtUser"]));
$vPassword = stripslashes(trim($_REQUEST["txtPass"]));
$returnFinalString = "";
$member_id = 0;
.
.
.
//if this condition is true, meaning, the username and password matched and exist
if(mysql_num_rows($member_res)>0){
$member_id = $mem_row['user_id'];
}
//and the $member_id is changed to non-zero
and for the result, you just have to parse the xml returned and get the iId value and check if it's 0 or not, by then you can determine if the user can be logged in or not.
Some pointers - you might consider using Robospice and then parsing the response into Java POJO's using Android Spring and the SimpleXML message converter. The response you are getting back is the XML for an iphone plist, which is going to be challenging to work with in the Android environment, but not impossible. The response from the server has nothing to do with your Android code, so you should be able to verify that it's the same as what gets sent to the iPhone app by using a web browser.
Hopefully this gives you some places to look and some forward momentum.
I Used to make my connection to php for mysql with HTTP, Now I am asked to Use HTTPS as it is more secure. but i tried to many ways but can't get the tablet to POST or GET any information, I made a self signed certificate and added to Local Computer trusted zone so i wont be asked that its is not verified do i want to continue, i tried connecting by browser and it worked fine and printed all the info that i needed, but not through the app. i attached my Previous HTTP code that i need to change to HTTPS. would like some help to change this connection to HTTPS.
httpclient = new DefaultHttpClient();
httppost = new HttpPost ("http://xx.xx.xx.xx/E-MENU/login.php");
username = etUser.getText().toString();
password = etPass.getText().toString();
password = md5(SHA1(password));
try{
nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("username", username));
nameValuePairs.add(new BasicNameValuePair("password", password));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
response = httpclient.execute(httppost);
if(response.getStatusLine().getStatusCode() == 200){
entity = response.getEntity();
if (entity!= null){
InputStream instream = entity.getContent();
JSONObject jsonResponse = new JSONObject (convertStreamToString(instream));
thanks Upfront.
So it works when you use HTTP but not HTTPS? The problem will be caused by the self-signed certificate on the server not being trusted by Android.
See the accepted answer for this question: Self-signed SSL acceptance on Android
when sending data to a php server through android I cam across this error: "Notice: Undefined index: IT in C:\xampp\htdocs\hello.php on line 4". After a few hours of messing around with it I can't solve my issue.
hello.php :
<?php
mysql_connect("localhost","user","pass");
mysql_select_db("mmo");
$r=mysql_query("update players set X = '".$_REQUEST['IT']."' where 22=22");
if(!$r)
echo "Error in query: ".mysql_error();
mysql_close();
header('Refresh: 0.01; URL=http://localhost/hello.php');
?>
update method in android:
public void UpdateSeverWithPlayer()
{List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("IT","3"));
try{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://10.0.2.2/hello.php");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
}catch(Exception e){
Log.e("log_tag", "Error in http connection"+e.toString());
}}
I am working on the android emulator and do have the internet permission. As always help is greatly appreciated.
EDIT 1: It appears the issue is in the android and not the php code.
You're not sending your query string propertly somehow, so there is no $_REQUEST['IT'] on the PHP side. do a var_dump($_REQUEST) to see what's coming through. Or better yet, don't use $_REQUEST. It's somewhat insecure. Better use the superglobal directly related to your HTTP method, _GET or _POST.
As well, the Refresh header is non-standard. it'll work, but you shouldn't depend on it. An HTTP level redirect uses the Location: header. Refresh: is a non-standard old-school Netscape extension.