/* Tibisay Movil Copyright (C) 2013 Antonio Araujo (aaraujo@cenditel.gob.ve), Jose Ruiz (jruiz@cenditel.gob.ve), Fundacion Centro Nacional de Desarrollo e Investigacion en Tecnologias Libres - CENDITEL. La Fundación CENDITEL concede permiso para usar, copiar, distribuir y/o modificar este programa, reconociendo el derecho que la humanidad posee al libre acceso al conocimiento, bajo los términos de la licencia de software GPL versión 2.0 de la Free Software Foundation. Este programa se distribuye con la esperanza de que sea util, pero SIN NINGUNA GARANTIA; tampoco las implicitas garantias de MERCANTILIDAD o ADECUACION A UN PROPOSITO PARTICULAR. Para mayor información sobre los términos de la licencia ver el archivo llamado "gpl-2.0.txt" en ingles. */ package ve.gob.cenditel.tibisaymovil; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Enumeration; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; import android.util.Log; import android.view.View; import android.widget.ListView; import android.widget.Toast; public class DirectKeyChain { private static final long serialVersionUID = 5677500714263795351L; private static final String keyStoreType = "BCPKCS12"; static final String keyStoreFilename = ".keystore"; private static KeyStore keystore = null; private static String password = null; private static DirectKeyChain theInstance; private Activity master; private boolean buttonsKeyStoreStatus; public boolean isButtonsKeyStoreStatus() { return buttonsKeyStoreStatus; } public void setButtonsKeyStoreStatus(boolean buttonsKeyStoreStatus) { this.buttonsKeyStoreStatus = buttonsKeyStoreStatus; } private ListView listView; public DirectCertificateAdapter adapter; // private MainBarClickListener mainbar; private DirectKeyChain() { } private DirectKeyChain(Activity master) { this.master=master; } public void setMasterActivity(Activity master){this.master=master;} public static DirectKeyChain getInstance() { if (DirectKeyChain.theInstance == null) { DirectKeyChain.theInstance = new DirectKeyChain(); } return (DirectKeyChain) DirectKeyChain.theInstance; } public void choosePrivateKeyAlias() { if (!keystoreExists()) { createKeystoreAndChoosePrivateKeyAlias(); } else { if (DirectKeyChain.keystore == null) { openKeystoreAndChoosePrivateKeyAlias(); } else { startKeyChainActivityAndChoosePrivateKeyAlias(); } } } public void openOrCreateKeyAlias() { DirectKeyChain.keystore=null; choosePrivateKeyAlias(); } public X509Certificate[] getCertificateChain(String alias) throws KeystoreException { checkKeystoreOpen(); try { KeyStore trusted = getKeystore(); Certificate[] chain = trusted.getCertificateChain(alias); X509Certificate[] result = new X509Certificate[chain.length]; for(int i = 0; i < chain.length; i++) { // Casting chain to X509Certificate[] seems to be not enough result[i] = (X509Certificate) chain[i]; } return result; } catch (Exception e) { throw new KeystoreException(e); } } public PrivateKey getPrivateKey(String alias) throws KeystoreException { checkKeystoreOpen(); try { KeyStore trusted = getKeystore(); return ((PrivateKeyEntry) trusted.getEntry(alias, null)).getPrivateKey(); } catch (Exception e) { throw new KeystoreException(e); } } public int deleteCertificate(String alias) throws KeystoreException{ try { KeyStore trusted = getKeystore(); trusted.deleteEntry(alias); } catch (Exception e) { throw new KeystoreException(e); } if (this.adapter!=null)this.adapter.notifyDataSetChanged(); return 0; } public Enumeration getAliases() throws KeystoreException { try { KeyStore trusted = getKeystore(); return new PrivateKeyEnumeration(trusted); } catch (KeyStoreException e) { throw new KeystoreException(e); } } public int importCertificate(Uri file, String password) throws KeystoreException { try { KeyStore trusted = getKeystore(); KeyStore p12 = KeyStore.getInstance("PKCS12"); InputStream stream = this.master.getContentResolver().openInputStream(file); p12.load(stream, password.toCharArray()); stream.close(); Enumeration aliases = p12.aliases(); int imported = 0; while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (p12.isKeyEntry(alias)) { Certificate[] chain = p12.getCertificateChain(alias); for (Certificate cert : chain) { if (!(cert instanceof X509Certificate)) { throw new KeyStoreException(); } } // TODO allow to import with a diferent alias. Warn if alias already exists trusted.setKeyEntry(alias, p12.getKey(alias, null), null, chain); imported++; } } return imported; } catch (Exception e) { IncorrectPasswordException.checkException(e); throw new KeystoreException(e); } } public void saveKeystore() throws KeystoreException, OutOfSpaceError { saveKeystore(DirectKeyChain.password); } public void saveKeystore(String password) throws KeystoreException, OutOfSpaceError { try { KeyStore trusted = getKeystore(); trusted.store( this.master.openFileOutput(DirectKeyChain.keyStoreFilename, Context.MODE_PRIVATE), password.toCharArray()); DirectKeyChain.password = password; } catch (Exception e) { FsUtils.checkOutOfSpace(e, false); throw new KeystoreException(e); } } private KeyStore newKeystore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { DirectKeyChain.keystore = KeyStore.getInstance(DirectKeyChain.keyStoreType); DirectKeyChain.keystore.load(null); return DirectKeyChain.keystore; } private KeyStore getKeystore() throws KeystoreException { return getKeystore(DirectKeyChain.password); } private KeyStore getKeystore(String password) throws KeystoreException { if (DirectKeyChain.keystore == null) { try { if (keystoreExists()) { DirectKeyChain.keystore = KeyStore.getInstance(DirectKeyChain.keyStoreType); DirectKeyChain.keystore.load(this.master.openFileInput( DirectKeyChain.keyStoreFilename), password.toCharArray()); } else { newKeystore(); } DirectKeyChain.password = password; } catch (Exception e) { DirectKeyChain.password=null; DirectKeyChain.keystore=null; if (IncorrectPasswordException.checkException(e)) { throw new IncorrectPasswordException(e); } throw new KeystoreException(e); } } return DirectKeyChain.keystore; } private KeyStore reloadKeyStore(){ //TODO if (DirectKeyChain.keystore!=null && DirectKeyChain.password!=null){ try { DirectKeyChain.keystore = KeyStore.getInstance(DirectKeyChain.keyStoreType); DirectKeyChain.keystore.load(this.master.openFileInput( DirectKeyChain.keyStoreFilename), password.toCharArray()); } catch (KeyStoreException e) {} catch (CertificateException e) {} catch (FileNotFoundException e) {} catch (NoSuchAlgorithmException e) {} catch (IOException e) {} } else{} return DirectKeyChain.keystore; } private boolean keystoreExists() { return this.master.getFileStreamPath(DirectKeyChain.keyStoreFilename).exists(); } private void checkKeystoreOpen() throws KeystoreException { if (DirectKeyChain.keystore == null) { throw new KeystoreException("Neeed to open the keystore first"); } } public void createKeystoreAndChoosePrivateKeyAlias() { final PasswordConfirmationDialog dialog = new PasswordConfirmationDialog(this.master, R.string.keystore_create_password); dialog.setAcceptListener(new DirectKeyChainAcceptListener(dialog, this.master)); dialog.show(); } private void openKeystoreAndChoosePrivateKeyAlias() { final PasswordDialog dialog = new PasswordDialog(this.master, R.string.keystore_password, true); dialog.setAcceptListener(new DirectKeyChainOpenListener(dialog, this.master)); dialog.setRecreateListener(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { createKeystoreAndChoosePrivateKeyAlias(); } }); dialog.show(); } private void startKeyChainActivityAndChoosePrivateKeyAlias() { if (DirectKeyChain.password==null) this.choosePrivateKeyAlias(); DirectKeyChain.this.buttonsKeyStoreStatus = true; DirectKeyChain.this.refreshAdapter(); /*Intent intent = new Intent(this.master, KeyChainActivity.class); this.master.startActivityForResult(intent, ActivityResult.PRIVATE_KEY_ALIAS);*/ } public ListView getListView() { return listView; } public void setListView(ListView listView) { this.listView = listView; } public void refreshAdapter(){ try { this.reloadKeyStore(); this.adapter = new DirectCertificateAdapter(DirectKeyChain.this, this.master); this.getListView().setAdapter(DirectKeyChain.this.adapter); } catch (KeystoreException e) { //DirectKeyChain.password=null; //DirectKeyChain.keystore=null; } } public class DirectKeyChainAcceptListener implements View.OnClickListener { private PasswordConfirmationDialog dialog; private Context context; public DirectKeyChainAcceptListener(PasswordConfirmationDialog dialog, Context context){ this.dialog=dialog; this.context=context; } @Override public void onClick(View v) { String newPassword = dialog.getPassword(); dialog.dismiss(); try { DirectKeyChain.this.newKeystore(); DirectKeyChain.this.saveKeystore(newPassword); DirectKeyChain.this.startKeyChainActivityAndChoosePrivateKeyAlias(); /******************** OJO********************/ // En este punto se muestran los elementos dentro del keystore /*********************************************/ Log.i("DEBUG", "Mostrar elementos1"); // if (DirectKeyChain.this.mainbar!=null) // DirectKeyChain.this.mainbar.prepareDisplayedChild(1); } catch (OutOfSpaceError e) { Toast.makeText(DirectKeyChain.this.master, R.string.error_no_space, Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(DirectKeyChain.this.master, R.string.error_unknown, Toast.LENGTH_LONG).show(); // throw new RuntimeException(e); } /*try { DirectKeyChain.this.getListView().setAdapter(new DirectCertificateAdapter(DirectKeyChain.this, this.context)); } catch (KeystoreException e) { }*/ DirectKeyChain.this.refreshAdapter(); } } public class DirectKeyChainOpenListener implements View.OnClickListener { private PasswordDialog dialog; private Context context; public DirectKeyChainOpenListener(PasswordDialog dialog, Context context){ this.dialog=dialog; this.context=context; } @Override public void onClick(View v) { String password = dialog.getPassword(); boolean ok = false; try { getKeystore(password); ok = true; } catch (IncorrectPasswordException e) { ok = false; DirectKeyChain.password=null; //DirectKeyChain.keystore=null; dialog.retry(); } catch (KeystoreException e) { //throw new RuntimeException(e); DirectKeyChain.password=null; DirectKeyChain.keystore=null; } if (ok) { DirectKeyChain.this.buttonsKeyStoreStatus = true; Log.i("DEBUG", "Mostrar elementos2"); dialog.dismiss(); startKeyChainActivityAndChoosePrivateKeyAlias(); } /*try { if (DirectKeyChain.this.adapter==null){ DirectKeyChain.this.adapter = new DirectCertificateAdapter(DirectKeyChain.this, this.context); DirectKeyChain.this.getListView().setAdapter(DirectKeyChain.this.adapter); } else{ DirectKeyChain.this.adapter.notifyDataSetChanged(); } } catch (KeystoreException e) { }*/ DirectKeyChain.this.refreshAdapter(); } } private static class PrivateKeyEnumeration implements Enumeration { private KeyStore keystore; private Enumeration aliases; private String next; public PrivateKeyEnumeration(KeyStore keystore) throws KeyStoreException { this.keystore = keystore; this.aliases = keystore.aliases(); this.next = lookForNextPrivateKeyAlias(); } private String lookForNextPrivateKeyAlias() throws KeyStoreException { while (this.aliases.hasMoreElements()) { String alias = this.aliases.nextElement(); if (this.keystore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { return alias; } } return null; } @Override public boolean hasMoreElements() { return this.next != null; } @Override public String nextElement() { String element = this.next; try { this.next = lookForNextPrivateKeyAlias(); } catch (KeyStoreException e) { //throw new RuntimeException(e); } return element; } } public boolean isPasswordCorrect() { return (DirectKeyChain.password!=null); } }