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); } }