changeset 1207:4382cb0e63d2

User is now prompted on unknown password to keystore * netx/net/sourceforge/jnlp/security/KeyStores.java: Operations above keystores moved to calls to SecurityUtil.storeKeyStore/loadKeyStore * netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java: same * netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java: same * netx/net/sourceforge/jnlp/security/SecurityUtil.java: (initKeyManagerFactory) setKeyEntry) (getKey) (loadKeyStore) (setKeyEntry/storeKeyStore) refactored to use unified call to unlockKeystore. Added inner class KeystorePasswordAttempter which is reponsible for attempting several passwrods and to ask user if fail.
author Jiri Vanek <jvanek@redhat.com>
date Wed, 15 Apr 2015 10:12:09 +0200
parents 039af1289ac9
children 3a2bcf0e60d9
files ChangeLog netx/net/sourceforge/jnlp/security/KeyStores.java netx/net/sourceforge/jnlp/security/SecurityUtil.java netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java
diffstat 5 files changed, 172 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Apr 13 11:25:58 2015 -0400
+++ b/ChangeLog	Wed Apr 15 10:12:09 2015 +0200
@@ -1,3 +1,15 @@
+2015-04-15  Jiri Vanek  <jvanek@redhat.com>
+
+	User is now prompted on unknown password to keystore
+	* netx/net/sourceforge/jnlp/security/KeyStores.java: Operations above keystores
+	moved to calls to SecurityUtil.storeKeyStore/loadKeyStore
+	* netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java: same
+	* netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java: same 
+	* netx/net/sourceforge/jnlp/security/SecurityUtil.java: (initKeyManagerFactory)
+	(setKeyEntry) (getKey) (loadKeyStore) (setKeyEntry/storeKeyStore) refactored
+	to use unified call to unlockKeystore. Added inner class KeystorePasswordAttempter
+	which is reponsible for attempting several passwrods and to ask user if fail.
+
 2015-04-13  Jie Kang  <jkang@redhat.com>
 
 	Increase server timeout for remote reproducers
--- a/netx/net/sourceforge/jnlp/security/KeyStores.java	Mon Apr 13 11:25:58 2015 -0400
+++ b/netx/net/sourceforge/jnlp/security/KeyStores.java	Wed Apr 15 10:12:09 2015 +0200
@@ -340,17 +340,12 @@
 
                 ks = KeyStore.getInstance(KEYSTORE_TYPE);
                 SecurityUtil.loadKeyStore(ks, null);
-                FileOutputStream fos = new FileOutputStream(file);
-                SecurityUtil.keyStoreStore(ks, fos);
-                fos.close();
+                SecurityUtil.storeKeyStore(ks, file);
             }
 
-            // TODO catch exception when password is incorrect and prompt user
-
             if (file.exists()) {
-                fis = new FileInputStream(file);
                 ks = KeyStore.getInstance(KEYSTORE_TYPE);
-                SecurityUtil.loadKeyStore(ks, fis);
+                SecurityUtil.loadKeyStore(ks, file);
             } else {
                 ks = KeyStore.getInstance(KEYSTORE_TYPE);
                 SecurityUtil.loadKeyStore(ks, null);
--- a/netx/net/sourceforge/jnlp/security/SecurityUtil.java	Mon Apr 13 11:25:58 2015 -0400
+++ b/netx/net/sourceforge/jnlp/security/SecurityUtil.java	Wed Apr 15 10:12:09 2015 +0200
@@ -43,6 +43,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.GeneralSecurityException;
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -50,7 +51,12 @@
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.List;
 import javax.net.ssl.KeyManagerFactory;
+import javax.swing.JOptionPane;
+import jdk.nashorn.internal.runtime.StoredScript;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
 
 import net.sourceforge.jnlp.security.KeyStores.Level;
 import net.sourceforge.jnlp.security.KeyStores.Type;
@@ -190,9 +196,7 @@
             if (madeDir || dir.isDirectory()) {
                 KeyStore ks = KeyStore.getInstance("JKS");
                 loadKeyStore(ks, null);
-                FileOutputStream fos = new FileOutputStream(certFile);
-                keyStoreStore(ks, fos);
-                fos.close();
+                storeKeyStore(ks, certFile);
                 return true;
             } else {
                 return false;
@@ -216,9 +220,8 @@
             try {
                 File file = new File(getTrustedCertsFilename());
                 if (file.exists()) {
-                    fis = new FileInputStream(file);
                     ks = KeyStore.getInstance("JKS");
-                    loadKeyStore(ks, fis);
+                    loadKeyStore(ks, file);
                 }
             } catch (Exception e) {
                 OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
@@ -290,23 +293,160 @@
     
     
     public static void initKeyManagerFactory(KeyManagerFactory kmf, KeyStore ks) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
-        kmf.init(ks, SecurityUtil.getTrustedCertsPassword());
-
+        try {
+            KeystorePasswordAttempter.INSTANCE.unlockKeystore(KeystorePasswordAttempter.UNLOCK_TYPE.initKeyManagerFactory, kmf, ks, null, null, null, null);
+        } catch (IOException | CertificateException ex) {
+            throw unexpectedException(ex);
+        }
     }
 
     public static void setKeyEntry(KeyStore ks, String alias, Key key, Certificate[] certChain) throws KeyStoreException {
-         ks.setKeyEntry(alias, key, SecurityUtil.getTrustedCertsPassword(), certChain);
+        try {
+            KeystorePasswordAttempter.INSTANCE.unlockKeystore(KeystorePasswordAttempter.UNLOCK_TYPE.setKeyEntry, null, ks, alias, key, certChain, null);
+        } catch (NoSuchAlgorithmException | UnrecoverableKeyException | IOException | CertificateException ex) {
+            throw unexpectedException(ex);
+        }
     }
 
     public static Key getKey(KeyStore ks, String alias) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
-        return ks.getKey(alias, getTrustedCertsPassword());
+        try {
+            return KeystorePasswordAttempter.INSTANCE.unlockKeystore(KeystorePasswordAttempter.UNLOCK_TYPE.getKey, null, ks, alias, null, null, null);
+        } catch (IOException | CertificateException ex) {
+            throw unexpectedException(ex);
+        }
+    }
+
+    public static void loadKeyStore(KeyStore ks, File f) throws IOException, NoSuchAlgorithmException, CertificateException {
+        try {
+            KeystorePasswordAttempter.INSTANCE.unlockKeystore(KeystorePasswordAttempter.UNLOCK_TYPE.loadKeyStore, null, ks, null, null, null, f);
+        } catch (KeyStoreException | UnrecoverableKeyException ex) {
+            throw unexpectedException(ex);
+        }
+
+    }
+
+    public static void storeKeyStore(KeyStore ks, File f) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
+        try {
+            KeystorePasswordAttempter.INSTANCE.unlockKeystore(KeystorePasswordAttempter.UNLOCK_TYPE.storeKeyStore, null, ks, null, null, null,  f);
+        } catch (UnrecoverableKeyException ex) {
+            throw unexpectedException(ex);
+        }
+    }
+
+    private static RuntimeException unexpectedException(Exception ex) {
+        return new RuntimeException("This usage of KeystorePasswordAttempter shopuld not throw this kind of exception", ex);
     }
 
-    public static void loadKeyStore(KeyStore ks, InputStream is) throws IOException, NoSuchAlgorithmException, CertificateException {
-        ks.load(is, SecurityUtil.getTrustedCertsPassword());
+    private static class KeystorePasswordAttempter {
+
+        private static enum UNLOCK_TYPE {
+
+            initKeyManagerFactory, setKeyEntry, getKey, loadKeyStore, storeKeyStore;
+        }
+
+        private static class SavedPassword {
+
+            private final char[] pass;
+
+            public SavedPassword(char[] pass) {
+                this.pass = pass;
+            }
+        }
+
+        private static final KeystorePasswordAttempter INSTANCE = new KeystorePasswordAttempter(getTrustedCertsPassword());
+        private final List<SavedPassword> passes;
+
+        private KeystorePasswordAttempter(char[]  
+            ... initialPasswords) {
+            passes = new ArrayList<>(initialPasswords.length);
+            for (char[] pass : initialPasswords) {
+                passes.add(new SavedPassword(pass));
+            }
+        }
+
+        private Key unlockKeystore(UNLOCK_TYPE type, KeyManagerFactory kmf, KeyStore ks, String alias, Key key, Certificate[] certChain, File f) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException {
+            Exception firstEx = null;
+            String messages = "";
+            for (int i = 0; i < passes.size(); i++) {
+                SavedPassword pass = passes.get(i);
+                try {
+                    switch (type) {
+                        case initKeyManagerFactory:
+                            kmf.init(ks, pass.pass);
+                            return null;
+                        case setKeyEntry:
+                            ks.setKeyEntry(alias, key, pass.pass, certChain);
+                            return null;
+                        case getKey:
+                            return ks.getKey(alias, pass.pass);
+                        case loadKeyStore:
+                            if (f == null) {
+                                ks.load(null, pass.pass);
+                            } else {
+                                try (FileInputStream fis = new FileInputStream(f)) {
+                                    ks.load(fis, pass.pass);
+                                }
+                            }
+                            return null;
+                        case storeKeyStore:
+                            if (f == null) {
+                                ks.store(null, pass.pass);
+                            } else {
+                                try (FileOutputStream fos = new FileOutputStream(f)) {
+                                    ks.store(fos, pass.pass);
+                                }
+                            }
+                            return null;
+                        default:
+                            throw new IllegalArgumentException("Unknow attempt during unlocking keystore");
+                    }
+                } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | IOException | CertificateException ex) {
+                    if (firstEx == null) {
+                        firstEx = ex;
+                    }
+                    messages += "'" + ex.getMessage() + "' ";
+                    OutputController.getLogger().log(ex);
+                    //tried all known, ask for new or finally die
+                    if (i + 1 == passes.size()) {
+                        String s1 = "Got " + messages + " during keystore operation " + type.toString() + ". Attempt " + (i + 1);
+                        OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, s1);
+                        OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, "Invalid password?");
+                        if (JNLPRuntime.isHeadless()) {
+                            OutputController.getLogger().log("Headless mode currently dont support runtime-passwords");
+                            finish(firstEx);
+                        } else {
+                            String s = JOptionPane.showInputDialog(s1 + "\n" + "Type new password and press ok. Give up by pressing anything else.");
+                            if (s == null) {
+                                finish(firstEx);
+                            }
+                            passes.add(new SavedPassword(s.toCharArray()));
+
+                        }
+                        //user already read all messages, now sho only last one
+                        messages = "";
+                    }
+                }
+
+            }
+            return null;
+        }
+
+        private void finish(Exception ex) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException {
+            if (ex instanceof KeyStoreException) {
+                throw (KeyStoreException) ex;
+            } else if (ex instanceof NoSuchAlgorithmException) {
+                throw (NoSuchAlgorithmException) ex;
+            } else if (ex instanceof UnrecoverableKeyException) {
+                throw (UnrecoverableKeyException) ex;
+            } else if (ex instanceof IOException) {
+                throw (IOException) ex;
+            } else if (ex instanceof CertificateException) {
+                throw (CertificateException) ex;
+            } else {
+                throw new RuntimeException("Unexpected exception", ex);
+            }
+        }
+
     }
 
-    public static void keyStoreStore(KeyStore ks, OutputStream fos) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
-        ks.store(fos,SecurityUtil.getTrustedCertsPassword());
-    }
 }
--- a/netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java	Mon Apr 13 11:25:58 2015 -0400
+++ b/netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java	Wed Apr 15 10:12:09 2015 +0200
@@ -357,13 +357,7 @@
                     if (!keyStoreFile.isFile()) {
                         FileUtils.createRestrictedFile(keyStoreFile, true);
                     }
-
-                    OutputStream os = new FileOutputStream(keyStoreFile);
-                    try {
-                        SecurityUtil.keyStoreStore(ks, os);
-                    } finally {
-                        os.close();
-                    }
+                    SecurityUtil.storeKeyStore(ks, keyStoreFile);
                     OutputController.getLogger().log("certificate is now permanently trusted");
                 } catch (Exception ex) {
                     // TODO: Let NetX show a dialog here notifying user
--- a/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java	Mon Apr 13 11:25:58 2015 -0400
+++ b/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java	Wed Apr 15 10:12:09 2015 +0200
@@ -399,12 +399,8 @@
                         FileUtils.createRestrictedFile(keyStoreFile, true);
                     }
 
-                    OutputStream os = new FileOutputStream(keyStoreFile);
-                    try {
-                        SecurityUtil.keyStoreStore(ks, os);
-                    } finally {
-                        os.close();
-                    }
+                    SecurityUtil.storeKeyStore(ks, keyStoreFile);
+
                     repopulateTables();
                 } catch (Exception ex) {
                     // TODO: handle exception
@@ -488,9 +484,7 @@
                             if (!keyStoreFile.isFile()) {
                                 FileUtils.createRestrictedFile(keyStoreFile, true);
                             }
-                            FileOutputStream fos = new FileOutputStream(keyStoreFile);
-                            SecurityUtil.keyStoreStore(keyStore, fos);
-                            fos.close();
+                            SecurityUtil.storeKeyStore(keyStore, keyStoreFile);
                         }
                     }
                     repopulateTables();