package net.screenfreeze.deskcon;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Locale;
import net.screenfreeze.deskcon.DesktopHostsActivity.OnPairListener;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Base64;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
@SuppressLint("ShowToast")
public class AuthenticationManager {
private Context context;
private DesktopHostsDBHelper dbhelper;
private static Toast ConnectionError;
public AuthenticationManager(Context context) {
this.context = context;
dbhelper = new DesktopHostsDBHelper(context);
ConnectionError = Toast.makeText(this.context, "could not connect",
Toast.LENGTH_SHORT);
}
@SuppressLint("NewApi")
public void pairWithHost(String ip, int port, String ssid, OnPairListener onpairlistener) {
Log.d("Pairing: ", "start");
// set UI Callback
ParingTask pairingTask = new ParingTask();
pairingTask.setOnPairListener(onpairlistener);
// start pairing
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
pairingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ip, port, ssid);
} else {
pairingTask.execute(ip, port, ssid);
}
}
private AlertDialog buildValidateFingerprintDialog(String myfp, String serverfp, final ParingTask pairingtask) {
LayoutInflater li = ((Activity) context).getLayoutInflater();
View verifyFpView = li.inflate(R.layout.verify_fingerprint_dialog, null);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setView(verifyFpView);
alertDialogBuilder.setTitle("Pairing");
final TextView serverfptextview = (TextView) verifyFpView.findViewById(R.id.serverfptextView);
final TextView myfptextview = (TextView) verifyFpView.findViewById(R.id.myfptextView);
serverfptextview.setText(serverfp);
myfptextview.setText(myfp);
alertDialogBuilder.setPositiveButton("yes", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
pairingtask.setFingerprintsValid(true);
dialog.cancel();
}});
alertDialogBuilder.setNegativeButton("no", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
pairingtask.setFingerprintsValid(false);
dialog.cancel();
}});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
private AlertDialog buildPairingFailedDialog() {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle("Pairing");
alertDialogBuilder.setMessage("The pairing Process failed!");
alertDialogBuilder.setCancelable(true);
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
private class ParingTask extends AsyncTask<Object, Object, Boolean> {
String ServerName;
String ServerUUID;
String ServerFp;
X509Certificate ServerCert;
String Host;
int Port;
int SecPort;
String Wifi;
String MacAddress;
String MyFp;
AlertDialog fpDialog;
ProgressDialog progressDialog = null;
private OnPairListener onpaircallback;
private boolean isValid = false;
private boolean validated = false;
private KeyStore MyKeyStore;
@Override
protected Boolean doInBackground(Object... params) {
Host = (String) params[0];
Port = (Integer) params[1];
Wifi = (String) params[2];
Socket clientSocket;
InputStream keyStoreStream;
try {
keyStoreStream = context.openFileInput("devicekeystore.bks");
} catch (FileNotFoundException e1) {
return false;
}
try {
try {
String HostAddress = InetAddress.getByName(Host).getHostAddress();
clientSocket = new Socket(HostAddress, Port);
}
catch (Exception e) {
ConnectionError.show();
return false;
}
// load the keystore
MyKeyStore = KeyStore.getInstance("BKS");
MyKeyStore.load(keyStoreStream, "android".toCharArray());
OutputStream outToServer = clientSocket.getOutputStream();
BufferedReader inFromServer = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
// 1. send Pair request "P"
publishProgress(1);
outToServer.write("P".getBytes());
// 2. Cert transfer
publishProgress(2);
byte[] ServerCertdata = readWithMaxBuffer(inFromServer);
byte[] dec_server_cert = Base64.decode(ServerCertdata, 0);
ServerFp = SHA256(dec_server_cert).toUpperCase(Locale.US).replaceAll("....(?=.)", "$0 ");
byte[] mycert = MyKeyStore.getCertificate("mykeypair").getEncoded();
MyFp = SHA256(mycert).toUpperCase(Locale.US).replaceAll("....(?=.)", "$0 ");
outToServer.write(mycert);
Log.d("Pairing: ", "exchanged Certificates");
// 3. Wait for Ack from user
publishProgress(4);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteArrayInputStream bis = new ByteArrayInputStream(dec_server_cert);
ServerCert = (X509Certificate) cf.generateCertificate(bis);
String CertIssuer = ServerCert.getIssuerX500Principal().getName().replace("CN=", "");
ServerUUID = CertIssuer.split("/",2)[0];
ServerName = CertIssuer.split("/",2)[1];
//dont override mykeypair (possible attack)
if (ServerUUID.equals("mykeypair")) {return false;}
String ack = inFromServer.readLine();
SecPort = Integer.parseInt(ack); // Server send his Secure Port if he accepts, 0 otherwise
while (!validated) {
Thread.sleep(1000); // wait for users choice
}
if (SecPort > 0 && isValid) {
// successfully paired
outToServer.write("OK".getBytes());
return true;
}
else {
// pairing failed
outToServer.write(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Connecting to Desktop Host...");
progressDialog.show();
super.onPreExecute();
}
@Override
protected void onProgressUpdate(Object... values) {
int phase = (Integer) values[0];
switch (phase) {
case 1:
progressDialog.setMessage("Sending pair Request...");
break;
case 2:
progressDialog.setMessage("Exchange Certificates...");
break;
case 4:
progressDialog.setMessage("Waiting for Desktop...");
fpDialog = buildValidateFingerprintDialog(MyFp, ServerFp, this);
fpDialog.show();
break;
}
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
try {
// add to keystore
MyKeyStore.setCertificateEntry(ServerUUID, ServerCert);
OutputStream output = context.openFileOutput("devicekeystore.bks", Context.MODE_PRIVATE);
MyKeyStore.store(output, "android".toCharArray());
output.close();
} catch (Exception e) {
e.printStackTrace();
}
dbhelper.addHost(Long.parseLong(ServerUUID), ServerName, Host, SecPort, Wifi, MacAddress, ServerFp);
onpaircallback.onPairingCompleted();
}
else {
AlertDialog pairingfaileddiealog = buildPairingFailedDialog();
pairingfaileddiealog.show();
}
super.onPostExecute(result);
}
// User input
public void setFingerprintsValid(boolean isvalid) {
this.isValid = isvalid;
validated = true;
}
public void setOnPairListener(OnPairListener listener) {
this.onpaircallback = listener;
}
}
public void deleteCertificate(Long uuid) {
String alias = String.valueOf(uuid);
InputStream keyStoreStream;
try {
keyStoreStream = context.openFileInput("devicekeystore.bks");
} catch (FileNotFoundException e1) {
return;
}
// load the keystore
KeyStore MyKeyStore;
try {
MyKeyStore = KeyStore.getInstance("BKS");
MyKeyStore.load(keyStoreStream, "android".toCharArray());
MyKeyStore.deleteEntry(alias);
// save keystore
OutputStream output = context.openFileOutput("devicekeystore.bks", Context.MODE_PRIVATE);
MyKeyStore.store(output, "android".toCharArray());
output.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String SHA256 (byte[] data) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data);
byte byteData[] = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<byteData.length;i++) {
String hex=Integer.toHexString(0xff & byteData[i]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
public byte[] convertChartoByteArray(char[] chars) {
byte[] bytes = String.valueOf(chars).getBytes();
return bytes;
}
private byte[] readWithMaxBuffer(BufferedReader inFromServer) throws IOException {
char[] recvbuffer = new char[4096]; // avoid buffer overflow
int readcnt = inFromServer.read(recvbuffer);
char[] tmp_dataArray = new char[readcnt];
for (int i=0; i<tmp_dataArray.length; i++) {
tmp_dataArray[i] = recvbuffer[i];
}
byte[] data = convertChartoByteArray(tmp_dataArray);
return data;
}
}