Mercurial > hg > release > icedtea-web-1.7
changeset 1208:3a2bcf0e60d9
KeystorePasswordAttempter moved to outer class. Added comments and prevention about possible null password writing to keystore. KeystorePasswordAttempter made much more object-like.
author | Jiri Vanek <jvanek@redhat.com> |
---|---|
date | Wed, 15 Apr 2015 10:36:09 +0200 |
parents | 4382cb0e63d2 |
children | 40d37c2486a0 |
files | ChangeLog netx/net/sourceforge/jnlp/resources/Messages.properties netx/net/sourceforge/jnlp/security/KeyStores.java netx/net/sourceforge/jnlp/security/KeystorePasswordAttempter.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 | 7 files changed, 315 insertions(+), 143 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Apr 15 10:12:09 2015 +0200 +++ b/ChangeLog Wed Apr 15 10:36:09 2015 +0200 @@ -1,3 +1,21 @@ +2015-04-15 Jiri Vanek <jvanek@redhat.com> + + KeystorePasswordAttempter moved to outer class. Added comments and prevention + about possible null password writing to keystore. KeystorePasswordAttempter + made much more object-like. + * netx/net/sourceforge/jnlp/resources/Messages.properties: added KSresultUntilNow + KSinvalidPassword KSheadlesWarning KSnwPassHelp keys for keystore prompt + * netx/net/sourceforge/jnlp/security/KeyStores.java: operation on keystores + moved from stream to file + * netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java: same + + removal of unused fields and imports + * netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java: same + * netx/net/sourceforge/jnlp/security/SecurityUtil.java: same. Also get rid + of default password - moved to KeystorePasswordAttempter. (initKeyManagerFactory) + (setKeyEntry) (getKey) (loadKeyStore) (storeKeyStore) moved from enum and switch to + runnable like approach. + * netx/net/sourceforge/jnlp/security/KeystorePasswordAttempter.java: moved from inner + 2015-04-15 Jiri Vanek <jvanek@redhat.com> User is now prompted on unknown password to keystore @@ -8,7 +26,7 @@ * 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. + which is responsible for attempting several passwords and to ask user if fail. 2015-04-13 Jie Kang <jkang@redhat.com>
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties Wed Apr 15 10:12:09 2015 +0200 +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties Wed Apr 15 10:36:09 2015 +0200 @@ -522,6 +522,12 @@ KSJsseCaCerts=Trusted JSSE Root CA Certificates KSClientCerts=Client Authentication Certificates +# KeyStores: set password +KSresultUntilNow=Got {0} during keystore operation {1}. Attempts to unlock: {2} +KSinvalidPassword=Invalid password? +KSheadlesWarning=Headless mode currently does not support runtime-passwords +KSnwPassHelp=Type new password and press ok. Give up by pressing anything else. + # Deployment Configuration messages DCIncorrectValue=Property "{0}" has incorrect value "{1}". Possible values {2}. DCInternal=Internal error: {0}
--- a/netx/net/sourceforge/jnlp/security/KeyStores.java Wed Apr 15 10:12:09 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/KeyStores.java Wed Apr 15 10:36:09 2015 +0200 @@ -39,7 +39,6 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.security.AllPermission; import java.security.KeyStore;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/security/KeystorePasswordAttempter.java Wed Apr 15 10:36:09 2015 +0200 @@ -0,0 +1,206 @@ +/* CertificatePane.java + Copyright (C) 2015 Red Hat, Inc. + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 2. + + IcedTea is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + */ +package net.sourceforge.jnlp.security; + +import java.io.File; +import java.io.IOException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.net.ssl.KeyManagerFactory; +import javax.swing.JOptionPane; +import net.sourceforge.jnlp.runtime.JNLPRuntime; +import net.sourceforge.jnlp.runtime.Translator; +import net.sourceforge.jnlp.util.logging.OutputController; + +class KeystorePasswordAttempter { + + private static final char[] DEFAULT_PASSWORD = "changeit".toCharArray(); + + private static char[] getTrustedCertsPassword() { + return DEFAULT_PASSWORD; + } + + static class SavedPassword { + + private final char[] pass; + + public SavedPassword(char[] pass) { + this.pass = pass; + } + } + + /** + * This password can read any keystore. But if you save with him, integrity + * of keystore will be lsot for ever. + */ + static class AllmightyPassword extends SavedPassword { + + public AllmightyPassword() { + super(null); + } + + } + + static abstract class KeystoreOperation { + + protected final KeyManagerFactory kmf; + protected final KeyStore ks; + protected final String alias; + protected final Key key; + protected final Certificate[] certChain; + protected final File f; + + public KeystoreOperation(KeyStore ks, File f) { + this(null, ks, null, null, null, f); + } + + public KeystoreOperation(KeyStore ks, String alias, Key key, Certificate[] certChain) { + this(null, ks, alias, key, certChain, null); + } + + public KeystoreOperation(KeyStore ks, String alias, Key key, Certificate[] certChain, File f) { + this(null, ks, alias, key, certChain, f); + } + + public KeystoreOperation(KeyManagerFactory kmf, KeyStore ks) { + this(kmf, ks, null, null, null, null); + } + + public KeystoreOperation(KeyManagerFactory kmf, KeyStore ks, String alias, Key key, Certificate[] certChain, File f) { + this.kmf = kmf; + this.ks = ks; + this.alias = alias; + this.key = key; + this.certChain = certChain; + this.f = f; + } + + abstract Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException; + + abstract String getId(); + + } + //static final KeystorePasswordAttempter INSTANCE = new KeystorePasswordAttempter(new SavedPassword(getTrustedCertsPassword()), new AllmightyPassword()); + static final KeystorePasswordAttempter INSTANCE = new KeystorePasswordAttempter(new SavedPassword(getTrustedCertsPassword())); + private final List<SavedPassword> passes; + private final Map<KeyStore, SavedPassword> sucesfullPerKeystore = new HashMap<>(); + + private KeystorePasswordAttempter(SavedPassword... initialPasswords) { + passes = new ArrayList<>(initialPasswords.length); + passes.addAll(Arrays.asList(initialPasswords)); + } + + Key unlockKeystore(KeystoreOperation operation) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException { + SavedPassword sucessfullKey = sucesfullPerKeystore.get(operation.ks); + Exception firstEx = null; + String messages = ""; + List<SavedPassword> localPases = new ArrayList<>(); + if (sucessfullKey != null){ + //sucessfull must be firts. If it is not, then writing to keystore by illegal password, will kill kesytore's integrity + localPases.add(sucessfullKey); + } + localPases.addAll(passes); + for (int i = 0; i < localPases.size(); i++) { + SavedPassword pass = localPases.get(i); + try { + //we expect, that any keystore is loaded before readed. + //so we are wrting by correct password + //if no sucessfull passwrod was provided during rading, then finish(firstEx); will save us from overwrite + Key result = operation.operateKeystore(pass.pass); + //ok we were sucessfull + //save the loading password for storing purposes (and another reading too) + sucesfullPerKeystore.put(operation.ks, pass); + return result; + } 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 == localPases.size()) { + String s1 = Translator.R("KSresultUntilNow", messages, operation.getId(), (i + 1)); + OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, s1); + OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, Translator.R("KSinvalidPassword")); + if (JNLPRuntime.isHeadless()) { + OutputController.getLogger().log(Translator.R("KSheadlesWarning")); + finish(firstEx); + } else { + String s = JOptionPane.showInputDialog(s1 + "\n" + Translator.R("KSnwPassHelp")); + if (s == null) { + finish(firstEx); + } + //if input is null, exception is thrown from finish method + SavedPassword users = new SavedPassword(s.toCharArray()); + passes.add(users); + localPases.add(users); + } + //user already read all messages, now show 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); + } + } + +}
--- a/netx/net/sourceforge/jnlp/security/SecurityUtil.java Wed Apr 15 10:12:09 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/SecurityUtil.java Wed Apr 15 10:36:09 2015 +0200 @@ -41,9 +41,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; 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; @@ -51,12 +48,7 @@ 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; @@ -64,15 +56,11 @@ public class SecurityUtil { - private static final char[] DEFAULT_PASSWORD = "changeit".toCharArray(); - + public static String getTrustedCertsFilename() throws Exception { return KeyStores.getKeyStoreLocation(Level.USER, Type.CERTS).getFullPath(); } - private static char[] getTrustedCertsPassword() { - return DEFAULT_PASSWORD; - } /** * Extracts the CN field from a Certificate principal string. Or, if it @@ -291,10 +279,22 @@ return caks; } - - public static void initKeyManagerFactory(KeyManagerFactory kmf, KeyStore ks) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + public static void initKeyManagerFactory(KeyManagerFactory kmf, KeyStore ks) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { try { - KeystorePasswordAttempter.INSTANCE.unlockKeystore(KeystorePasswordAttempter.UNLOCK_TYPE.initKeyManagerFactory, kmf, ks, null, null, null, null); + KeystorePasswordAttempter.INSTANCE.unlockKeystore( + new KeystorePasswordAttempter.KeystoreOperation(kmf, ks) { + + @Override + String getId() { + return "'init keymanager-factory'"; + } + + @Override + Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException { + kmf.init(ks, pass); + return null; + } + }); } catch (IOException | CertificateException ex) { throw unexpectedException(ex); } @@ -302,7 +302,20 @@ 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); + KeystorePasswordAttempter.INSTANCE.unlockKeystore( + new KeystorePasswordAttempter.KeystoreOperation(ks, alias, key, certChain) { + + @Override + String getId() { + return "'set key entry'"; + } + + @Override + Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException { + ks.setKeyEntry(alias, key, pass, certChain); + return null; + } + }); } catch (NoSuchAlgorithmException | UnrecoverableKeyException | IOException | CertificateException ex) { throw unexpectedException(ex); } @@ -310,7 +323,19 @@ 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); + return KeystorePasswordAttempter.INSTANCE.unlockKeystore( + new KeystorePasswordAttempter.KeystoreOperation(ks, alias, null, null) { + + @Override + String getId() { + return "'get key'"; + } + + @Override + Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException { + return ks.getKey(alias, pass); + } + }); } catch (IOException | CertificateException ex) { throw unexpectedException(ex); } @@ -318,7 +343,26 @@ 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); + KeystorePasswordAttempter.INSTANCE.unlockKeystore( + new KeystorePasswordAttempter.KeystoreOperation(ks, f) { + + @Override + String getId() { + return "'load keystore'"; + } + + @Override + Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException { + if (f == null) { + ks.load(null, pass); + } else { + try (FileInputStream fis = new FileInputStream(f)) { + ks.load(fis, pass); + } + } + return null; + } + }); } catch (KeyStoreException | UnrecoverableKeyException ex) { throw unexpectedException(ex); } @@ -327,7 +371,26 @@ 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); + KeystorePasswordAttempter.INSTANCE.unlockKeystore( + new KeystorePasswordAttempter.KeystoreOperation(ks, f) { + + @Override + String getId() { + return "'store keystore'"; + } + + @Override + Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IOException, CertificateException { + if (f == null) { + ks.store(null, pass); + } else { + try (FileOutputStream fos = new FileOutputStream(f)) { + ks.store(fos, pass); + } + } + return null; + } + }); } catch (UnrecoverableKeyException ex) { throw unexpectedException(ex); } @@ -337,116 +400,4 @@ return new RuntimeException("This usage of KeystorePasswordAttempter shopuld not throw this kind of exception", ex); } - 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); - } - } - - } - }
--- a/netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java Wed Apr 15 10:12:09 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/dialogs/CertWarningPane.java Wed Apr 15 10:36:09 2015 +0200 @@ -48,8 +48,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; -import java.io.FileOutputStream; -import java.io.OutputStream; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -61,7 +59,6 @@ import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JPopupMenu; import javax.swing.SwingConstants; import net.sourceforge.jnlp.JNLPFile; @@ -76,7 +73,6 @@ import net.sourceforge.jnlp.security.SecurityDialog; import net.sourceforge.jnlp.security.SecurityDialogs.AccessType; import net.sourceforge.jnlp.security.SecurityUtil; -import net.sourceforge.jnlp.security.policyeditor.PolicyEditor.PolicyEditorWindow; import net.sourceforge.jnlp.util.FileUtils; import net.sourceforge.jnlp.util.logging.OutputController; @@ -95,14 +91,12 @@ private final Certificate cert; private JCheckBox alwaysTrust; private final CertVerifier certVerifier; - private SecurityDelegate securityDelegate; - private JPopupMenu policyMenu; + private final SecurityDelegate securityDelegate; private JPanel topPanel, infoPanel, buttonPanel, bottomPanel; private JLabel topLabel, nameLabel, publisherLabel, fromLabel, bottomLabel; private JButton run, sandbox, advancedOptions, cancel, moreInfo; private boolean alwaysTrustSelected; private String bottomLabelWarningText; - private PolicyEditorWindow policyEditor = null; public CertWarningPane(SecurityDialog x, CertVerifier certVerifier, SecurityDelegate securityDelegate) { super(x, certVerifier);
--- a/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java Wed Apr 15 10:12:09 2015 +0200 +++ b/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java Wed Apr 15 10:36:09 2015 +0200 @@ -47,8 +47,6 @@ import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.File; -import java.io.FileOutputStream; -import java.io.OutputStream; import java.io.PrintStream; import java.security.KeyStore; import java.security.cert.Certificate;