From 04d525173adbbb841caac014675bbf48634cb707 Mon Sep 17 00:00:00 2001 From: csottile <42802710+csottile@users.noreply.github.com> Date: Wed, 11 Aug 2021 11:49:00 +0200 Subject: [PATCH 1/2] Enable Windows Key Store Access The changes contained in the commit allow, through appropriate configurations, to be able to access the Windows Key Stores through the use of the SunMSCAPI. This access is allowed only if the software is installed on a Windows OS machine. --- .../certificateviewer/CertificatePane.java | 90 +++++++---- .../dialogs/security/CertWarningPane.java | 2 +- .../jnlp/config/ConfigurationConstants.java | 9 ++ .../net/sourceforge/jnlp/config/Defaults.java | 15 ++ .../sourceforge/jnlp/runtime/JNLPRuntime.java | 2 +- .../jnlp/security/CertificateUtils.java | 4 +- .../sourceforge/jnlp/security/KeyStores.java | 151 ++++++++++++++---- .../jnlp/security/SecurityUtil.java | 32 ++-- .../windows/WindowsKeyStoresManager.java | 127 +++++++++++++++ .../jnlp/security/KeyStoresTest.java | 24 ++- 10 files changed, 378 insertions(+), 78 deletions(-) create mode 100644 core/src/main/java/net/sourceforge/jnlp/security/windows/WindowsKeyStoresManager.java diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/certificateviewer/CertificatePane.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/certificateviewer/CertificatePane.java index 5ac3b86c8..fd066936f 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/certificateviewer/CertificatePane.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/certificateviewer/CertificatePane.java @@ -42,6 +42,9 @@ import net.sourceforge.jnlp.runtime.JNLPRuntime; import net.sourceforge.jnlp.security.CertificateUtils; import net.sourceforge.jnlp.security.KeyStores; +import net.sourceforge.jnlp.security.KeyStores.KeyStoreWrap; +import net.sourceforge.jnlp.security.KeyStores.KeyStoreWindows; +import net.sourceforge.jnlp.security.KeyStores.KeyStoreWithPath; import net.sourceforge.jnlp.security.KeyStores.Level; import net.sourceforge.jnlp.security.SecurityUtil; import net.sourceforge.jnlp.util.RestrictedFileUtils; @@ -111,7 +114,7 @@ public class CertificatePane extends JPanel { }; JTabbedPane tabbedPane; - JTextField certPath = new JTextField(); + JTextField certLocation = new JTextField(); private final JTable userTable; private final JTable systemTable; private JComboBox certificateTypeCombo; @@ -120,6 +123,7 @@ public class CertificatePane extends JPanel { /** JComponents that should be disabled for system store */ private final List disableForSystem; + private final List disableForWindowsStores; private Window parent; private JComponent defaultFocusComponent = null; @@ -128,7 +132,7 @@ public class CertificatePane extends JPanel { * The Current KeyStore. Only one table/tab is visible for interaction to * the user. This KeyStore corresponds to that. */ - private KeyStores.KeyStoreWithPath keyStore = null; + private KeyStoreWrap keyStoreWrap = null; public CertificatePane(Window parent) { super(); @@ -139,6 +143,7 @@ public CertificatePane(Window parent) { systemTable = new JTable(null); systemTable.setAutoCreateRowSorter(true); disableForSystem = new ArrayList<>(); + disableForWindowsStores = new ArrayList<>(); addComponents(); @@ -157,7 +162,7 @@ public CertificatePane(Window parent) { */ private void initializeKeyStore() { try { - keyStore = KeyStores.getKeyStore(currentKeyStoreLevel, currentKeyStoreType); + keyStoreWrap = KeyStores.getWrapContainer(currentKeyStoreLevel, currentKeyStoreType).getWrap(); } catch (Exception e) { LOG.error(IcedTeaWebConstants.DEFAULT_ERROR_MESSAGE, e); } @@ -224,24 +229,24 @@ private void addComponents() { for (int i = 0; i < buttonNames.length; i++) { button = new JButton(buttonNames[i]); maxWidth = Math.max(maxWidth, button.getMinimumSize().width); - } - + } for (int i = 0; i < buttonNames.length; i++) { button = new JButton(buttonNames[i]); button.setMnemonic(buttonMnemonics[i]); button.addActionListener(listeners[i]); - button.setSize(maxWidth, button.getSize().height); + button.setSize(maxWidth, button.getSize().height); // import and remove buttons if (i == 0 || i == 2) { - disableForSystem.add(button); + disableForSystem.add(button); + disableForWindowsStores.add(button); } buttonPanel.add(button); } tablePanel.add(tabbedPane, BorderLayout.CENTER); JPanel buttonPanelWrapper = new JPanel(new BorderLayout()); - certPath.setEditable(false); - buttonPanelWrapper.add(certPath, BorderLayout.CENTER); + certLocation.setEditable(false); + buttonPanelWrapper.add(certLocation, BorderLayout.CENTER); buttonPanelWrapper.add(buttonPanel, BorderLayout.EAST); tablePanel.add(buttonPanelWrapper, BorderLayout.SOUTH); main.add(certificateTypePanel, BorderLayout.NORTH); @@ -277,9 +282,9 @@ private void readKeyStore() { try { //Get all of the X509Certificates and put them into an ArrayList - aliases = keyStore.getKs().aliases(); + aliases = keyStoreWrap.getKs().aliases(); while (aliases.hasMoreElements()) { - Certificate c = keyStore.getKs().getCertificate(aliases.nextElement()); + Certificate c = keyStoreWrap.getKs().getCertificate(aliases.nextElement()); if (c instanceof X509Certificate) { certs.add((X509Certificate) c); } @@ -308,15 +313,40 @@ private void repopulateTables() { initializeKeyStore(); readKeyStore(); try { - File src = new File(keyStore.getPath()); - File resolved = src.getCanonicalFile(); - if (resolved.equals(src)) { - certPath.setText(keyStore.getPath()); - LOG.info(keyStore.getPath()); - } else { - certPath.setText(keyStore.getPath() + " -> " + resolved.getCanonicalPath()); - LOG.info("{} -> {}", keyStore.getPath(), resolved.getCanonicalPath()); - } + + KeyStores.Family keyStoreFamily = keyStoreWrap.getFamily(); + + switch(keyStoreFamily) { + + case JKS: + KeyStoreWithPath keyStorePath = (KeyStoreWithPath)keyStoreWrap; + + File src = new File(keyStorePath.getPath()); + File resolved = src.getCanonicalFile(); + if (resolved.equals(src)) { + certLocation.setText(keyStorePath.getPath()); + LOG.info(keyStorePath.getPath()); + } else { + certLocation.setText(keyStorePath.getPath() + " -> " + resolved.getCanonicalPath()); + LOG.info("{} -> {}", keyStorePath.getPath(), resolved.getCanonicalPath()); + } + + if(currentKeyStoreLevel == Level.USER) { + for (JComponent component : disableForSystem) { + component.setEnabled(true); + } + } + case WINDOWS: + KeyStoreWindows keyStoreWin = (KeyStoreWindows)keyStoreWrap; + + certLocation.setText(keyStoreWin.getStoreType().getDescription()); + + for (JComponent component : disableForWindowsStores) { + component.setEnabled(false); + } + + LOG.info(keyStoreWin.getStoreType().getDescription()); + } } catch (Exception ex) { LOG.error(IcedTeaWebConstants.DEFAULT_ERROR_MESSAGE, ex); } @@ -380,6 +410,7 @@ public void actionPerformed(ActionEvent e) { * or vice versa). Changes the currentKeyStore Enables or disables buttons. */ private class TabChangeListener implements ChangeListener { + @Override public void stateChanged(ChangeEvent e) { JTabbedPane source = (JTabbedPane) e.getSource(); @@ -388,7 +419,8 @@ public void stateChanged(ChangeEvent e) { currentKeyStoreLevel = Level.USER; for (JComponent component : disableForSystem) { component.setEnabled(true); - } + } + break; case 1: currentKeyStoreLevel = Level.SYSTEM; @@ -410,11 +442,11 @@ public void actionPerformed(ActionEvent e) { int returnVal = chooser.showOpenDialog(parent); if (returnVal == JFileChooser.APPROVE_OPTION) { try { - KeyStore ks = keyStore.getKs(); + KeyStore ks = keyStoreWrap.getKs(); if (currentKeyStoreType == KeyStores.Type.CLIENT_CERTS) { char[] password = getPassword(R("CVImportPasswordMessage")); if (password != null) { - final KeyStore caks = KeyStores.getKeyStore(currentKeyStoreLevel, KeyStores.Type.CA_CERTS).getKs(); + final KeyStore caks = KeyStores.getWrapContainer(currentKeyStoreLevel, KeyStores.Type.CA_CERTS).getWrap().getKs(); final int caksSize = caks.size(); CertificateUtils.addPKCS12ToKeyStore(chooser.getSelectedFile(), ks, password, caks); if (caks.size() > caksSize) { @@ -475,16 +507,16 @@ public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); int returnVal = chooser.showSaveDialog(parent); if (returnVal == JFileChooser.APPROVE_OPTION) { - String alias = keyStore.getKs().getCertificateAlias(certs + String alias = keyStoreWrap.getKs().getCertificateAlias(certs .get(selectedRow)); if (alias != null) { if (currentKeyStoreType == KeyStores.Type.CLIENT_CERTS) { char[] password = getPassword(R("CVExportPasswordMessage")); if (password != null) { - CertificateUtils.dumpPKCS12(alias, chooser.getSelectedFile(), keyStore.getKs(), password); + CertificateUtils.dumpPKCS12(alias, chooser.getSelectedFile(), keyStoreWrap.getKs(), password); } } else { - Certificate c = keyStore.getKs().getCertificate(alias); + Certificate c = keyStoreWrap.getKs().getCertificate(alias); PrintStream ps = new PrintStream(chooser.getSelectedFile().getAbsolutePath()); CertificateUtils.dump(c, ps); } @@ -516,7 +548,7 @@ public void actionPerformed(ActionEvent e) { int selectedRow = table.getSelectedRow(); if (selectedRow != -1) { - String alias = keyStore.getKs().getCertificateAlias(certs.get(selectedRow)); + String alias = keyStoreWrap.getKs().getCertificateAlias(certs.get(selectedRow)); if (alias != null) { int i = JOptionPane.showConfirmDialog(parent, @@ -524,12 +556,12 @@ public void actionPerformed(ActionEvent e) { R("CVRemoveConfirmTitle"), JOptionPane.YES_NO_OPTION); if (i == 0) { - keyStore.getKs().deleteEntry(alias); + keyStoreWrap.getKs().deleteEntry(alias); File keyStoreFile = KeyStores.getKeyStoreLocation(currentKeyStoreLevel, currentKeyStoreType).getFile(); if (!keyStoreFile.isFile()) { RestrictedFileUtils.createRestrictedFile(keyStoreFile); } - SecurityUtil.storeKeyStore(keyStore.getKs(), keyStoreFile); + SecurityUtil.storeKeyStore(keyStoreWrap.getKs(), keyStoreFile); } } repopulateTables(); diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/CertWarningPane.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/CertWarningPane.java index a509fe628..a9d585776 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/CertWarningPane.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/CertWarningPane.java @@ -332,7 +332,7 @@ public void actionPerformed(ActionEvent e) { public void saveCert() { try { - KeyStore ks = KeyStores.getKeyStore(Level.USER, Type.CERTS).getKs(); + KeyStore ks = KeyStores.getWrapContainer(Level.USER, Type.CERTS).getWrap().getKs(); X509Certificate c = (X509Certificate) parent.getCertVerifier().getPublisher(null); CertificateUtils.addToKeyStore(c, ks); File keyStoreFile = KeyStores.getKeyStoreLocation(Level.USER, Type.CERTS).getFile(); diff --git a/core/src/main/java/net/sourceforge/jnlp/config/ConfigurationConstants.java b/core/src/main/java/net/sourceforge/jnlp/config/ConfigurationConstants.java index 1b63e1d07..0bb5eeab1 100644 --- a/core/src/main/java/net/sourceforge/jnlp/config/ConfigurationConstants.java +++ b/core/src/main/java/net/sourceforge/jnlp/config/ConfigurationConstants.java @@ -110,6 +110,15 @@ public interface ConfigurationConstants { String KEY_SECURITY_EXPIRED_WARNING = "deployment.security.expired.warning"; String KEY_SECURITY_JSSE_HOSTMISMATCH_WARNING = "deployment.security.jsse.hostmismatch.warning"; + + + /** + * Properties to manage to access Windows key stores + */ + String KEY_SECURITY_USE_ROOTCA_STORE_TYPE_WINDOWS_ROOT = "deployment.security.use.rootca.store.type.windowsRoot"; + + String KEY_SECURITY_USE_ROOTCA_STORE_TYPE_WINDOWS_MY = "deployment.security.use.rootca.store.type.windowsMy"; + /** * Boolean. Only show security prompts to user if true diff --git a/core/src/main/java/net/sourceforge/jnlp/config/Defaults.java b/core/src/main/java/net/sourceforge/jnlp/config/Defaults.java index 9619b8078..3a6365cf1 100644 --- a/core/src/main/java/net/sourceforge/jnlp/config/Defaults.java +++ b/core/src/main/java/net/sourceforge/jnlp/config/Defaults.java @@ -182,6 +182,21 @@ public class Defaults { ValidatorFactory.createFilePathValidator() ), + /* + * Windows certificates key stores + */ + Setting.createDefault( + ConfigurationConstants.KEY_SECURITY_USE_ROOTCA_STORE_TYPE_WINDOWS_ROOT, + String.valueOf(false), + ValidatorFactory.createBooleanValidator() + ), + Setting.createDefault( + ConfigurationConstants.KEY_SECURITY_USE_ROOTCA_STORE_TYPE_WINDOWS_MY, + String.valueOf(false), + ValidatorFactory.createBooleanValidator() + ), + + /* * security access and control */ diff --git a/core/src/main/java/net/sourceforge/jnlp/runtime/JNLPRuntime.java b/core/src/main/java/net/sourceforge/jnlp/runtime/JNLPRuntime.java index 7bffd2c48..9a94eccbe 100644 --- a/core/src/main/java/net/sourceforge/jnlp/runtime/JNLPRuntime.java +++ b/core/src/main/java/net/sourceforge/jnlp/runtime/JNLPRuntime.java @@ -272,7 +272,7 @@ public static void initialize() throws IllegalStateException { try { SSLSocketFactory sslSocketFactory; SSLContext context = SSLContext.getInstance("SSL"); - KeyStore ks = KeyStores.getKeyStore(KeyStores.Level.USER, KeyStores.Type.CLIENT_CERTS).getKs(); + KeyStore ks = KeyStores.getWrapContainer(KeyStores.Level.USER, KeyStores.Type.CLIENT_CERTS).getWrap().getKs(); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); SecurityUtil.initKeyManagerFactory(kmf, ks); TrustManager[] trust = new TrustManager[] { getSSLSocketTrustManager() }; diff --git a/core/src/main/java/net/sourceforge/jnlp/security/CertificateUtils.java b/core/src/main/java/net/sourceforge/jnlp/security/CertificateUtils.java index 4cca1e3f1..19b3bb397 100644 --- a/core/src/main/java/net/sourceforge/jnlp/security/CertificateUtils.java +++ b/core/src/main/java/net/sourceforge/jnlp/security/CertificateUtils.java @@ -126,7 +126,7 @@ public static void addPKCS12ToKeyStore(File file, KeyStore ks, char[] password, BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(bis, password); - KeyStore systemCa = KeyStores.getKeyStore(KeyStores.Level.SYSTEM, KeyStores.Type.CA_CERTS).getKs(); + KeyStore systemCa = KeyStores.getWrapContainer(KeyStores.Level.SYSTEM, KeyStores.Type.CA_CERTS).getWrap().getKs(); Enumeration aliasList = keyStore.aliases(); @@ -182,7 +182,7 @@ public static boolean inKeyStores(X509Certificate c, List keyStores) { // Verify against this entry final String alias = aliases.nextElement(); if (c.equals(keyStore.getCertificate(alias))) { - LOG.debug("{} found in cacerts ({})", c.getSubjectX500Principal().getName(), KeyStores.getPathToKeystore(keyStore)); + LOG.debug("{} found in cacerts ({})", c.getSubjectX500Principal().getName(), KeyStores.getLocationToKeystore(keyStore)); return true; } // else continue } diff --git a/core/src/main/java/net/sourceforge/jnlp/security/KeyStores.java b/core/src/main/java/net/sourceforge/jnlp/security/KeyStores.java index 50f115ba4..fe86e2ca4 100644 --- a/core/src/main/java/net/sourceforge/jnlp/security/KeyStores.java +++ b/core/src/main/java/net/sourceforge/jnlp/security/KeyStores.java @@ -33,13 +33,6 @@ */ package net.sourceforge.jnlp.security; -import net.adoptopenjdk.icedteaweb.i18n.Translator; -import net.adoptopenjdk.icedteaweb.logging.Logger; -import net.adoptopenjdk.icedteaweb.logging.LoggerFactory; -import net.sourceforge.jnlp.config.InfrastructureFileDescriptor; -import net.sourceforge.jnlp.config.PathsAndFiles; -import net.sourceforge.jnlp.util.RestrictedFileUtils; - import java.io.File; import java.io.IOException; import java.security.AllPermission; @@ -53,6 +46,15 @@ import java.util.Map; import java.util.StringTokenizer; +import net.adoptopenjdk.icedteaweb.i18n.Translator; +import net.adoptopenjdk.icedteaweb.logging.Logger; +import net.adoptopenjdk.icedteaweb.logging.LoggerFactory; +import net.sourceforge.jnlp.config.InfrastructureFileDescriptor; +import net.sourceforge.jnlp.config.PathsAndFiles; +import net.sourceforge.jnlp.security.windows.WindowsKeyStoresManager; +import net.sourceforge.jnlp.security.windows.WindowsKeyStoresManager.WindowsKeyStoreType; +import net.sourceforge.jnlp.util.RestrictedFileUtils; + /** * The {@code KeyStores} class allows easily accessing the various KeyStores * used. @@ -61,7 +63,24 @@ public final class KeyStores { private static final Logger LOG = LoggerFactory.getLogger(KeyStores.class); - public static class KeyStoreWithPath { + public static class KeyStoreWrapContainer{ + private final T wrap; + + public KeyStoreWrapContainer(T t){ + this.wrap = t; + } + + public T getWrap() { + return wrap; + } + } + + public static interface KeyStoreWrap{ + public KeyStore getKs(); + public Family getFamily(); + } + + public static class KeyStoreWithPath implements KeyStoreWrap { private final KeyStore ks; private final String path; @@ -78,6 +97,40 @@ public KeyStore getKs() { public String getPath() { return path; } + + @Override + public Family getFamily() { + return Family.JKS; + } + } + + public static class KeyStoreWindows implements KeyStoreWrap { + + private final KeyStore ks; + private final WindowsKeyStoreType storeType; + + public KeyStoreWindows(KeyStore ks, WindowsKeyStoreType storeType) { + this.ks = ks; + this.storeType = storeType; + } + + public KeyStore getKs() { + return ks; + } + + public WindowsKeyStoreType getStoreType() { + return storeType; + } + + @Override + public Family getFamily() { + return Family.WINDOWS; + } + } + + public enum Family { + JKS, + WINDOWS } /* this gets turned into user-readable strings, see toUserReadableString */ @@ -94,39 +147,62 @@ public enum Type { CLIENT_CERTS, } - public static final Map keystoresPaths = new HashMap<>(); + public static final Map keystoresLocations = new HashMap<>(); private static final String KEYSTORE_TYPE = "JKS"; /** - * Returns a KeyStore corresponding to the appropriate level level (user or + * Returns a KeyStoreWrapContainer corresponding to the appropriate level level (user or * system) and type. * * @param level whether the KeyStore desired is a user-level or system-level KeyStore * @param type the type of KeyStore desired * @return a KeyStore containing certificates from the appropriate */ - public static KeyStoreWithPath getKeyStore(Level level, Type type) { + public static KeyStoreWrapContainer getWrapContainer(Level level, Type type) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new AllPermission()); } - final String location = getKeyStoreLocation(level, type).getFullPath(); + //The existence and accessibility of a Windows Key Store is checked if required by configuration + //Windows-ROOT --> "deployment.security.use.rootca.store.type.windowsRoot=true" + //Windows-My --> "deployment.security.use.rootca.store.type.windowsMy=true" + + final WindowsKeyStoresManager windowsStore = WindowsKeyStoresManager.getInfo(type); + final String location; + + KeyStoreWrapContainer keyStoreWrapContainer = null; + + if(windowsStore.isAccessible()) + location = windowsStore.getType().getStoreType(); + else + location = getKeyStoreLocation(level, type).getFullPath(); + KeyStore ks = null; try { - ks = createKeyStoreFromFile(new File(location), level == Level.USER); + if(windowsStore.isAccessible()) { + ks = createKeyStoreFromWindows(location); + keyStoreWrapContainer = new KeyStoreWrapContainer(new KeyStoreWindows(ks, windowsStore.getType())); + + } + else { + ks = createKeyStoreFromFile(new File(location), level == Level.USER); + keyStoreWrapContainer = new KeyStoreWrapContainer(new KeyStoreWithPath(ks, location)); + + } //hashcode is used instead of instance so when no references are left //to keystore, then this will not be blocker for garbage collection - keystoresPaths.put(ks.hashCode(), location); + keystoresLocations.put(ks.hashCode(), location); } catch (Exception e) { LOG.error("failed to get keystore " + level + " " + type + " -> " + location, e); } - return new KeyStoreWithPath(ks, location); + + return keyStoreWrapContainer; } - public static String getPathToKeystore(KeyStore k) { - final String s = keystoresPaths.get(k.hashCode()); + public static String getLocationToKeystore(KeyStore k) { + final String s = keystoresLocations.get(k.hashCode()); if (s == null) { return "unknown keystore location"; } @@ -141,24 +217,26 @@ public static String getPathToKeystore(KeyStore k) { */ public static List getCertKeyStores() { final List result = new ArrayList<>(10); - /* System-level JSSE certificates */ + KeyStore ks; - ks = getKeyStore(Level.SYSTEM, Type.JSSE_CERTS).getKs(); + + /* System-level JSSE certificates */ + ks = getWrapContainer(Level.SYSTEM, Type.JSSE_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } /* System-level certificates */ - ks = getKeyStore(Level.SYSTEM, Type.CERTS).getKs(); + ks = getWrapContainer(Level.SYSTEM, Type.CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } /* User-level JSSE certificates */ - ks = getKeyStore(Level.USER, Type.JSSE_CERTS).getKs(); + ks = getWrapContainer(Level.USER, Type.JSSE_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } /* User-level certificates */ - ks = getKeyStore(Level.USER, Type.CERTS).getKs(); + ks = getWrapContainer(Level.USER, Type.CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } @@ -173,24 +251,25 @@ public static List getCertKeyStores() { */ public static List getCAKeyStores() { List result = new ArrayList<>(10); - /* System-level JSSE CA certificates */ + KeyStore ks; - ks = getKeyStore(Level.SYSTEM, Type.JSSE_CA_CERTS).getKs(); + /* System-level JSSE CA certificates */ + ks = getWrapContainer(Level.SYSTEM, Type.JSSE_CA_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } /* System-level CA certificates */ - ks = getKeyStore(Level.SYSTEM, Type.CA_CERTS).getKs(); + ks = getWrapContainer(Level.SYSTEM, Type.CA_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } /* User-level JSSE CA certificates */ - ks = getKeyStore(Level.USER, Type.JSSE_CA_CERTS).getKs(); + ks = getWrapContainer(Level.USER, Type.JSSE_CA_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } /* User-level CA certificates */ - ks = getKeyStore(Level.USER, Type.CA_CERTS).getKs(); + ks = getWrapContainer(Level.USER, Type.CA_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } @@ -208,12 +287,12 @@ public static List getClientKeyStores() { List result = new ArrayList<>(); KeyStore ks; - ks = getKeyStore(Level.SYSTEM, Type.CLIENT_CERTS).getKs(); + ks = getWrapContainer(Level.SYSTEM, Type.CLIENT_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } - ks = getKeyStore(Level.USER, Type.CLIENT_CERTS).getKs(); + ks = getWrapContainer(Level.USER, Type.CLIENT_CERTS).getWrap().getKs(); if (ks != null) { result.add(ks); } @@ -342,4 +421,18 @@ private static KeyStore createKeyStoreFromFile(File file, boolean createIfNotFou } return ks; } + + + private static KeyStore createKeyStoreFromWindows(String windowsStoreType) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException { + final KeyStore ks = KeyStore.getInstance(windowsStoreType); + if (ks!=null) { + LOG.debug("Keystore windows {} exists.", windowsStoreType); + SecurityUtil.loadKeyStore(ks, null); + } else { + LOG.debug("Keystore windows {} does not exists.", windowsStoreType); + SecurityUtil.loadKeyStore(ks, null); + } + return ks; + } + } diff --git a/core/src/main/java/net/sourceforge/jnlp/security/SecurityUtil.java b/core/src/main/java/net/sourceforge/jnlp/security/SecurityUtil.java index 257856d74..2f6167eed 100644 --- a/core/src/main/java/net/sourceforge/jnlp/security/SecurityUtil.java +++ b/core/src/main/java/net/sourceforge/jnlp/security/SecurityUtil.java @@ -34,14 +34,10 @@ package net.sourceforge.jnlp.security; -import net.adoptopenjdk.icedteaweb.IcedTeaWebConstants; -import net.adoptopenjdk.icedteaweb.JavaSystemProperties; -import net.adoptopenjdk.icedteaweb.logging.Logger; -import net.adoptopenjdk.icedteaweb.logging.LoggerFactory; -import net.sourceforge.jnlp.security.KeyStores.Level; -import net.sourceforge.jnlp.security.KeyStores.Type; +import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SSL_TRUST_STORE; +import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SSL_TRUST_STORE_PASSWORD; +import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SSL_TRUST_STORE_TYPE; -import javax.net.ssl.KeyManagerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -54,9 +50,15 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; -import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SSL_TRUST_STORE; -import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SSL_TRUST_STORE_PASSWORD; -import static net.adoptopenjdk.icedteaweb.JavaSystemPropertiesConstants.SSL_TRUST_STORE_TYPE; +import javax.net.ssl.KeyManagerFactory; + +import net.adoptopenjdk.icedteaweb.IcedTeaWebConstants; +import net.adoptopenjdk.icedteaweb.JavaSystemProperties; +import net.adoptopenjdk.icedteaweb.logging.Logger; +import net.adoptopenjdk.icedteaweb.logging.LoggerFactory; +import net.sourceforge.jnlp.security.KeyStores.Level; +import net.sourceforge.jnlp.security.KeyStores.Type; +import net.sourceforge.jnlp.security.windows.WindowsKeyStoresManager; public class SecurityUtil { @@ -353,8 +355,14 @@ Key operateKeystore(char[] pass) throws KeyStoreException, NoSuchAlgorithmExcept } public static void loadKeyStore(KeyStore ks, File f) throws IOException, NoSuchAlgorithmException, CertificateException { - try { - LOG.debug("Loading Keystore {}", f != null ? f.toString() : "Unknown"); + try { + if(WindowsKeyStoresManager.isWindowsStore(ks)) { + LOG.debug("Loading Keystore {}", ks.getType()); + } + else { + LOG.debug("Loading Keystore {}", f != null ? f.toString() : "Unknown"); + } + KeystorePasswordAttempter.INSTANCE.unlockKeystore( new KeystorePasswordAttempter.KeystoreOperation(ks, f) { diff --git a/core/src/main/java/net/sourceforge/jnlp/security/windows/WindowsKeyStoresManager.java b/core/src/main/java/net/sourceforge/jnlp/security/windows/WindowsKeyStoresManager.java new file mode 100644 index 000000000..3453fd188 --- /dev/null +++ b/core/src/main/java/net/sourceforge/jnlp/security/windows/WindowsKeyStoresManager.java @@ -0,0 +1,127 @@ +package net.sourceforge.jnlp.security.windows; + +import java.security.KeyStore; +import java.util.ArrayList; + +import net.sourceforge.jnlp.config.ConfigurationConstants; +import net.sourceforge.jnlp.config.DeploymentConfiguration; +import net.sourceforge.jnlp.runtime.JNLPRuntime; +import net.sourceforge.jnlp.security.KeyStores; + +public class WindowsKeyStoresManager { + + private boolean accessible; + private WindowsKeyStoreType type; + + private static final ArrayList ADMITTED_STORE_TYPES; + private static final String KS_PROVIDER_NAME = "SunMSCAPI"; + + private static final String OS_NAME = System.getProperty("os.name") == null ? "" : System.getProperty("os.name"); + + private static final WindowsKeyStoresManager INSTANCE; + private static final WindowsKeyStoresManager EMPTY_INSTANCE = new WindowsKeyStoresManager(); + + static { + //Admitted Stores - Only Root CA admitted + ADMITTED_STORE_TYPES = new ArrayList(); + ADMITTED_STORE_TYPES.add(KeyStores.Type.CA_CERTS); + + boolean isWindowsMachine = OS_NAME.indexOf("Windows") >= 0; + if(isWindowsMachine) { + //Load configurations. Only for Windows OS machines. + WindowsKeyStoresManager windowsStoresManager = new WindowsKeyStoresManager(); + + DeploymentConfiguration config = JNLPRuntime.getConfiguration(); + + boolean windowsRoot = Boolean.parseBoolean(config.getProperty(ConfigurationConstants.KEY_SECURITY_USE_ROOTCA_STORE_TYPE_WINDOWS_ROOT)); + boolean windowsMy = Boolean.parseBoolean(config.getProperty(ConfigurationConstants.KEY_SECURITY_USE_ROOTCA_STORE_TYPE_WINDOWS_MY)); + + windowsStoresManager.accessible = windowsRoot || windowsMy; + + if(windowsRoot && windowsMy) { + windowsStoresManager.type = WindowsKeyStoreType.WINDOWS_ROOT; + } + else if(!windowsRoot && windowsMy) { + windowsStoresManager.type = WindowsKeyStoreType.WINDOWS_MY; + } + else if(windowsRoot && !windowsMy) { + windowsStoresManager.type = WindowsKeyStoreType.WINDOWS_ROOT; + } + else { + windowsStoresManager.type = null; + } + + INSTANCE = windowsStoresManager; + } + else { + INSTANCE = EMPTY_INSTANCE; + } + + + } + + private WindowsKeyStoresManager () { + + } + + public enum WindowsKeyStoreType { + WINDOWS_ROOT("Windows-ROOT","Windows Trusted Root Certification Authorities"), + WINDOWS_MY("Windows-MY","Windows Personal"); + + private String type; + private String description; + + WindowsKeyStoreType(String storeType, String storeDescription){ + this.type = storeType; + this.description = storeDescription; + } + + public String getStoreType() { + return type; + } + + + public String getDescription() { + return description; + } + + public static WindowsKeyStoreType getValue(String storeType) { + for (WindowsKeyStoreType st : WindowsKeyStoreType.values()) { + if (st.getStoreType().equals(storeType)) { + return st; + } + } + + return null; + } + } + + public boolean isAccessible() { + return accessible; + } + + public WindowsKeyStoreType getType() { + return type; + } + + public static WindowsKeyStoresManager getInfo(KeyStores.Type storeType) { + + boolean admitted = ADMITTED_STORE_TYPES.contains(storeType); + + if(!admitted) + return EMPTY_INSTANCE; + else + return INSTANCE; + + + } + + public static boolean isWindowsStore(KeyStore ks) { + + if(ks!= null && ks.getProvider() != null) + return KS_PROVIDER_NAME.equals(ks.getProvider().getName()); + else + return false; + + } +} \ No newline at end of file diff --git a/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java b/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java index 2f4475fda..d343b0cd1 100644 --- a/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java +++ b/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java @@ -33,13 +33,15 @@ */ package net.sourceforge.jnlp.security; -import net.sourceforge.jnlp.config.InfrastructureFileDescriptor; -import net.sourceforge.jnlp.config.PathsAndFiles; +import java.security.Permission; + import org.junit.AfterClass; import org.junit.Assert; import org.junit.Test; -import java.security.Permission; +import net.sourceforge.jnlp.config.InfrastructureFileDescriptor; +import net.sourceforge.jnlp.config.PathsAndFiles; +import net.sourceforge.jnlp.security.KeyStores.KeyStoreWrap; public class KeyStoresTest { @@ -124,6 +126,20 @@ public void getKeyStoreSystemLocationTestSM() { s = KeyStores.getKeyStoreLocation(KeyStores.Level.SYSTEM, KeyStores.Type.JSSE_CERTS); Assert.assertEquals(s.getFile(), PathsAndFiles.SYS_JSSECERT.getFile()); Assert.assertEquals(true, dm.called); - } + } + + @Test + public void getKeyStoreWindowsRootTestSM() { + DummySM dm = new DummySM(); + System.setSecurityManager(dm); + + KeyStoreWrap keyStoreWrap; + + keyStoreWrap = KeyStores.getWrapContainer(KeyStores.Level.SYSTEM, KeyStores.Type.CA_CERTS).getWrap(); + Assert.assertEquals(keyStoreWrap.getFamily(), KeyStores.Family.WINDOWS); + + keyStoreWrap = KeyStores.getWrapContainer(KeyStores.Level.USER, KeyStores.Type.CA_CERTS).getWrap(); + Assert.assertEquals(keyStoreWrap.getFamily(), KeyStores.Family.WINDOWS); + } } From f0b4cbf80ee1911b1280a7ad6e95ac770d2f0ec6 Mon Sep 17 00:00:00 2001 From: csottile <42802710+csottile@users.noreply.github.com> Date: Wed, 10 Nov 2021 12:04:18 +0100 Subject: [PATCH 2/2] KeyStoresTest - Fix test precondition Test needs to check preconditions. Windows Store may be accessible. --- .../sourceforge/jnlp/security/KeyStoresTest.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java b/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java index d343b0cd1..23605e6a0 100644 --- a/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java +++ b/core/src/test/java/net/sourceforge/jnlp/security/KeyStoresTest.java @@ -42,6 +42,7 @@ import net.sourceforge.jnlp.config.InfrastructureFileDescriptor; import net.sourceforge.jnlp.config.PathsAndFiles; import net.sourceforge.jnlp.security.KeyStores.KeyStoreWrap; +import net.sourceforge.jnlp.security.windows.WindowsKeyStoresManager; public class KeyStoresTest { @@ -135,11 +136,16 @@ public void getKeyStoreWindowsRootTestSM() { KeyStoreWrap keyStoreWrap; - keyStoreWrap = KeyStores.getWrapContainer(KeyStores.Level.SYSTEM, KeyStores.Type.CA_CERTS).getWrap(); - Assert.assertEquals(keyStoreWrap.getFamily(), KeyStores.Family.WINDOWS); + final WindowsKeyStoresManager windowsStore = WindowsKeyStoresManager.getInfo(KeyStores.Type.CA_CERTS); - keyStoreWrap = KeyStores.getWrapContainer(KeyStores.Level.USER, KeyStores.Type.CA_CERTS).getWrap(); - Assert.assertEquals(keyStoreWrap.getFamily(), KeyStores.Family.WINDOWS); + if(windowsStore.isAccessible()) { + + keyStoreWrap = KeyStores.getWrapContainer(KeyStores.Level.SYSTEM, KeyStores.Type.CA_CERTS).getWrap(); + Assert.assertEquals(keyStoreWrap.getFamily(), KeyStores.Family.WINDOWS); + + keyStoreWrap = KeyStores.getWrapContainer(KeyStores.Level.USER, KeyStores.Type.CA_CERTS).getWrap(); + Assert.assertEquals(keyStoreWrap.getFamily(), KeyStores.Family.WINDOWS); + } } }