package edu.princeton.bitcointwofactorauth.android; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import com.google.bitcoin.core.MakeCertificate; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; import android.app.Activity; import android.content.Intent; import android.net.nsd.NsdManager.ResolveListener; import android.net.nsd.NsdServiceInfo; import android.os.Bundle; import android.util.Base64; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class MainActivity extends Activity implements ResolveListener { public enum State { INITIALIZING, SIGNING, WAITING } public static final String TAG = "NsdTFA"; private static String KEYSTORE_FILENAME = "mykeystore.bks"; private static String KEYSTORE_PASSWORD = "password"; private NsdHelper mNsdHelper; public static final String mServiceName = "BitcoinTwoFactor"; public static final String PREFS_NAME = mServiceName; private static final String WALLET_DATA_FILENAME = "walletDataList"; private ListView mListView; private State state; private ArrayList<WalletData> walletDataList; private SecureRandom rand = new SecureRandom(); private ArrayAdapter<WalletData> mAdapter; private File keystoreFile; private X509Certificate publicCert; @Override protected void onCreate(Bundle savedInstanceState) { Log.d("appm", "Calling onCreate for application"); super.onCreate(null); setContentView(R.layout.activity_main); keystoreFile = getFileStreamPath(KEYSTORE_FILENAME); if (!keystoreFile.exists()) { MakeCertificate.generateSelfSignedCertificate("phoneTLSCert", keystoreFile, KEYSTORE_PASSWORD); } try { KeyStore store = KeyStore.getInstance("BKS"); FileInputStream fis = new FileInputStream(keystoreFile); store.load(fis, KEYSTORE_PASSWORD.toCharArray()); fis.close(); publicCert = (X509Certificate) store.getCertificate("phoneTLSCert"); } catch (KeyStoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CertificateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.state = State.WAITING; this.walletDataList = loadWalletDataList(); mListView = (ListView) findViewById(R.id.listView); mAdapter = new ArrayAdapter<WalletData>(this, R.layout.mytextview, android.R.id.text1, walletDataList); if (mListView == null) { Log.d(TAG, "ERROR NULL mListView"); } // Assign adapter to ListView mListView.setAdapter(mAdapter); // ListView Item Click Listener mListView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // ListView Clicked item index int itemPosition = position; // ListView Clicked item value String itemValue = (String) mListView.getItemAtPosition(position); // Show Alert Toast.makeText(getApplicationContext(), "Position :"+itemPosition+" ListItem : " +itemValue , Toast.LENGTH_LONG) .show(); } }); } private void saveWalletDataList() { ObjectOutputStream objectOut = null; try { FileOutputStream fileOut = openFileOutput(WALLET_DATA_FILENAME, Activity.MODE_PRIVATE); objectOut = new ObjectOutputStream(fileOut); objectOut.writeObject(walletDataList); fileOut.getFD().sync(); } catch (IOException e) { e.printStackTrace(); } finally { if (objectOut != null) { try { objectOut.close(); } catch (IOException e) { // do nowt } } } } @SuppressWarnings("unchecked") private ArrayList<WalletData> loadWalletDataList() { ArrayList<WalletData> list = null; ObjectInputStream objectIn = null; try { FileInputStream fileIn = getApplicationContext().openFileInput(WALLET_DATA_FILENAME); objectIn = new ObjectInputStream(fileIn); list = (ArrayList<WalletData>) objectIn.readObject(); } catch (Exception e) { list = new ArrayList<WalletData>(); } finally { if (objectIn != null) { try { objectIn.close(); } catch (IOException e) { } } } return list; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } protected void onStart() { mNsdHelper = new NsdHelper(this); mNsdHelper.initializeNsd(); mNsdHelper.discoverServices(); super.onStart(); } @Override protected void onPause() { if (mNsdHelper != null) { mNsdHelper.tearDown(); } super.onPause(); } @Override protected void onResume() { super.onResume(); if (mNsdHelper != null) { mNsdHelper.discoverServices(); } } @Override protected void onDestroy() { mNsdHelper.tearDown(); super.onDestroy(); } public void newWallet(View view) { IntentIntegrator scanIntegrator = new IntentIntegrator(this); scanIntegrator.initiateScan(); } public void onActivityResult(int requestCode, int resultCode, Intent intent) { IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); if (scanningResult != null) { String scanContent = scanningResult.getContents(); if (scanContent != null) { try { byte[] qrBytes = Base64.decode(scanContent, Base64.DEFAULT); byte[] oneTimePass = Arrays.copyOf(qrBytes, 256); byte[] certBytes = Arrays.copyOfRange(qrBytes, oneTimePass.length, qrBytes.length); InputStream is = new ByteArrayInputStream(certBytes); CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate(is); is.close(); WalletData data = new WalletData(oneTimePass, cert); walletDataList.add(data); saveWalletDataList(); runOnUiThread(new Runnable() { public void run() { if(mAdapter != null) { mAdapter.notifyDataSetChanged(); } } }); state = State.INITIALIZING; Log.d(TAG, "Got certificate and password from QR code"); } catch (CertificateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { Log.e(TAG, "Resolve failed: " + errorCode); } public void onServiceResolved(NsdServiceInfo serviceInfo) { Log.e(TAG, "Resolve Succeeded. " + serviceInfo); if (serviceInfo.getServiceName().equals(mServiceName)) { Log.d(TAG, "Same IP."); return; } try { InetAddress host = serviceInfo.getHost(); int port = serviceInfo.getPort(); if (state.equals(State.INITIALIZING)) { Log.d(TAG, "Initializing wallet"); WalletData walletData = walletDataList.get(walletDataList.size() - 1); System.out.println("Cert being added is " + walletData.mCert); MakeCertificate.addPublicCert(Base64.encodeToString(walletData.mOneTimePass, Base64.DEFAULT), walletData.mCert, keystoreFile, KEYSTORE_PASSWORD); Log.d(TAG, "Creating SSL Socket " + host.toString() + ":" + port); SSLSocketFactory socketFactory = newSSLSocketFactory(); SSLSocket sslSock = (SSLSocket) socketFactory.createSocket(host, port); Log.d(TAG, "Created SSL Socket " + serviceInfo); TFAConnection connection = new TFAConnection(sslSock); walletData.initialize(connection, rand, publicCert); // TODO Fix cert alias saveWalletDataList(); mListView.refreshDrawableState(); connection.tearDown(); this.runOnUiThread(new Runnable() { public void run() { mAdapter.notifyDataSetChanged(); } }); Log.d(TAG, "Finished initializing wallet"); state = State.WAITING; } else if (state.equals(State.WAITING)) { Log.d(TAG, "Signing transaction"); Log.d(TAG, "Creating SSL Socket " + host.toString() + ":" + port); SSLSocketFactory socketFactory = newSSLSocketFactory(); SSLSocket sslSock = (SSLSocket) socketFactory.createSocket(host, port); Log.d(TAG, "Created SSL Socket " + serviceInfo); TFAConnection connection = new TFAConnection(sslSock); byte[] publicKey = connection.readPublicKey(); WalletData walletData = null; ArrayList<WalletData> list = loadWalletDataList(); for (WalletData wd : list) { if (Arrays.equals(publicKey, wd.mPublicKey)) { walletData = wd; break; } } Boolean canSign = walletData != null; connection.sendBoolean(canSign); if (canSign) { TransactionData txData = connection.readTransactionData(); connection.setState(walletData, txData); TFAConnection.setMainConnection(connection); Intent intent = new Intent(this, ConfirmActivity.class); Log.e(TAG, "Switching to confirm activity"); startActivity(intent); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private SSLSocketFactory newSSLSocketFactory() { try { // Get an instance of the Bouncy Castle KeyStore format KeyStore store = KeyStore.getInstance("BKS"); FileInputStream fis = new FileInputStream(keystoreFile); store.load(fis, KEYSTORE_PASSWORD.toCharArray()); fis.close(); KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509"); kmf.init(store, KEYSTORE_PASSWORD.toCharArray()); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(store); SSLContext context = SSLContext.getInstance("TLS"); context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLSocketFactory sf = context.getSocketFactory(); return sf; } catch (Exception e) { e.printStackTrace(); throw new AssertionError(e); } } }