diff -r 7755e7bc3b31 netx/net/sourceforge/jnlp/security/KeyStores.java --- a/netx/net/sourceforge/jnlp/security/KeyStores.java Fri Apr 03 11:00:45 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/KeyStores.java Fri Apr 03 16:06:56 2015 +0200 @@ -364,17 +364,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); diff -r 7755e7bc3b31 netx/net/sourceforge/jnlp/security/SecurityUtil.java --- a/netx/net/sourceforge/jnlp/security/SecurityUtil.java Fri Apr 03 11:00:45 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/SecurityUtil.java Fri Apr 03 16:06:56 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 { + 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 { + 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 setKeyEntry(KeyStore ks, String alias, Key key, Certificate[] certChain) throws KeyStoreException { - ks.setKeyEntry(alias, key, SecurityUtil.getTrustedCertsPassword(), certChain); + 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); + } } - public static Key getKey(KeyStore ks, String alias) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - return ks.getKey(alias, getTrustedCertsPassword()); + 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 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()); - } } diff -r 7755e7bc3b31 netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java --- a/netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java Fri Apr 03 11:00:45 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java Fri Apr 03 16:06:56 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 diff -r 7755e7bc3b31 netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java --- a/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java Fri Apr 03 11:00:45 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java Fri Apr 03 16:06:56 2015 +0200 @@ -400,12 +400,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 @@ -490,9 +486,7 @@ if (!keyStoreFile.isFile()) { FileUtils.createRestrictedFile(keyStoreFile, true); } - FileOutputStream fos = new FileOutputStream(keyStoreFile); - SecurityUtil.keyStoreStore(keyStore, fos); - fos.close(); + SecurityUtil.storeKeyStore(keyStore, keyStoreFile); } } repopulateTables();