Ticket #10033: remove_https_remote_control.diff

File remove_https_remote_control.diff, 57.7 KB (added by Don-vip, 6 years ago)

what it looks like if we remove HTTP support for remote control

  • build.xml

     
    152152                <attribute name="Codebase" value="josm.openstreetmap.de"/>
    153153                <attribute name="Application-Name" value="JOSM - Java OpenStreetMap Editor"/>
    154154                <!-- Java 9 stuff. Entries are safely ignored by Java 8 -->
    155                 <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" />
     155                <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" />
    156156                <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" />
    157157            </manifest>
    158158            <service type="java.text.spi.DecimalFormatSymbolsProvider" provider="org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider" />
     
    371371            <doctitle><![CDATA[<h2>JOSM - Javadoc</h2>]]></doctitle>
    372372            <bottom><![CDATA[<a href="https://josm.openstreetmap.de/">JOSM</a>]]></bottom>
    373373            <arg value="--add-exports" if:set="isJava9" />
    374             <arg value="java.base/sun.security.util=ALL-UNNAMED" if:set="isJava9" />
    375             <arg value="--add-exports" if:set="isJava9" />
    376             <arg value="java.base/sun.security.x509=ALL-UNNAMED" if:set="isJava9" />
    377             <arg value="--add-exports" if:set="isJava9" />
    378374            <arg value="javafx.graphics/com.sun.javafx.application=ALL-UNNAMED" if:set="isJava9" />
    379375        </javadoc>
    380376    </target>
     
    464460                    <jvmarg value="--add-modules" if:set="isJava9" />
    465461                    <jvmarg value="java.activation,java.se.ee" if:set="isJava9" />
    466462                    <jvmarg value="--add-exports" if:set="isJava9" />
    467                     <jvmarg value="java.base/sun.security.util=ALL-UNNAMED" if:set="isJava9" />
    468                     <jvmarg value="--add-exports" if:set="isJava9" />
    469                     <jvmarg value="java.base/sun.security.x509=ALL-UNNAMED" if:set="isJava9" />
    470                     <jvmarg value="--add-exports" if:set="isJava9" />
    471463                    <jvmarg value="javafx.graphics/com.sun.javafx.application=ALL-UNNAMED" if:set="isJava9" />
    472464                    <jvmarg value="--add-exports" if:set="isJava9" />
    473465                    <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

     
    8686    private static final String[] OBSOLETE_PREF_KEYS = {
    8787      "projection", /* remove entry after Nov. 2017 */
    8888      "projection.sub", /* remove entry after Nov. 2017 */
     89      "remotecontrol.https.enabled", /* remove entry after Aug. 2018 */
     90      "remotecontrol.https.port", /* remove entry after Aug. 2018 */
    8991    };
    9092
    9193    private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50);
  • src/org/openstreetmap/josm/gui/MainApplication.java

     
    2424import java.security.AllPermission;
    2525import java.security.CodeSource;
    2626import java.security.GeneralSecurityException;
    27 import java.security.KeyStoreException;
    28 import java.security.NoSuchAlgorithmException;
    2927import java.security.PermissionCollection;
    3028import java.security.Permissions;
    3129import java.security.Policy;
    32 import java.security.cert.CertificateException;
    3330import java.util.ArrayList;
    3431import java.util.Arrays;
    3532import java.util.Collection;
     
    10341031
    10351032        SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
    10361033
    1037         if (Main.isPlatformWindows()) {
    1038             try {
    1039                 // Check for insecure certificates to remove.
    1040                 // This is Windows-dependant code but it can't go to preStartupHook (need i18n)
    1041                 // neither startupHook (need to be called before remote control)
    1042                 PlatformHookWindows.removeInsecureCertificates();
    1043             } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
    1044                 Logging.error(e);
    1045             }
    1046         }
    1047 
    10481034        if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
    10491035            RemoteControl.start();
    10501036        }
  • 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.preferences.DefaultTabPreferenceSetting;
    3122import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
    3223import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
     
    3526import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
    3627import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
    3728import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
    38 import org.openstreetmap.josm.io.remotecontrol.RemoteControlHttpsServer;
    3929import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler;
    4030import org.openstreetmap.josm.spi.preferences.Config;
    4131import org.openstreetmap.josm.tools.GBC;
    42 import org.openstreetmap.josm.tools.Logging;
    43 import org.openstreetmap.josm.tools.PlatformHookWindows;
    4432
    4533/**
    4634 * Preference settings for Remote Control.
     
    7159
    7260    private final Map<PermissionPrefWithDefault, JCheckBox> prefs = new LinkedHashMap<>();
    7361    private JCheckBox enableRemoteControl;
    74     private JCheckBox enableHttpsSupport;
    75 
    76     private JButton installCertificate;
    77     private JButton uninstallCertificate;
    7862
    7963    private final JCheckBox loadInNewLayer = new JCheckBox(tr("Download objects to new layer"));
    8064    private final JCheckBox alwaysAskUserConfirm = new JCheckBox(tr("Confirm all Remote Control actions manually"));
     
    9175        remote.add(descLabel, GBC.eol().insets(5, 5, 0, 10).fill(GBC.HORIZONTAL));
    9276
    9377        final JLabel portLabel = new JLabel("<html>"
    94                 + tr("JOSM will always listen at <b>port {0}</b> (http) and <b>port {1}</b> (https) on localhost."
    95                 + "<br>These ports are not configurable because they are referenced by external applications talking to JOSM.",
    96                 Config.getPref().get("remote.control.port", "8111"),
    97                 Config.getPref().get("remote.control.https.port", "8112")) + "</html>");
     78                + tr("JOSM will always listen at <b>port {0}</b> (http) on localhost."
     79                + "<br>This port is not configurable because it is referenced by external applications talking to JOSM.",
     80                Config.getPref().get("remote.control.port", "8111")) + "</html>");
    9881        portLabel.setFont(portLabel.getFont().deriveFont(Font.PLAIN));
    9982        remote.add(portLabel, GBC.eol().insets(5, 5, 0, 10).fill(GBC.HORIZONTAL));
    10083
     
    10689
    10790        remote.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 5));
    10891
    109         boolean https = RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.get();
    110 
    111         enableHttpsSupport = new JCheckBox(tr("Enable HTTPS support"), https);
    112         wrapper.add(enableHttpsSupport, GBC.eol().fill(GBC.HORIZONTAL));
    113 
    114         // Certificate installation only available on Windows for now, see #10033
    115         if (Main.isPlatformWindows()) {
    116             installCertificate = new JButton(tr("Install..."));
    117             uninstallCertificate = new JButton(tr("Uninstall..."));
    118             installCertificate.setToolTipText(tr("Install JOSM localhost certificate to system/browser root keystores"));
    119             uninstallCertificate.setToolTipText(tr("Uninstall JOSM localhost certificate from system/browser root keystores"));
    120             wrapper.add(new JLabel(tr("Certificate:")), GBC.std().insets(15, 5, 0, 0));
    121             wrapper.add(installCertificate, GBC.std().insets(5, 5, 0, 0));
    122             wrapper.add(uninstallCertificate, GBC.eol().insets(5, 5, 0, 0));
    123             enableHttpsSupport.addActionListener(e -> installCertificate.setEnabled(enableHttpsSupport.isSelected()));
    124             installCertificate.addActionListener(e -> {
    125                 try {
    126                     boolean changed = RemoteControlHttpsServer.setupPlatform(
    127                             RemoteControlHttpsServer.loadJosmKeystore());
    128                     String msg = changed ?
    129                             tr("Certificate has been successfully installed.") :
    130                             tr("Certificate is already installed. Nothing to do.");
    131                     Logging.info(msg);
    132                     JOptionPane.showMessageDialog(wrapper, msg);
    133                 } catch (IOException | GeneralSecurityException ex) {
    134                     Logging.error(ex);
    135                 }
    136             });
    137             uninstallCertificate.addActionListener(e -> {
    138                 try {
    139                     String msg;
    140                     KeyStore ks = PlatformHookWindows.getRootKeystore();
    141                     if (ks.containsAlias(RemoteControlHttpsServer.ENTRY_ALIAS)) {
    142                         Logging.info(tr("Removing certificate {0} from root keystore.", RemoteControlHttpsServer.ENTRY_ALIAS));
    143                         ks.deleteEntry(RemoteControlHttpsServer.ENTRY_ALIAS);
    144                         msg = tr("Certificate has been successfully uninstalled.");
    145                     } else {
    146                         msg = tr("Certificate is not installed. Nothing to do.");
    147                     }
    148                     Logging.info(msg);
    149                     JOptionPane.showMessageDialog(wrapper, msg);
    150                 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException ex) {
    151                     Logging.error(ex);
    152                 }
    153             });
    154             installCertificate.setEnabled(https);
    155         }
    156 
    15792        wrapper.add(new JSeparator(), GBC.eop().fill(GBC.HORIZONTAL).insets(15, 5, 15, 5));
    15893
    15994        wrapper.add(new JLabel(tr("Permitted actions:")), GBC.eol().insets(5, 0, 0, 0));
     
    174109
    175110        ActionListener remoteControlEnabled = e -> {
    176111            GuiHelper.setEnabledRec(wrapper, enableRemoteControl.isSelected());
    177             enableHttpsSupport.setEnabled(RemoteControl.supportsHttps());
    178             // 'setEnabled(false)' does not work for JLabel with html text, so do it manually
    179             // FIXME: use QuadStateCheckBox to make checkboxes unset when disabled
    180             if (installCertificate != null && uninstallCertificate != null) {
    181                 // Install certificate button is enabled if HTTPS is also enabled
    182                 installCertificate.setEnabled(enableRemoteControl.isSelected()
    183                         && enableHttpsSupport.isSelected() && RemoteControl.supportsHttps());
    184                 // Uninstall certificate button is always enabled
    185                 uninstallCertificate.setEnabled(RemoteControl.supportsHttps());
    186             }
    187112        };
    188113        enableRemoteControl.addActionListener(remoteControlEnabled);
    189114        remoteControlEnabled.actionPerformed(null);
     
    193118    @Override
    194119    public boolean ok() {
    195120        boolean enabled = enableRemoteControl.isSelected();
    196         boolean httpsEnabled = enableHttpsSupport.isSelected();
    197121        boolean changed = RemoteControl.PROP_REMOTECONTROL_ENABLED.put(enabled);
    198         boolean httpsChanged = RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.put(httpsEnabled);
    199122        if (enabled) {
    200123            for (Entry<PermissionPrefWithDefault, JCheckBox> p : prefs.entrySet()) {
    201124                Config.getPref().putBoolean(p.getKey().pref, p.getValue().isSelected());
     
    209132            } else {
    210133                RemoteControl.stop();
    211134            }
    212         } else if (httpsChanged) {
    213             if (httpsEnabled) {
    214                 RemoteControlHttpsServer.restartRemoteControlHttpsServer();
    215             } else {
    216                 RemoteControlHttpsServer.stopRemoteControlHttpsServer();
    217             }
    218135        }
    219136        return false;
    220137    }
  • 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 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("TLS");
    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

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