Ticket #10033: remove_https_remote_control2.diff

File remove_https_remote_control2.diff, 57.7 KB (added by Don-vip, 6 years ago)
  • build.xml

     
    177177                <attribute name="Codebase" value="josm.openstreetmap.de"/>
    178178                <attribute name="Application-Name" value="JOSM - Java OpenStreetMap Editor"/>
    179179                <!-- Java 9 stuff. Entries are safely ignored by Java 8 -->
    180                 <attribute name="Add-Exports" value="java.base/sun.security.util java.base/sun.security.x509 java.desktop/com.apple.eawt java.desktop/com.sun.imageio.spi javafx.graphics/com.sun.javafx.application jdk.deploy/com.sun.deploy.config" />
     180                <attribute name="Add-Exports" value="java.desktop/com.apple.eawt java.desktop/com.sun.imageio.spi javafx.graphics/com.sun.javafx.application jdk.deploy/com.sun.deploy.config" />
    181181                <attribute name="Add-Opens" value="java.base/java.lang java.base/java.nio java.base/jdk.internal.loader java.base/jdk.internal.ref java.desktop/javax.imageio.spi java.desktop/javax.swing.text.html java.prefs/java.util.prefs" />
    182182            </manifest>
    183183            <service type="java.text.spi.DecimalFormatSymbolsProvider" provider="org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider" />
     
    399399            <bottom><![CDATA[<a href="https://josm.openstreetmap.de/">JOSM</a>]]></bottom>
    400400            <arg value="-html5" if:set="isJava9" />
    401401            <arg value="--add-exports" if:set="isJava9" />
    402             <arg value="java.base/sun.security.util=ALL-UNNAMED" if:set="isJava9" />
    403             <arg value="--add-exports" if:set="isJava9" />
    404             <arg value="java.base/sun.security.x509=ALL-UNNAMED" if:set="isJava9" />
    405             <arg value="--add-exports" if:set="isJava9" />
    406402            <arg value="javafx.graphics/com.sun.javafx.application=ALL-UNNAMED" if:set="isJava9" />
    407403        </javadoc>
    408404    </target>
     
    493489                    <jvmarg value="--add-modules" if:set="isJava9" unless:set="isJava11" />
    494490                    <jvmarg value="java.activation,java.se.ee" if:set="isJava9" unless:set="isJava11" />
    495491                    <jvmarg value="--add-exports" if:set="isJava9" />
    496                     <jvmarg value="java.base/sun.security.util=ALL-UNNAMED" if:set="isJava9" />
    497                     <jvmarg value="--add-exports" if:set="isJava9" />
    498                     <jvmarg value="java.base/sun.security.x509=ALL-UNNAMED" if:set="isJava9" />
    499                     <jvmarg value="--add-exports" if:set="isJava9" />
    500492                    <jvmarg value="javafx.graphics/com.sun.javafx.application=ALL-UNNAMED" if:set="isJava9" />
    501493                    <jvmarg value="--add-exports" if:set="isJava9" />
    502494                    <jvmarg value="jdk.deploy/com.sun.deploy.config=ALL-UNNAMED" if:set="isJava9" />
  • josm-latest.jnlp

     
    1919        <all-permissions/>
    2020    </security>
    2121    <resources>
    22         <java version="1.8+" java-vm-args="--add-modules=java.activation,java.se.ee --add-exports=java.base/sun.security.util=ALL-UNNAMED --add-exports=java.base/sun.security.x509=ALL-UNNAMED --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports=jdk.deploy/com.sun.deploy.config=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"/>
     22        <java version="1.8+" java-vm-args="--add-modules=java.activation,java.se.ee --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports=jdk.deploy/com.sun.deploy.config=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"/>
    2323        <jar href="josm-latest.jar"/>
    2424        <property name="java.util.Arrays.useLegacyMergeSort" value="true"/>
    2525    </resources>
  • josm.jnlp

     
    1919        <all-permissions/>
    2020    </security>
    2121    <resources>
    22         <java version="1.8+" java-vm-args="--add-modules=java.activation,java.se.ee --add-exports=java.base/sun.security.util=ALL-UNNAMED --add-exports=java.base/sun.security.x509=ALL-UNNAMED --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports=jdk.deploy/com.sun.deploy.config=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"/>
     22        <java version="1.8+" java-vm-args="--add-modules=java.activation,java.se.ee --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports=jdk.deploy/com.sun.deploy.config=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"/>
    2323        <jar href="josm-tested.jar"/>
    2424        <property name="java.util.Arrays.useLegacyMergeSort" value="true"/>
    2525    </resources>
  • src/org/openstreetmap/josm/data/Preferences.java

     
    7878public class Preferences extends AbstractPreferences {
    7979
    8080    private static final String[] OBSOLETE_PREF_KEYS = {
     81        "remotecontrol.https.enabled", /* remove entry after Dec. 2018 */
     82        "remotecontrol.https.port", /* remove entry after Dec. 2018 */
    8183    };
    8284
    8385    private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50);
  • src/org/openstreetmap/josm/gui/MainApplication.java

     
    2727import java.security.AllPermission;
    2828import java.security.CodeSource;
    2929import java.security.GeneralSecurityException;
    30 import java.security.KeyStoreException;
    31 import java.security.NoSuchAlgorithmException;
    3230import java.security.PermissionCollection;
    3331import java.security.Permissions;
    3432import java.security.Policy;
    35 import java.security.cert.CertificateException;
    3633import java.util.ArrayList;
    3734import java.util.Arrays;
    3835import java.util.Collection;
     
    10801077
    10811078        SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
    10821079
    1083         if (Main.isPlatformWindows()) {
    1084             try {
    1085                 // Check for insecure certificates to remove.
    1086                 // This is Windows-dependant code but it can't go to preStartupHook (need i18n)
    1087                 // neither startupHook (need to be called before remote control)
    1088                 PlatformHookWindows.removeInsecureCertificates();
    1089             } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
    1090                 Logging.error(e);
    1091             }
    1092         }
    1093 
    10941080        if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
    10951081            RemoteControl.start();
    10961082        }
  • src/org/openstreetmap/josm/gui/preferences/remotecontrol/RemoteControlPreference.java

     
    77import java.awt.Font;
    88import java.awt.GridBagLayout;
    99import java.awt.event.ActionListener;
    10 import java.io.IOException;
    11 import java.security.GeneralSecurityException;
    12 import java.security.KeyStore;
    13 import java.security.KeyStoreException;
    14 import java.security.NoSuchAlgorithmException;
    15 import java.security.cert.CertificateException;
    1610import java.util.LinkedHashMap;
    1711import java.util.Map;
    1812import java.util.Map.Entry;
    1913
    2014import javax.swing.BorderFactory;
    2115import javax.swing.Box;
    22 import javax.swing.JButton;
    2316import javax.swing.JCheckBox;
    2417import javax.swing.JLabel;
    25 import javax.swing.JOptionPane;
    2618import javax.swing.JPanel;
    2719import javax.swing.JSeparator;
    2820
    29 import org.openstreetmap.josm.Main;
    3021import org.openstreetmap.josm.gui.help.HelpUtil;
    3122import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
    3223import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
     
    3627import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
    3728import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
    3829import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
    39 import org.openstreetmap.josm.io.remotecontrol.RemoteControlHttpsServer;
    4030import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler;
    4131import org.openstreetmap.josm.spi.preferences.Config;
    4232import org.openstreetmap.josm.tools.GBC;
    43 import org.openstreetmap.josm.tools.Logging;
    44 import org.openstreetmap.josm.tools.PlatformHookWindows;
    4533
    4634/**
    4735 * Preference settings for Remote Control.
     
    7260
    7361    private final Map<PermissionPrefWithDefault, JCheckBox> prefs = new LinkedHashMap<>();
    7462    private JCheckBox enableRemoteControl;
    75     private JCheckBox enableHttpsSupport;
    76 
    77     private JButton installCertificate;
    78     private JButton uninstallCertificate;
    7963
    8064    private final JCheckBox loadInNewLayer = new JCheckBox(tr("Download as new layer"));
    8165    private final JCheckBox alwaysAskUserConfirm = new JCheckBox(tr("Confirm all Remote Control actions manually"));
     
    9276        remote.add(descLabel, GBC.eol().insets(5, 5, 0, 10).fill(GBC.HORIZONTAL));
    9377
    9478        final JLabel portLabel = new JLabel("<html>"
    95                 + tr("JOSM will always listen at <b>port {0}</b> (http) and <b>port {1}</b> (https) on localhost."
    96                 + "<br>These ports are not configurable because they are referenced by external applications talking to JOSM.",
    97                 Config.getPref().get("remote.control.port", "8111"),
    98                 Config.getPref().get("remote.control.https.port", "8112")) + "</html>");
     79                + tr("JOSM will always listen at <b>port {0}</b> (http) on localhost."
     80                + "<br>This port is not configurable because it is referenced by external applications talking to JOSM.",
     81                Config.getPref().get("remote.control.port", "8111")) + "</html>");
    9982        portLabel.setFont(portLabel.getFont().deriveFont(Font.PLAIN));
    10083        remote.add(portLabel, GBC.eol().insets(5, 5, 0, 10).fill(GBC.HORIZONTAL));
    10184
     
    10790
    10891        remote.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 5));
    10992
    110         boolean https = RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.get();
    111 
    112         enableHttpsSupport = new JCheckBox(tr("Enable HTTPS support"), https);
    113         wrapper.add(enableHttpsSupport, GBC.eol().fill(GBC.HORIZONTAL));
    114 
    115         // Certificate installation only available on Windows for now, see #10033
    116         if (Main.isPlatformWindows()) {
    117             installCertificate = new JButton(tr("Install..."));
    118             uninstallCertificate = new JButton(tr("Uninstall..."));
    119             installCertificate.setToolTipText(tr("Install JOSM localhost certificate to system/browser root keystores"));
    120             uninstallCertificate.setToolTipText(tr("Uninstall JOSM localhost certificate from system/browser root keystores"));
    121             wrapper.add(new JLabel(tr("Certificate:")), GBC.std().insets(15, 5, 0, 0));
    122             wrapper.add(installCertificate, GBC.std().insets(5, 5, 0, 0));
    123             wrapper.add(uninstallCertificate, GBC.eol().insets(5, 5, 0, 0));
    124             enableHttpsSupport.addActionListener(e -> installCertificate.setEnabled(enableHttpsSupport.isSelected()));
    125             installCertificate.addActionListener(e -> {
    126                 try {
    127                     boolean changed = RemoteControlHttpsServer.setupPlatform(
    128                             RemoteControlHttpsServer.loadJosmKeystore());
    129                     String msg = changed ?
    130                             tr("Certificate has been successfully installed.") :
    131                             tr("Certificate is already installed. Nothing to do.");
    132                     Logging.info(msg);
    133                     JOptionPane.showMessageDialog(wrapper, msg);
    134                 } catch (IOException | GeneralSecurityException ex) {
    135                     Logging.error(ex);
    136                 }
    137             });
    138             uninstallCertificate.addActionListener(e -> {
    139                 try {
    140                     String msg;
    141                     KeyStore ks = PlatformHookWindows.getRootKeystore();
    142                     if (ks.containsAlias(RemoteControlHttpsServer.ENTRY_ALIAS)) {
    143                         Logging.info(tr("Removing certificate {0} from root keystore.", RemoteControlHttpsServer.ENTRY_ALIAS));
    144                         ks.deleteEntry(RemoteControlHttpsServer.ENTRY_ALIAS);
    145                         msg = tr("Certificate has been successfully uninstalled.");
    146                     } else {
    147                         msg = tr("Certificate is not installed. Nothing to do.");
    148                     }
    149                     Logging.info(msg);
    150                     JOptionPane.showMessageDialog(wrapper, msg);
    151                 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException ex) {
    152                     Logging.error(ex);
    153                 }
    154             });
    155             installCertificate.setEnabled(https);
    156         }
    157 
    15893        wrapper.add(new JSeparator(), GBC.eop().fill(GBC.HORIZONTAL).insets(15, 5, 15, 5));
    15994
    16095        wrapper.add(new JLabel(tr("Permitted actions:")), GBC.eol().insets(5, 0, 0, 0));
     
    175110
    176111        ActionListener remoteControlEnabled = e -> {
    177112            GuiHelper.setEnabledRec(wrapper, enableRemoteControl.isSelected());
    178             enableHttpsSupport.setEnabled(RemoteControl.supportsHttps());
    179             // 'setEnabled(false)' does not work for JLabel with html text, so do it manually
    180             // FIXME: use QuadStateCheckBox to make checkboxes unset when disabled
    181             if (installCertificate != null && uninstallCertificate != null) {
    182                 // Install certificate button is enabled if HTTPS is also enabled
    183                 installCertificate.setEnabled(enableRemoteControl.isSelected()
    184                         && enableHttpsSupport.isSelected() && RemoteControl.supportsHttps());
    185                 // Uninstall certificate button is always enabled
    186                 uninstallCertificate.setEnabled(RemoteControl.supportsHttps());
    187             }
    188113        };
    189114        enableRemoteControl.addActionListener(remoteControlEnabled);
    190115        remoteControlEnabled.actionPerformed(null);
     
    194119    @Override
    195120    public boolean ok() {
    196121        boolean enabled = enableRemoteControl.isSelected();
    197         boolean httpsEnabled = enableHttpsSupport.isSelected();
    198122        boolean changed = RemoteControl.PROP_REMOTECONTROL_ENABLED.put(enabled);
    199         boolean httpsChanged = RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.put(httpsEnabled);
    200123        if (enabled) {
    201124            for (Entry<PermissionPrefWithDefault, JCheckBox> p : prefs.entrySet()) {
    202125                Config.getPref().putBoolean(p.getKey().pref, p.getValue().isSelected());
     
    210133            } else {
    211134                RemoteControl.stop();
    212135            }
    213         } else if (httpsChanged) {
    214             if (httpsEnabled) {
    215                 RemoteControlHttpsServer.restartRemoteControlHttpsServer();
    216             } else {
    217                 RemoteControlHttpsServer.stopRemoteControlHttpsServer();
    218             }
    219136        }
    220137        return false;
    221138    }
  • src/org/openstreetmap/josm/io/remotecontrol/RemoteControl.java

     
    1010import org.openstreetmap.josm.data.preferences.BooleanProperty;
    1111import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler;
    1212import org.openstreetmap.josm.spi.preferences.Config;
    13 import org.openstreetmap.josm.tools.Logging;
    1413
    1514/**
    1615 * Manager class for remote control operations.
     
    2726    public static final BooleanProperty PROP_REMOTECONTROL_ENABLED = new BooleanProperty("remotecontrol.enabled", false);
    2827
    2928    /**
    30      * If the remote control feature is enabled or disabled for HTTPS. If disabled,
    31      * only HTTP access will be available.
    32      * @since 7335
    33      */
    34     public static final BooleanProperty PROP_REMOTECONTROL_HTTPS_ENABLED = new BooleanProperty(
    35             "remotecontrol.https.enabled", false);
    36 
    37     /**
    3829     * RemoteControl HTTP protocol version. Change minor number for compatible
    3930     * interface extensions. Change major number in case of incompatible
    4031     * changes.
     
    4738     */
    4839    public static void start() {
    4940        RemoteControlHttpServer.restartRemoteControlHttpServer();
    50         if (supportsHttps()) {
    51             RemoteControlHttpsServer.restartRemoteControlHttpsServer();
    52         }
    5341    }
    5442
    5543    /**
     
    5846     */
    5947    public static void stop() {
    6048        RemoteControlHttpServer.stopRemoteControlHttpServer();
    61         if (supportsHttps()) {
    62             RemoteControlHttpsServer.stopRemoteControlHttpsServer();
    63         }
    64     }
    65 
    66     /**
    67      * Determines if the current JVM support HTTPS remote control.
    68      * @return {@code true} if the JVM provides {@code sun.security.x509} classes
    69      * @since 12703
    70      */
    71     public static boolean supportsHttps() {
    72         try {
    73             return Class.forName("sun.security.x509.GeneralName") != null;
    74         } catch (ClassNotFoundException | SecurityException e) {
    75             Logging.trace(e);
    76             return false;
    77         }
    7849    }
    7950
    8051    /**
  • src/org/openstreetmap/josm/io/remotecontrol/RemoteControlHttpsServer.java

     
    1 // License: GPL. For details, see LICENSE file.
    2 package org.openstreetmap.josm.io.remotecontrol;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.marktr;
    5 
    6 import java.io.IOException;
    7 import java.io.InputStream;
    8 import java.io.OutputStream;
    9 import java.math.BigInteger;
    10 import java.net.ServerSocket;
    11 import java.net.Socket;
    12 import java.net.SocketException;
    13 import java.nio.file.Files;
    14 import java.nio.file.Path;
    15 import java.nio.file.Paths;
    16 import java.nio.file.StandardOpenOption;
    17 import java.security.GeneralSecurityException;
    18 import java.security.KeyPair;
    19 import java.security.KeyPairGenerator;
    20 import java.security.KeyStore;
    21 import java.security.KeyStoreException;
    22 import java.security.NoSuchAlgorithmException;
    23 import java.security.PrivateKey;
    24 import java.security.SecureRandom;
    25 import java.security.cert.Certificate;
    26 import java.security.cert.CertificateException;
    27 import java.security.cert.X509Certificate;
    28 import java.util.Arrays;
    29 import java.util.Date;
    30 import java.util.Enumeration;
    31 import java.util.Locale;
    32 import java.util.Vector;
    33 
    34 import javax.net.ssl.KeyManagerFactory;
    35 import javax.net.ssl.SSLContext;
    36 import javax.net.ssl.SSLServerSocket;
    37 import javax.net.ssl.SSLServerSocketFactory;
    38 import javax.net.ssl.SSLSocket;
    39 import javax.net.ssl.TrustManagerFactory;
    40 
    41 import org.openstreetmap.josm.Main;
    42 import org.openstreetmap.josm.data.preferences.StringProperty;
    43 import org.openstreetmap.josm.spi.preferences.Config;
    44 import org.openstreetmap.josm.tools.Logging;
    45 
    46 import sun.security.util.ObjectIdentifier;
    47 import sun.security.x509.AlgorithmId;
    48 import sun.security.x509.BasicConstraintsExtension;
    49 import sun.security.x509.CertificateAlgorithmId;
    50 import sun.security.x509.CertificateExtensions;
    51 import sun.security.x509.CertificateSerialNumber;
    52 import sun.security.x509.CertificateValidity;
    53 import sun.security.x509.CertificateVersion;
    54 import sun.security.x509.CertificateX509Key;
    55 import sun.security.x509.DNSName;
    56 import sun.security.x509.ExtendedKeyUsageExtension;
    57 import sun.security.x509.GeneralName;
    58 import sun.security.x509.GeneralNameInterface;
    59 import sun.security.x509.GeneralNames;
    60 import sun.security.x509.IPAddressName;
    61 import sun.security.x509.OIDName;
    62 import sun.security.x509.SubjectAlternativeNameExtension;
    63 import sun.security.x509.URIName;
    64 import sun.security.x509.X500Name;
    65 import sun.security.x509.X509CertImpl;
    66 import sun.security.x509.X509CertInfo;
    67 
    68 /**
    69  * Simple HTTPS server that spawns a {@link RequestProcessor} for every secure connection.
    70  *
    71  * @since 6941
    72  */
    73 public class RemoteControlHttpsServer extends Thread {
    74 
    75     /** The server socket */
    76     private final ServerSocket server;
    77 
    78     /** The server instance for IPv4 */
    79     private static volatile RemoteControlHttpsServer instance4;
    80     /** The server instance for IPv6 */
    81     private static volatile RemoteControlHttpsServer instance6;
    82 
    83     /** SSL context information for connections */
    84     private SSLContext sslContext;
    85 
    86     /* the default port for HTTPS remote control */
    87     private static final int HTTPS_PORT = 8112;
    88 
    89     /**
    90      * JOSM keystore file name.
    91      * @since 7337
    92      */
    93     public static final String KEYSTORE_FILENAME = "josm.keystore";
    94 
    95     /**
    96      * Preference for keystore password (automatically generated by JOSM).
    97      * @since 7335
    98      */
    99     public static final StringProperty KEYSTORE_PASSWORD = new StringProperty("remotecontrol.https.keystore.password", "");
    100 
    101     /**
    102      * Preference for certificate password (automatically generated by JOSM).
    103      * @since 7335
    104      */
    105     public static final StringProperty KEYENTRY_PASSWORD = new StringProperty("remotecontrol.https.keyentry.password", "");
    106 
    107     /**
    108      * Unique alias used to store JOSM localhost entry, both in JOSM keystore and system/browser keystores.
    109      * @since 7343
    110      */
    111     public static final String ENTRY_ALIAS = "josm_localhost";
    112 
    113     /**
    114      * Creates a GeneralNameInterface object from known types.
    115      * @param t one of 4 known types
    116      * @param v value
    117      * @return which one
    118      * @throws IOException if any I/O error occurs
    119      */
    120     private static GeneralNameInterface createGeneralNameInterface(String t, String v) throws IOException {
    121         switch (t.toLowerCase(Locale.ENGLISH)) {
    122             case "uri": return new URIName(v);
    123             case "dns": return new DNSName(v);
    124             case "ip": return new IPAddressName(v);
    125             default: return new OIDName(v);
    126         }
    127     }
    128 
    129     /**
    130      * Create a self-signed X.509 Certificate.
    131      * @param dn the X.509 Distinguished Name, eg "CN=localhost, OU=JOSM, O=OpenStreetMap"
    132      * @param pair the KeyPair
    133      * @param days how many days from now the Certificate is valid for
    134      * @param algorithm the signing algorithm, eg "SHA256withRSA"
    135      * @param san SubjectAlternativeName extension (optional)
    136      * @return the self-signed X.509 Certificate
    137      * @throws GeneralSecurityException if any security error occurs
    138      * @throws IOException if any I/O error occurs
    139      */
    140     private static X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm, String san)
    141             throws GeneralSecurityException, IOException {
    142         X509CertInfo info = new X509CertInfo();
    143         Date from = new Date();
    144         Date to = new Date(from.getTime() + days * 86_400_000L);
    145         CertificateValidity interval = new CertificateValidity(from, to);
    146         BigInteger sn = new BigInteger(64, new SecureRandom());
    147         X500Name owner = new X500Name(dn);
    148 
    149         info.set(X509CertInfo.VALIDITY, interval);
    150         info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
    151         info.set(X509CertInfo.SUBJECT, owner);
    152         info.set(X509CertInfo.ISSUER, owner);
    153 
    154         info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
    155         info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
    156         AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
    157         info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
    158 
    159         CertificateExtensions ext = new CertificateExtensions();
    160         // Critical: Not CA, max path len 0
    161         ext.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(Boolean.TRUE, false, 0));
    162         // Critical: only allow TLS ("serverAuth" = 1.3.6.1.5.5.7.3.1)
    163         ext.set(ExtendedKeyUsageExtension.NAME, new ExtendedKeyUsageExtension(Boolean.TRUE,
    164                 new Vector<>(Arrays.asList(new ObjectIdentifier("1.3.6.1.5.5.7.3.1")))));
    165 
    166         if (san != null) {
    167             int colonpos;
    168             String[] ps = san.split(",");
    169             GeneralNames gnames = new GeneralNames();
    170             for (String item: ps) {
    171                 colonpos = item.indexOf(':');
    172                 if (colonpos < 0) {
    173                     throw new IllegalArgumentException("Illegal item " + item + " in " + san);
    174                 }
    175                 String t = item.substring(0, colonpos);
    176                 String v = item.substring(colonpos+1);
    177                 gnames.add(new GeneralName(createGeneralNameInterface(t, v)));
    178             }
    179             // Non critical
    180             ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(Boolean.FALSE, gnames));
    181         }
    182 
    183         info.set(X509CertInfo.EXTENSIONS, ext);
    184 
    185         // Sign the cert to identify the algorithm that's used.
    186         PrivateKey privkey = pair.getPrivate();
    187         X509CertImpl cert = new X509CertImpl(info);
    188         cert.sign(privkey, algorithm);
    189 
    190         // Update the algorithm, and resign.
    191         algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG);
    192         info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
    193         cert = new X509CertImpl(info);
    194         cert.sign(privkey, algorithm);
    195         return cert;
    196     }
    197 
    198     /**
    199      * Setup the JOSM internal keystore, used to store HTTPS certificate and private key.
    200      * @return Path to the (initialized) JOSM keystore
    201      * @throws IOException if an I/O error occurs
    202      * @throws GeneralSecurityException if a security error occurs
    203      * @since 7343
    204      */
    205     public static Path setupJosmKeystore() throws IOException, GeneralSecurityException {
    206 
    207         Path dir = Paths.get(RemoteControl.getRemoteControlDir());
    208         Path path = dir.resolve(KEYSTORE_FILENAME);
    209         Files.createDirectories(dir);
    210 
    211         if (!path.toFile().exists()) {
    212             Logging.debug("No keystore found, creating a new one");
    213 
    214             // Create new keystore like previous one generated with JDK keytool as follows:
    215             // keytool -genkeypair -storepass josm_ssl -keypass josm_ssl -alias josm_localhost -dname "CN=localhost, OU=JOSM, O=OpenStreetMap"
    216             // -ext san=ip:127.0.0.1 -keyalg RSA -validity 1825
    217 
    218             KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    219             generator.initialize(2048);
    220             KeyPair pair = generator.generateKeyPair();
    221 
    222             X509Certificate cert = generateCertificate("CN=localhost, OU=JOSM, O=OpenStreetMap", pair, 1825, "SHA256withRSA",
    223                     "dns:localhost,ip:127.0.0.1,ip:::1,uri:https://127.0.0.1:"+HTTPS_PORT+",uri:https://::1:"+HTTPS_PORT);
    224 
    225             KeyStore ks = KeyStore.getInstance("JKS");
    226             ks.load(null, null);
    227 
    228             // Generate new passwords. See https://stackoverflow.com/a/41156/2257172
    229             SecureRandom random = new SecureRandom();
    230             KEYSTORE_PASSWORD.put(new BigInteger(130, random).toString(32));
    231             KEYENTRY_PASSWORD.put(new BigInteger(130, random).toString(32));
    232 
    233             char[] storePassword = KEYSTORE_PASSWORD.get().toCharArray();
    234             char[] entryPassword = KEYENTRY_PASSWORD.get().toCharArray();
    235 
    236             ks.setKeyEntry(ENTRY_ALIAS, pair.getPrivate(), entryPassword, new Certificate[]{cert});
    237             try (OutputStream out = Files.newOutputStream(path, StandardOpenOption.CREATE)) {
    238                 ks.store(out, storePassword);
    239             }
    240         }
    241         return path;
    242     }
    243 
    244     /**
    245      * Loads the JOSM keystore.
    246      * @return the (initialized) JOSM keystore
    247      * @throws IOException if an I/O error occurs
    248      * @throws GeneralSecurityException if a security error occurs
    249      * @since 7343
    250      */
    251     public static KeyStore loadJosmKeystore() throws IOException, GeneralSecurityException {
    252         try (InputStream in = Files.newInputStream(setupJosmKeystore())) {
    253             KeyStore ks = KeyStore.getInstance("JKS");
    254             ks.load(in, KEYSTORE_PASSWORD.get().toCharArray());
    255 
    256             if (Logging.isDebugEnabled()) {
    257                 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) {
    258                     Logging.debug("Alias in JOSM keystore: {0}", aliases.nextElement());
    259                 }
    260             }
    261             return ks;
    262         }
    263     }
    264 
    265     /**
    266      * Initializes the TLS basics.
    267      * @throws IOException if an I/O error occurs
    268      * @throws GeneralSecurityException if a security error occurs
    269      */
    270     private void initialize() throws IOException, GeneralSecurityException {
    271         KeyStore ks = loadJosmKeystore();
    272 
    273         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    274         kmf.init(ks, KEYENTRY_PASSWORD.get().toCharArray());
    275 
    276         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
    277         tmf.init(ks);
    278 
    279         sslContext = SSLContext.getInstance("TLSv1.2");
    280         sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    281 
    282         if (Logging.isTraceEnabled()) {
    283             Logging.trace("SSL Context protocol: {0}", sslContext.getProtocol());
    284             Logging.trace("SSL Context provider: {0}", sslContext.getProvider());
    285         }
    286 
    287         setupPlatform(ks);
    288     }
    289 
    290     /**
    291      * Setup the platform-dependant certificate stuff.
    292      * @param josmKs The JOSM keystore, containing localhost certificate and private key.
    293      * @return {@code true} if something has changed as a result of the call (certificate installation, etc.)
    294      * @throws KeyStoreException if the keystore has not been initialized (loaded)
    295      * @throws NoSuchAlgorithmException in case of error
    296      * @throws CertificateException in case of error
    297      * @throws IOException in case of error
    298      * @since 7343
    299      */
    300     public static boolean setupPlatform(KeyStore josmKs) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
    301         Enumeration<String> aliases = josmKs.aliases();
    302         if (aliases.hasMoreElements()) {
    303             return Main.platform.setupHttpsCertificate(ENTRY_ALIAS,
    304                     new KeyStore.TrustedCertificateEntry(josmKs.getCertificate(aliases.nextElement())));
    305         }
    306         return false;
    307     }
    308 
    309     /**
    310      * Starts or restarts the HTTPS server
    311      */
    312     public static void restartRemoteControlHttpsServer() {
    313         stopRemoteControlHttpsServer();
    314         if (RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.get()) {
    315             int port = Config.getPref().getInt("remote.control.https.port", HTTPS_PORT);
    316             try {
    317                 instance4 = new RemoteControlHttpsServer(port, false);
    318                 instance4.start();
    319             } catch (IOException | GeneralSecurityException ex) {
    320                 Logging.debug(ex);
    321                 Logging.warn(marktr("Cannot start IPv4 remotecontrol https server on port {0}: {1}"),
    322                         Integer.toString(port), ex.getLocalizedMessage());
    323             }
    324             try {
    325                 instance6 = new RemoteControlHttpsServer(port, true);
    326                 instance6.start();
    327             } catch (IOException | GeneralSecurityException ex) {
    328                 /* only show error when we also have no IPv4 */
    329                 if (instance4 == null) {
    330                     Logging.debug(ex);
    331                     Logging.warn(marktr("Cannot start IPv6 remotecontrol https server on port {0}: {1}"),
    332                         Integer.toString(port), ex.getLocalizedMessage());
    333                 }
    334             }
    335         }
    336     }
    337 
    338     /**
    339      * Stops the HTTPS server
    340      */
    341     public static void stopRemoteControlHttpsServer() {
    342         if (instance4 != null) {
    343             try {
    344                 instance4.stopServer();
    345             } catch (IOException ioe) {
    346                 Logging.error(ioe);
    347             }
    348             instance4 = null;
    349         }
    350         if (instance6 != null) {
    351             try {
    352                 instance6.stopServer();
    353             } catch (IOException ioe) {
    354                 Logging.error(ioe);
    355             }
    356             instance6 = null;
    357         }
    358     }
    359 
    360     /**
    361      * Constructs a new {@code RemoteControlHttpsServer}.
    362      * @param port The port this server will listen on
    363      * @param ipv6 Whether IPv6 or IPv4 server should be started
    364      * @throws IOException when connection errors
    365      * @throws GeneralSecurityException in case of SSL setup errors
    366      * @since 8339
    367      */
    368     public RemoteControlHttpsServer(int port, boolean ipv6) throws IOException, GeneralSecurityException {
    369         super("RemoteControl HTTPS Server");
    370         this.setDaemon(true);
    371 
    372         initialize();
    373 
    374         // Create SSL Server factory
    375         SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
    376         if (Logging.isTraceEnabled()) {
    377             Logging.trace("SSL factory - Supported Cipher suites: {0}", Arrays.toString(factory.getSupportedCipherSuites()));
    378         }
    379 
    380         this.server = factory.createServerSocket(port, 1, ipv6 ?
    381             RemoteControl.getInet6Address() : RemoteControl.getInet4Address());
    382 
    383         if (Logging.isTraceEnabled() && server instanceof SSLServerSocket) {
    384             SSLServerSocket sslServer = (SSLServerSocket) server;
    385             Logging.trace("SSL server - Enabled Cipher suites: {0}", Arrays.toString(sslServer.getEnabledCipherSuites()));
    386             Logging.trace("SSL server - Enabled Protocols: {0}", Arrays.toString(sslServer.getEnabledProtocols()));
    387             Logging.trace("SSL server - Enable Session Creation: {0}", sslServer.getEnableSessionCreation());
    388             Logging.trace("SSL server - Need Client Auth: {0}", sslServer.getNeedClientAuth());
    389             Logging.trace("SSL server - Want Client Auth: {0}", sslServer.getWantClientAuth());
    390             Logging.trace("SSL server - Use Client Mode: {0}", sslServer.getUseClientMode());
    391         }
    392     }
    393 
    394     /**
    395      * The main loop, spawns a {@link RequestProcessor} for each connection.
    396      */
    397     @Override
    398     public void run() {
    399         Logging.info(marktr("RemoteControl::Accepting secure remote connections on {0}:{1}"),
    400                 server.getInetAddress(), Integer.toString(server.getLocalPort()));
    401         while (true) {
    402             try {
    403                 @SuppressWarnings("resource")
    404                 Socket request = server.accept();
    405                 if (Logging.isTraceEnabled() && request instanceof SSLSocket) {
    406                     SSLSocket sslSocket = (SSLSocket) request;
    407                     Logging.trace("SSL socket - Enabled Cipher suites: {0}", Arrays.toString(sslSocket.getEnabledCipherSuites()));
    408                     Logging.trace("SSL socket - Enabled Protocols: {0}", Arrays.toString(sslSocket.getEnabledProtocols()));
    409                     Logging.trace("SSL socket - Enable Session Creation: {0}", sslSocket.getEnableSessionCreation());
    410                     Logging.trace("SSL socket - Need Client Auth: {0}", sslSocket.getNeedClientAuth());
    411                     Logging.trace("SSL socket - Want Client Auth: {0}", sslSocket.getWantClientAuth());
    412                     Logging.trace("SSL socket - Use Client Mode: {0}", sslSocket.getUseClientMode());
    413                     Logging.trace("SSL socket - Session: {0}", sslSocket.getSession());
    414                 }
    415                 RequestProcessor.processRequest(request);
    416             } catch (SocketException e) {
    417                 if (!server.isClosed()) {
    418                     Logging.error(e);
    419                 }
    420             } catch (IOException ioe) {
    421                 Logging.error(ioe);
    422             }
    423         }
    424     }
    425 
    426     /**
    427      * Stops the HTTPS server.
    428      *
    429      * @throws IOException if any I/O error occurs
    430      */
    431     public void stopServer() throws IOException {
    432         Logging.info(marktr("RemoteControl::Server {0}:{1} stopped."),
    433         server.getInetAddress(), Integer.toString(server.getLocalPort()));
    434         server.close();
    435     }
    436 }
  • src/org/openstreetmap/josm/tools/PlatformHook.java

     
    99import java.io.IOException;
    1010import java.io.InputStreamReader;
    1111import java.nio.charset.StandardCharsets;
    12 import java.security.KeyStore;
    1312import java.security.KeyStoreException;
    1413import java.security.NoSuchAlgorithmException;
    1514import java.security.cert.CertificateException;
     
    186185    }
    187186
    188187    /**
    189      * Setup system keystore to add JOSM HTTPS certificate (for remote control).
    190      * @param entryAlias The entry alias to use
    191      * @param trustedCert the JOSM certificate for localhost
    192      * @return {@code true} if something has changed as a result of the call (certificate installation, etc.)
    193      * @throws KeyStoreException in case of error
    194      * @throws IOException in case of error
    195      * @throws CertificateException in case of error
    196      * @throws NoSuchAlgorithmException in case of error
    197      * @since 7343
    198      */
    199     default boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
    200             throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
    201         // TODO setup HTTPS certificate on Unix and OS X systems
    202         return false;
    203     }
    204 
    205     /**
    206188     * Returns the {@code X509Certificate} matching the given certificate amendment information.
    207189     * @param certAmend certificate amendment
    208190     * @return the {@code X509Certificate} matching the given certificate amendment information, or {@code null}
  • src/org/openstreetmap/josm/tools/PlatformHookWindows.java

     
    3030import static org.openstreetmap.josm.tools.Utils.getSystemProperty;
    3131import static org.openstreetmap.josm.tools.WinRegistry.HKEY_LOCAL_MACHINE;
    3232
    33 import java.awt.GraphicsEnvironment;
    3433import java.io.BufferedWriter;
    3534import java.io.File;
    3635import java.io.IOException;
     
    4544import java.nio.file.Files;
    4645import java.nio.file.InvalidPathException;
    4746import java.nio.file.Path;
    48 import java.security.InvalidKeyException;
    49 import java.security.KeyFactory;
    5047import java.security.KeyStore;
    5148import java.security.KeyStoreException;
    5249import java.security.MessageDigest;
    5350import java.security.NoSuchAlgorithmException;
    54 import java.security.NoSuchProviderException;
    55 import java.security.PublicKey;
    56 import java.security.SignatureException;
    5751import java.security.cert.Certificate;
    5852import java.security.cert.CertificateException;
    5953import java.security.cert.X509Certificate;
    60 import java.security.spec.InvalidKeySpecException;
    61 import java.security.spec.X509EncodedKeySpec;
    6254import java.text.ParseException;
    6355import java.util.ArrayList;
    6456import java.util.Arrays;
     
    7264import java.util.regex.Matcher;
    7365import java.util.regex.Pattern;
    7466
    75 import javax.swing.JOptionPane;
    76 
    7767import org.openstreetmap.josm.Main;
    7868import org.openstreetmap.josm.data.StructUtils;
    7969import org.openstreetmap.josm.data.StructUtils.StructEntry;
     
    133123        }
    134124    }
    135125
    136     private static final byte[] INSECURE_PUBLIC_KEY = new byte[] {
    137         0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48,
    138         (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0,
    139         0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88,
    140         (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc,
    141         0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2,
    142         (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51,
    143         (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3,
    144         (byte) 0xc7, (byte) 0xc3, 0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d,
    145         (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba,
    146         (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7,
    147         (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc,
    148         0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc,
    149         (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c,
    150         0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03,
    151         (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0,
    152         (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b,
    153         0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60,
    154         (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6,
    155         (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f,
    156         (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8,
    157         0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4,
    158         (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63,
    159         (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e,
    160         0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01
    161     };
    162 
    163126    private static final String WINDOWS_ROOT = "Windows-ROOT";
    164127
    165128    private static final String CURRENT_VERSION = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
     
    348311        return ks;
    349312    }
    350313
    351     /**
    352      * Removes potential insecure certificates installed with previous versions of JOSM on Windows.
    353      * @throws NoSuchAlgorithmException on unsupported signature algorithms
    354      * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded
    355      * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT"
    356      * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
    357      * @since 7335
    358      */
    359     public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
    360         // We offered before a public private key we need now to remove from Windows PCs as it might be a huge security risk (see #10230)
    361         PublicKey insecurePubKey = null;
    362         try {
    363             insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY));
    364         } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
    365             Logging.error(e);
    366             return;
    367         }
    368         KeyStore ks = getRootKeystore();
    369         Enumeration<String> en = ks.aliases();
    370         Collection<String> insecureCertificates = new ArrayList<>();
    371         while (en.hasMoreElements()) {
    372             String alias = en.nextElement();
    373             // Look for certificates associated with a private key
    374             if (ks.isKeyEntry(alias)) {
    375                 try {
    376                     ks.getCertificate(alias).verify(insecurePubKey);
    377                     // If no exception, this is a certificate signed with the insecure key -> remove it
    378                     insecureCertificates.add(alias);
    379                 } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {
    380                     // If exception this is not a certificate related to JOSM, just trace it
    381                     Logging.trace(alias + " --> " + e.getClass().getName());
    382                     Logging.trace(e);
    383                 }
    384             }
    385         }
    386         // Remove insecure certificates
    387         if (!insecureCertificates.isEmpty()) {
    388             StringBuilder message = new StringBuilder("<html>");
    389             message.append(tr("A previous version of JOSM has installed a custom certificate "+
    390                     "in order to provide HTTPS support for Remote Control:"))
    391                    .append("<br><ul>");
    392             for (String alias : insecureCertificates) {
    393                 message.append("<li>")
    394                        .append(alias)
    395                        .append("</li>");
    396             }
    397             message.append("</ul>")
    398                    .append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+
    399                     "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+
    400                     "For your own safety, <b>please click Yes</b> in next dialog."))
    401                    .append("</html>");
    402             JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE);
    403             for (String alias : insecureCertificates) {
    404                 Logging.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias));
    405                 try {
    406                     ks.deleteEntry(alias);
    407                 } catch (KeyStoreException e) {
    408                     Logging.log(Logging.LEVEL_ERROR, tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage()), e);
    409                 }
    410             }
    411         }
    412     }
    413 
    414     @Override
    415     public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
    416             throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
    417         KeyStore ks = getRootKeystore();
    418         // Look for certificate to install
    419         try {
    420             String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());
    421             if (alias != null) {
    422                 // JOSM certificate found, return
    423                 Logging.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias));
    424                 return false;
    425             }
    426         } catch (ArrayIndexOutOfBoundsException e) {
    427             // catch error of JDK-8172244 as bug seems to not be fixed anytime soon
    428             Logging.log(Logging.LEVEL_ERROR, "JDK-8172244 occured. Abort HTTPS setup", e);
    429             return false;
    430         }
    431         if (!GraphicsEnvironment.isHeadless()) {
    432             // JOSM certificate not found, warn user
    433             StringBuilder message = new StringBuilder("<html>");
    434             message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+
    435                     "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+
    436                     "You are now going to be prompted by Windows to confirm this operation.<br>"+
    437                     "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+
    438                     "If unsure, you can also click No then disable HTTPS support in Remote Control preferences."))
    439                    .append("</html>");
    440             JOptionPane.showMessageDialog(Main.parent, message.toString(),
    441                     tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE);
    442         }
    443         // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox
    444         Logging.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));
    445         ks.setEntry(entryAlias, trustedCert, null);
    446         return true;
    447     }
    448 
    449314    @Override
    450315    public X509Certificate getX509Certificate(NativeCertAmend certAmend)
    451316            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
  • test/unit/org/openstreetmap/josm/io/remotecontrol/RemoteControlTest.java

     
    1010import java.net.HttpURLConnection;
    1111import java.net.URL;
    1212import java.nio.charset.StandardCharsets;
    13 import java.nio.file.Files;
    14 import java.nio.file.Paths;
    1513import java.security.GeneralSecurityException;
    16 import java.security.SecureRandom;
    17 import java.security.cert.X509Certificate;
    18 
    19 import javax.net.ssl.HostnameVerifier;
    20 import javax.net.ssl.HttpsURLConnection;
    21 import javax.net.ssl.SSLContext;
    22 import javax.net.ssl.TrustManager;
    23 import javax.net.ssl.X509TrustManager;
    2414
    2515import org.junit.After;
    2616import org.junit.Before;
    2717import org.junit.Test;
    2818import org.openstreetmap.josm.JOSMFixture;
    2919import org.openstreetmap.josm.spi.preferences.Config;
    30 import org.openstreetmap.josm.tools.Logging;
    31 
    32 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    3320
    3421/**
    3522 * Unit tests for Remote Control
     
    3724public class RemoteControlTest {
    3825
    3926    private String httpBase;
    40     private String httpsBase;
    4127
    4228    /**
    4329     * Starts Remote control before testing requests.
     
    4632    @Before
    4733    public void setUp() throws GeneralSecurityException {
    4834        JOSMFixture.createUnitTestFixture().init();
    49         RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.put(true);
    50         deleteKeystore();
    5135
    5236        RemoteControl.start();
    53         disableCertificateValidation();
    5437        httpBase = "http://127.0.0.1:"+Config.getPref().getInt("remote.control.port", 8111);
    55         httpsBase = "https://127.0.0.1:"+Config.getPref().getInt("remote.control.https.port", 8112);
    56     }
    57 
    58     /**
    59      * Deletes JOSM keystore, if it exists.
    60      */
    61     public static void deleteKeystore() {
    62         try {
    63             Files.deleteIfExists(Paths.get(
    64                     RemoteControl.getRemoteControlDir()).resolve(RemoteControlHttpsServer.KEYSTORE_FILENAME));
    65         } catch (IOException e) {
    66             Logging.error(e);
    67         }
    68     }
    69 
    70     /**
    71      * Disable all HTTPS validation mechanisms as described
    72      * <a href="http://stackoverflow.com/a/2893932/2257172">here</a> and
    73      * <a href="http://stackoverflow.com/a/19542614/2257172">here</a>
    74      * @throws GeneralSecurityException if a security error occurs
    75      */
    76     public void disableCertificateValidation() throws GeneralSecurityException {
    77         // Create a trust manager that does not validate certificate chains
    78         TrustManager[] trustAllCerts = new TrustManager[] {
    79             new X509TrustManager() {
    80                 @Override
    81                 @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")
    82                 public X509Certificate[] getAcceptedIssuers() {
    83                     return new X509Certificate[0];
    84                 }
    85 
    86                 @Override
    87                 @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")
    88                 public void checkClientTrusted(X509Certificate[] certs, String authType) {
    89                 }
    90 
    91                 @Override
    92                 @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")
    93                 public void checkServerTrusted(X509Certificate[] certs, String authType) {
    94                 }
    95             }
    96         };
    97 
    98         // Install the all-trusting trust manager
    99         SSLContext sc = SSLContext.getInstance("TLS");
    100         sc.init(null, trustAllCerts, new SecureRandom());
    101         HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    102 
    103         // Create all-trusting host name verifier
    104         HostnameVerifier allHostsValid = (hostname, session) -> true;
    105 
    106         // Install the all-trusting host verifier
    107         HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    10838    }
    10939
    11040    /**
     
    12454        testListOfCommands(httpBase);
    12555    }
    12656
    127     /**
    128      * Tests that sending an HTTPS request without command results in HTTP 400, with all available commands in error message.
    129      * @throws Exception if an error occurs
    130      */
    131     @Test
    132     public void testHttpsListOfCommands() throws Exception {
    133         testListOfCommands(httpsBase);
    134     }
    135 
    13657    private void testListOfCommands(String url) throws IOException, ReflectiveOperationException {
    13758        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
    13859        connection.connect();
  • test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java

     
    4040    }
    4141
    4242    /**
    43      * Test method for {@code PlatformHookOsx#setupHttpsCertificate}
    44      * @throws Exception if an error occurs
    45      */
    46     @Test
    47     public void testSetupHttpsCertificate() throws Exception {
    48         assertFalse(hook.setupHttpsCertificate(null, null));
    49     }
    50 
    51     /**
    5243     * Test method for {@code PlatformHookOsx#afterPrefStartupHook}
    5344     */
    5445    @Test
  • test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java

     
    1010
    1111import java.io.File;
    1212import java.io.IOException;
    13 import java.security.KeyStore;
    14 import java.security.KeyStore.TrustedCertificateEntry;
    1513import java.security.KeyStoreException;
    1614import java.util.Collection;
    1715
     
    1917import org.junit.Test;
    2018import org.openstreetmap.josm.JOSMFixture;
    2119import org.openstreetmap.josm.Main;
    22 import org.openstreetmap.josm.io.remotecontrol.RemoteControlHttpsServer;
    23 import org.openstreetmap.josm.io.remotecontrol.RemoteControlTest;
    2420
    2521/**
    2622 * Unit tests of {@link PlatformHookWindows} class.
     
    6561    }
    6662
    6763    /**
    68      * Test method for {@code PlatformHookWindows#removeInsecureCertificates}
    69      * @throws Exception if an error occurs
    70      */
    71     @Test
    72     public void testRemoveInsecureCertificates() throws Exception {
    73         if (Main.isPlatformWindows()) {
    74             PlatformHookWindows.removeInsecureCertificates();
    75         } else {
    76             try {
    77                 PlatformHookWindows.removeInsecureCertificates();
    78                 fail("Expected KeyStoreException");
    79             } catch (KeyStoreException e) {
    80                 Logging.info(e.getMessage());
    81             }
    82         }
    83     }
    84 
    85     /**
    86      * Test method for {@code PlatformHookWindows#setupHttpsCertificate}
    87      * @throws Exception if an error occurs
    88      */
    89     @Test
    90     public void testSetupHttpsCertificate() throws Exception {
    91         RemoteControlTest.deleteKeystore();
    92         KeyStore ks = RemoteControlHttpsServer.loadJosmKeystore();
    93         TrustedCertificateEntry trustedCert = new KeyStore.TrustedCertificateEntry(ks.getCertificate(ks.aliases().nextElement()));
    94         if (Main.isPlatformWindows()) {
    95             hook.setupHttpsCertificate(RemoteControlHttpsServer.ENTRY_ALIAS, trustedCert);
    96         } else {
    97             try {
    98                 hook.setupHttpsCertificate(RemoteControlHttpsServer.ENTRY_ALIAS, trustedCert);
    99                 fail("Expected KeyStoreException");
    100             } catch (KeyStoreException e) {
    101                 Logging.info(e.getMessage());
    102             }
    103         }
    104     }
    105 
    106     /**
    10764     * Test method for {@code PlatformHookWindows#afterPrefStartupHook}
    10865     */
    10966    @Test