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.Collections; import java.util.Enumeration; import java.util.List; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.view.View; import android.view.View.OnClickListener; import android.widget.Toast; public class CustomKeyChain extends KeyChainStrategy { private static final long serialVersionUID = 5677500714263795951L; private static final String keyStoreType = "BCPKCS12"; private static final String keyStoreFilename = ".keystore"; private static KeyStore keystore = null; private static String password = null; private CustomKeyChain() {} public static CustomKeyChain getInstance() { if (KeyChainStrategy.theInstance == null) { KeyChainStrategy.theInstance = new CustomKeyChain(); } return (CustomKeyChain) KeyChainStrategy.theInstance; } @Override public void choosePrivateKeyAlias() { this.keystore=null; /*if (!keystoreExists()) { createKeystoreAndChoosePrivateKeyAlias(); } else {*/ if (CustomKeyChain.keystore == null) { openKeystoreAndChoosePrivateKeyAlias(); } else { startKeyChainActivityAndChoosePrivateKeyAlias(); } //} } //May not be used @Override public void choosePrivateKeyList() { if (!keystoreExists()) { createKeystoreToSelect(); } else { if (CustomKeyChain.keystore == null) { openKeystoreToSelect(); } else { startKeyChainActivityToSelect(); } } } @Override 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); } } @Override 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); } } @Override public int deleteCertificate(String alias) throws KeystoreException{ try { KeyStore trusted = getKeystore(); trusted.deleteEntry(alias); } catch (Exception e) { throw new KeystoreException(e); } 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 = KeyChainStrategy.activity.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(CustomKeyChain.password); } public void saveKeystore(String password) throws KeystoreException, OutOfSpaceError { try { KeyStore trusted = getKeystore(); trusted.store( KeyChainStrategy.activity.openFileOutput(CustomKeyChain.keyStoreFilename, Context.MODE_PRIVATE), password.toCharArray()); CustomKeyChain.password = password; } catch (Exception e) { FsUtils.checkOutOfSpace(e, false); throw new KeystoreException(e); } } public KeyStore reloadKeyStore(){ if (CustomKeyChain.keystore!=null && CustomKeyChain.password!=null){ try { CustomKeyChain.keystore = KeyStore.getInstance(CustomKeyChain.keyStoreType); CustomKeyChain.keystore.load(KeyChainStrategy.activity.openFileInput( CustomKeyChain.keyStoreFilename), password.toCharArray()); } catch (KeyStoreException e) {} catch (CertificateException e) {} catch (FileNotFoundException e) {} catch (NoSuchAlgorithmException e) {} catch (IOException e) {} } return CustomKeyChain.keystore; } private KeyStore newKeystore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { CustomKeyChain.keystore = KeyStore.getInstance(CustomKeyChain.keyStoreType); CustomKeyChain.keystore.load(null); return CustomKeyChain.keystore; } private KeyStore getKeystore() throws KeystoreException { return getKeystore(CustomKeyChain.password); } private KeyStore getKeystore(String password) throws KeystoreException { if (CustomKeyChain.keystore == null) { try { if (keystoreExists()) { CustomKeyChain.keystore = KeyStore.getInstance(CustomKeyChain.keyStoreType); CustomKeyChain.keystore.load(KeyChainStrategy.activity.openFileInput( CustomKeyChain.keyStoreFilename), password.toCharArray()); } else { newKeystore(); } CustomKeyChain.password = password; } catch (Exception e) { if (IncorrectPasswordException.checkException(e)) { CustomKeyChain.keystore = null; throw new IncorrectPasswordException(e); } throw new KeystoreException(e); } } return CustomKeyChain.keystore; } private boolean keystoreExists() { return KeyChainStrategy.activity.getFileStreamPath(CustomKeyChain.keyStoreFilename).exists(); } private void checkKeystoreOpen() throws KeystoreException { if (CustomKeyChain.keystore == null) { throw new KeystoreException("Neeed to open the keystore first"); } } private void createKeystoreAndChoosePrivateKeyAlias() { Toast.makeText(CustomKeyChain.activity, "createKeystoreAndChoosePrivateKeyAliasCustom", Toast.LENGTH_LONG).show(); final PasswordConfirmationDialog dialog = new PasswordConfirmationDialog(KeyChainStrategy.activity, R.string.keystore_create_password); dialog.setAcceptListener(new OnClickListener() { @Override public void onClick(View v) { String newPassword = dialog.getPassword(); dialog.dismiss(); try { newKeystore(); saveKeystore(newPassword); startKeyChainActivityAndChoosePrivateKeyAlias(); } catch (OutOfSpaceError e) { Toast.makeText(KeyChainStrategy.activity, R.string.error_no_space, Toast.LENGTH_LONG).show(); } catch (Exception e) { //throw new RuntimeException(e); Toast.makeText(KeyChainStrategy.activity, R.string.error_unknown, Toast.LENGTH_LONG).show(); } } }); dialog.show(); } private void openKeystoreAndChoosePrivateKeyAlias() { Toast.makeText(CustomKeyChain.activity, "openKeystoreAndChoosePrivateKeyAlias", Toast.LENGTH_LONG).show(); //TODO: Setting boolean to false instead of true. Disables "New KeyStore" option. final PasswordDialog dialog = new PasswordDialog(KeyChainStrategy.activity, R.string.keystore_password, false); dialog.setAcceptListener(new OnClickListener() { @Override public void onClick(View v) { String password = dialog.getPassword(); boolean ok = false; try { getKeystore(password); ok = true; } catch (IncorrectPasswordException e) { ok = false; dialog.retry(); } catch (KeystoreException e) { Toast.makeText(KeyChainStrategy.activity, R.string.error_unknown, Toast.LENGTH_LONG).show(); //throw new RuntimeException(e); } if (ok) { //Error #75046. No certificates installed. try { List aliases = Collections.list(CustomKeyChain.this.getAliases()); if (aliases.size()>0){ dialog.dismiss(); startKeyChainActivityAndChoosePrivateKeyAlias(); }else{ Toast.makeText(KeyChainStrategy.activity, R.string.error_empty_or_null_certificates, Toast.LENGTH_LONG).show(); } } catch (KeystoreException e) { } } } }); dialog.setRecreateListener(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { createKeystoreAndChoosePrivateKeyAlias(); } }); dialog.show(); } private void startKeyChainActivityAndChoosePrivateKeyAlias() { Toast.makeText(CustomKeyChain.activity, "startKeyChainActivityAndChoosePrivateKeyAlias", Toast.LENGTH_LONG).show(); Intent intent = new Intent(KeyChainStrategy.activity, KeyChainActivity.class); KeyChainStrategy.activity.startActivityForResult(intent, ActivityResult.PRIVATE_KEY_ALIAS); } private void startKeyChainActivityToSelect() { Intent intent = new Intent(KeyChainStrategy.activity, CertificateRepositoryActivity.class); KeyChainStrategy.activity.startActivityForResult(intent, ActivityResult.PRIVATE_KEY_ALIAS); } private void createKeystoreToSelect() { Toast.makeText(CustomKeyChain.activity, "createKeystoreToSelect", Toast.LENGTH_LONG).show(); final PasswordConfirmationDialog dialog = new PasswordConfirmationDialog(KeyChainStrategy.activity, R.string.keystore_create_password); dialog.setAcceptListener(new OnClickListener() { @Override public void onClick(View v) { String newPassword = dialog.getPassword(); dialog.dismiss(); try { newKeystore(); saveKeystore(newPassword); startKeyChainActivityToSelect(); } catch (OutOfSpaceError e) { Toast.makeText(KeyChainStrategy.activity, R.string.error_no_space, Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(KeyChainStrategy.activity, R.string.error_unknown, Toast.LENGTH_LONG).show(); // throw new RuntimeException(e); } } }); dialog.show(); } private void openKeystoreToSelect() { Toast.makeText(CustomKeyChain.activity, "openKeystoreToSelect", Toast.LENGTH_LONG).show(); //TODO: Setting boolean to false instead of true. Disables "New KeyStore" option. final PasswordDialog dialog = new PasswordDialog(KeyChainStrategy.activity, R.string.keystore_password, false); dialog.setAcceptListener(new OnClickListener() { @Override public void onClick(View v) { String password = dialog.getPassword(); boolean ok; try { getKeystore(password); ok = true; } catch (IncorrectPasswordException e) { ok = false; dialog.retry(); } catch (KeystoreException e) { Toast.makeText(KeyChainStrategy.activity, R.string.error_unknown, Toast.LENGTH_LONG).show(); //throw new RuntimeException(e); ok = false; } if (ok) { dialog.dismiss(); startKeyChainActivityToSelect(); } } }); dialog.setRecreateListener(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { createKeystoreToSelect(); } }); dialog.show(); } 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) { Toast.makeText(KeyChainStrategy.activity, R.string.error_unknown, Toast.LENGTH_LONG).show(); //throw new RuntimeException(e); } return element; } } }