Ticket #10033: remove_https_remote_control2.diff
File remove_https_remote_control2.diff, 57.7 KB (added by , 6 years ago) |
---|
-
build.xml
177 177 <attribute name="Codebase" value="josm.openstreetmap.de"/> 178 178 <attribute name="Application-Name" value="JOSM - Java OpenStreetMap Editor"/> 179 179 <!-- 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" /> 181 181 <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" /> 182 182 </manifest> 183 183 <service type="java.text.spi.DecimalFormatSymbolsProvider" provider="org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider" /> … … 399 399 <bottom><![CDATA[<a href="https://josm.openstreetmap.de/">JOSM</a>]]></bottom> 400 400 <arg value="-html5" if:set="isJava9" /> 401 401 <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" />406 402 <arg value="javafx.graphics/com.sun.javafx.application=ALL-UNNAMED" if:set="isJava9" /> 407 403 </javadoc> 408 404 </target> … … 493 489 <jvmarg value="--add-modules" if:set="isJava9" unless:set="isJava11" /> 494 490 <jvmarg value="java.activation,java.se.ee" if:set="isJava9" unless:set="isJava11" /> 495 491 <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" />500 492 <jvmarg value="javafx.graphics/com.sun.javafx.application=ALL-UNNAMED" if:set="isJava9" /> 501 493 <jvmarg value="--add-exports" if:set="isJava9" /> 502 494 <jvmarg value="jdk.deploy/com.sun.deploy.config=ALL-UNNAMED" if:set="isJava9" /> -
josm-latest.jnlp
19 19 <all-permissions/> 20 20 </security> 21 21 <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"/> 23 23 <jar href="josm-latest.jar"/> 24 24 <property name="java.util.Arrays.useLegacyMergeSort" value="true"/> 25 25 </resources> -
josm.jnlp
19 19 <all-permissions/> 20 20 </security> 21 21 <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"/> 23 23 <jar href="josm-tested.jar"/> 24 24 <property name="java.util.Arrays.useLegacyMergeSort" value="true"/> 25 25 </resources> -
src/org/openstreetmap/josm/data/Preferences.java
78 78 public class Preferences extends AbstractPreferences { 79 79 80 80 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 */ 81 83 }; 82 84 83 85 private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50); -
src/org/openstreetmap/josm/gui/MainApplication.java
27 27 import java.security.AllPermission; 28 28 import java.security.CodeSource; 29 29 import java.security.GeneralSecurityException; 30 import java.security.KeyStoreException;31 import java.security.NoSuchAlgorithmException;32 30 import java.security.PermissionCollection; 33 31 import java.security.Permissions; 34 32 import java.security.Policy; 35 import java.security.cert.CertificateException;36 33 import java.util.ArrayList; 37 34 import java.util.Arrays; 38 35 import java.util.Collection; … … 1080 1077 1081 1078 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector)); 1082 1079 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 1094 1080 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 1095 1081 RemoteControl.start(); 1096 1082 } -
src/org/openstreetmap/josm/gui/preferences/remotecontrol/RemoteControlPreference.java
7 7 import java.awt.Font; 8 8 import java.awt.GridBagLayout; 9 9 import 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;16 10 import java.util.LinkedHashMap; 17 11 import java.util.Map; 18 12 import java.util.Map.Entry; 19 13 20 14 import javax.swing.BorderFactory; 21 15 import javax.swing.Box; 22 import javax.swing.JButton;23 16 import javax.swing.JCheckBox; 24 17 import javax.swing.JLabel; 25 import javax.swing.JOptionPane;26 18 import javax.swing.JPanel; 27 19 import javax.swing.JSeparator; 28 20 29 import org.openstreetmap.josm.Main;30 21 import org.openstreetmap.josm.gui.help.HelpUtil; 31 22 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting; 32 23 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; … … 36 27 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel; 37 28 import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault; 38 29 import org.openstreetmap.josm.io.remotecontrol.RemoteControl; 39 import org.openstreetmap.josm.io.remotecontrol.RemoteControlHttpsServer;40 30 import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler; 41 31 import org.openstreetmap.josm.spi.preferences.Config; 42 32 import org.openstreetmap.josm.tools.GBC; 43 import org.openstreetmap.josm.tools.Logging;44 import org.openstreetmap.josm.tools.PlatformHookWindows;45 33 46 34 /** 47 35 * Preference settings for Remote Control. … … 72 60 73 61 private final Map<PermissionPrefWithDefault, JCheckBox> prefs = new LinkedHashMap<>(); 74 62 private JCheckBox enableRemoteControl; 75 private JCheckBox enableHttpsSupport;76 77 private JButton installCertificate;78 private JButton uninstallCertificate;79 63 80 64 private final JCheckBox loadInNewLayer = new JCheckBox(tr("Download as new layer")); 81 65 private final JCheckBox alwaysAskUserConfirm = new JCheckBox(tr("Confirm all Remote Control actions manually")); … … 92 76 remote.add(descLabel, GBC.eol().insets(5, 5, 0, 10).fill(GBC.HORIZONTAL)); 93 77 94 78 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>"); 99 82 portLabel.setFont(portLabel.getFont().deriveFont(Font.PLAIN)); 100 83 remote.add(portLabel, GBC.eol().insets(5, 5, 0, 10).fill(GBC.HORIZONTAL)); 101 84 … … 107 90 108 91 remote.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 5)); 109 92 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 #10033116 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 158 93 wrapper.add(new JSeparator(), GBC.eop().fill(GBC.HORIZONTAL).insets(15, 5, 15, 5)); 159 94 160 95 wrapper.add(new JLabel(tr("Permitted actions:")), GBC.eol().insets(5, 0, 0, 0)); … … 175 110 176 111 ActionListener remoteControlEnabled = e -> { 177 112 GuiHelper.setEnabledRec(wrapper, enableRemoteControl.isSelected()); 178 enableHttpsSupport.setEnabled(RemoteControl.supportsHttps());179 // 'setEnabled(false)' does not work for JLabel with html text, so do it manually180 // FIXME: use QuadStateCheckBox to make checkboxes unset when disabled181 if (installCertificate != null && uninstallCertificate != null) {182 // Install certificate button is enabled if HTTPS is also enabled183 installCertificate.setEnabled(enableRemoteControl.isSelected()184 && enableHttpsSupport.isSelected() && RemoteControl.supportsHttps());185 // Uninstall certificate button is always enabled186 uninstallCertificate.setEnabled(RemoteControl.supportsHttps());187 }188 113 }; 189 114 enableRemoteControl.addActionListener(remoteControlEnabled); 190 115 remoteControlEnabled.actionPerformed(null); … … 194 119 @Override 195 120 public boolean ok() { 196 121 boolean enabled = enableRemoteControl.isSelected(); 197 boolean httpsEnabled = enableHttpsSupport.isSelected();198 122 boolean changed = RemoteControl.PROP_REMOTECONTROL_ENABLED.put(enabled); 199 boolean httpsChanged = RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.put(httpsEnabled);200 123 if (enabled) { 201 124 for (Entry<PermissionPrefWithDefault, JCheckBox> p : prefs.entrySet()) { 202 125 Config.getPref().putBoolean(p.getKey().pref, p.getValue().isSelected()); … … 210 133 } else { 211 134 RemoteControl.stop(); 212 135 } 213 } else if (httpsChanged) {214 if (httpsEnabled) {215 RemoteControlHttpsServer.restartRemoteControlHttpsServer();216 } else {217 RemoteControlHttpsServer.stopRemoteControlHttpsServer();218 }219 136 } 220 137 return false; 221 138 } -
src/org/openstreetmap/josm/io/remotecontrol/RemoteControl.java
10 10 import org.openstreetmap.josm.data.preferences.BooleanProperty; 11 11 import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler; 12 12 import org.openstreetmap.josm.spi.preferences.Config; 13 import org.openstreetmap.josm.tools.Logging;14 13 15 14 /** 16 15 * Manager class for remote control operations. … … 27 26 public static final BooleanProperty PROP_REMOTECONTROL_ENABLED = new BooleanProperty("remotecontrol.enabled", false); 28 27 29 28 /** 30 * If the remote control feature is enabled or disabled for HTTPS. If disabled,31 * only HTTP access will be available.32 * @since 733533 */34 public static final BooleanProperty PROP_REMOTECONTROL_HTTPS_ENABLED = new BooleanProperty(35 "remotecontrol.https.enabled", false);36 37 /**38 29 * RemoteControl HTTP protocol version. Change minor number for compatible 39 30 * interface extensions. Change major number in case of incompatible 40 31 * changes. … … 47 38 */ 48 39 public static void start() { 49 40 RemoteControlHttpServer.restartRemoteControlHttpServer(); 50 if (supportsHttps()) {51 RemoteControlHttpsServer.restartRemoteControlHttpsServer();52 }53 41 } 54 42 55 43 /** … … 58 46 */ 59 47 public static void stop() { 60 48 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} classes69 * @since 1270370 */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 }78 49 } 79 50 80 51 /** -
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 694172 */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 733792 */93 public static final String KEYSTORE_FILENAME = "josm.keystore";94 95 /**96 * Preference for keystore password (automatically generated by JOSM).97 * @since 733598 */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 7335104 */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 7343110 */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 types116 * @param v value117 * @return which one118 * @throws IOException if any I/O error occurs119 */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 KeyPair133 * @param days how many days from now the Certificate is valid for134 * @param algorithm the signing algorithm, eg "SHA256withRSA"135 * @param san SubjectAlternativeName extension (optional)136 * @return the self-signed X.509 Certificate137 * @throws GeneralSecurityException if any security error occurs138 * @throws IOException if any I/O error occurs139 */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 0161 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 critical180 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 keystore201 * @throws IOException if an I/O error occurs202 * @throws GeneralSecurityException if a security error occurs203 * @since 7343204 */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 1825217 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/2257172229 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 keystore247 * @throws IOException if an I/O error occurs248 * @throws GeneralSecurityException if a security error occurs249 * @since 7343250 */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 occurs268 * @throws GeneralSecurityException if a security error occurs269 */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 error296 * @throws CertificateException in case of error297 * @throws IOException in case of error298 * @since 7343299 */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 server311 */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 server340 */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 on363 * @param ipv6 Whether IPv6 or IPv4 server should be started364 * @throws IOException when connection errors365 * @throws GeneralSecurityException in case of SSL setup errors366 * @since 8339367 */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 factory375 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 @Override398 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 occurs430 */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
9 9 import java.io.IOException; 10 10 import java.io.InputStreamReader; 11 11 import java.nio.charset.StandardCharsets; 12 import java.security.KeyStore;13 12 import java.security.KeyStoreException; 14 13 import java.security.NoSuchAlgorithmException; 15 14 import java.security.cert.CertificateException; … … 186 185 } 187 186 188 187 /** 189 * Setup system keystore to add JOSM HTTPS certificate (for remote control).190 * @param entryAlias The entry alias to use191 * @param trustedCert the JOSM certificate for localhost192 * @return {@code true} if something has changed as a result of the call (certificate installation, etc.)193 * @throws KeyStoreException in case of error194 * @throws IOException in case of error195 * @throws CertificateException in case of error196 * @throws NoSuchAlgorithmException in case of error197 * @since 7343198 */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 systems202 return false;203 }204 205 /**206 188 * Returns the {@code X509Certificate} matching the given certificate amendment information. 207 189 * @param certAmend certificate amendment 208 190 * @return the {@code X509Certificate} matching the given certificate amendment information, or {@code null} -
src/org/openstreetmap/josm/tools/PlatformHookWindows.java
30 30 import static org.openstreetmap.josm.tools.Utils.getSystemProperty; 31 31 import static org.openstreetmap.josm.tools.WinRegistry.HKEY_LOCAL_MACHINE; 32 32 33 import java.awt.GraphicsEnvironment;34 33 import java.io.BufferedWriter; 35 34 import java.io.File; 36 35 import java.io.IOException; … … 45 44 import java.nio.file.Files; 46 45 import java.nio.file.InvalidPathException; 47 46 import java.nio.file.Path; 48 import java.security.InvalidKeyException;49 import java.security.KeyFactory;50 47 import java.security.KeyStore; 51 48 import java.security.KeyStoreException; 52 49 import java.security.MessageDigest; 53 50 import java.security.NoSuchAlgorithmException; 54 import java.security.NoSuchProviderException;55 import java.security.PublicKey;56 import java.security.SignatureException;57 51 import java.security.cert.Certificate; 58 52 import java.security.cert.CertificateException; 59 53 import java.security.cert.X509Certificate; 60 import java.security.spec.InvalidKeySpecException;61 import java.security.spec.X509EncodedKeySpec;62 54 import java.text.ParseException; 63 55 import java.util.ArrayList; 64 56 import java.util.Arrays; … … 72 64 import java.util.regex.Matcher; 73 65 import java.util.regex.Pattern; 74 66 75 import javax.swing.JOptionPane;76 77 67 import org.openstreetmap.josm.Main; 78 68 import org.openstreetmap.josm.data.StructUtils; 79 69 import org.openstreetmap.josm.data.StructUtils.StructEntry; … … 133 123 } 134 124 } 135 125 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, 0x01161 };162 163 126 private static final String WINDOWS_ROOT = "Windows-ROOT"; 164 127 165 128 private static final String CURRENT_VERSION = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; … … 348 311 return ks; 349 312 } 350 313 351 /**352 * Removes potential insecure certificates installed with previous versions of JOSM on Windows.353 * @throws NoSuchAlgorithmException on unsupported signature algorithms354 * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded355 * @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 given357 * @since 7335358 */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 key374 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 it378 insecureCertificates.add(alias);379 } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {380 // If exception this is not a certificate related to JOSM, just trace it381 Logging.trace(alias + " --> " + e.getClass().getName());382 Logging.trace(e);383 }384 }385 }386 // Remove insecure certificates387 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 @Override415 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)416 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {417 KeyStore ks = getRootKeystore();418 // Look for certificate to install419 try {420 String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());421 if (alias != null) {422 // JOSM certificate found, return423 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 soon428 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 user433 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 Firefox444 Logging.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));445 ks.setEntry(entryAlias, trustedCert, null);446 return true;447 }448 449 314 @Override 450 315 public X509Certificate getX509Certificate(NativeCertAmend certAmend) 451 316 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { -
test/unit/org/openstreetmap/josm/io/remotecontrol/RemoteControlTest.java
10 10 import java.net.HttpURLConnection; 11 11 import java.net.URL; 12 12 import java.nio.charset.StandardCharsets; 13 import java.nio.file.Files;14 import java.nio.file.Paths;15 13 import 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;24 14 25 15 import org.junit.After; 26 16 import org.junit.Before; 27 17 import org.junit.Test; 28 18 import org.openstreetmap.josm.JOSMFixture; 29 19 import org.openstreetmap.josm.spi.preferences.Config; 30 import org.openstreetmap.josm.tools.Logging;31 32 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;33 20 34 21 /** 35 22 * Unit tests for Remote Control … … 37 24 public class RemoteControlTest { 38 25 39 26 private String httpBase; 40 private String httpsBase;41 27 42 28 /** 43 29 * Starts Remote control before testing requests. … … 46 32 @Before 47 33 public void setUp() throws GeneralSecurityException { 48 34 JOSMFixture.createUnitTestFixture().init(); 49 RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.put(true);50 deleteKeystore();51 35 52 36 RemoteControl.start(); 53 disableCertificateValidation();54 37 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 described72 * <a href="http://stackoverflow.com/a/2893932/2257172">here</a> and73 * <a href="http://stackoverflow.com/a/19542614/2257172">here</a>74 * @throws GeneralSecurityException if a security error occurs75 */76 public void disableCertificateValidation() throws GeneralSecurityException {77 // Create a trust manager that does not validate certificate chains78 TrustManager[] trustAllCerts = new TrustManager[] {79 new X509TrustManager() {80 @Override81 @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")82 public X509Certificate[] getAcceptedIssuers() {83 return new X509Certificate[0];84 }85 86 @Override87 @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")88 public void checkClientTrusted(X509Certificate[] certs, String authType) {89 }90 91 @Override92 @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")93 public void checkServerTrusted(X509Certificate[] certs, String authType) {94 }95 }96 };97 98 // Install the all-trusting trust manager99 SSLContext sc = SSLContext.getInstance("TLS");100 sc.init(null, trustAllCerts, new SecureRandom());101 HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());102 103 // Create all-trusting host name verifier104 HostnameVerifier allHostsValid = (hostname, session) -> true;105 106 // Install the all-trusting host verifier107 HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);108 38 } 109 39 110 40 /** … … 124 54 testListOfCommands(httpBase); 125 55 } 126 56 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 occurs130 */131 @Test132 public void testHttpsListOfCommands() throws Exception {133 testListOfCommands(httpsBase);134 }135 136 57 private void testListOfCommands(String url) throws IOException, ReflectiveOperationException { 137 58 HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); 138 59 connection.connect(); -
test/unit/org/openstreetmap/josm/tools/PlatformHookOsxTest.java
40 40 } 41 41 42 42 /** 43 * Test method for {@code PlatformHookOsx#setupHttpsCertificate}44 * @throws Exception if an error occurs45 */46 @Test47 public void testSetupHttpsCertificate() throws Exception {48 assertFalse(hook.setupHttpsCertificate(null, null));49 }50 51 /**52 43 * Test method for {@code PlatformHookOsx#afterPrefStartupHook} 53 44 */ 54 45 @Test -
test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java
10 10 11 11 import java.io.File; 12 12 import java.io.IOException; 13 import java.security.KeyStore;14 import java.security.KeyStore.TrustedCertificateEntry;15 13 import java.security.KeyStoreException; 16 14 import java.util.Collection; 17 15 … … 19 17 import org.junit.Test; 20 18 import org.openstreetmap.josm.JOSMFixture; 21 19 import org.openstreetmap.josm.Main; 22 import org.openstreetmap.josm.io.remotecontrol.RemoteControlHttpsServer;23 import org.openstreetmap.josm.io.remotecontrol.RemoteControlTest;24 20 25 21 /** 26 22 * Unit tests of {@link PlatformHookWindows} class. … … 65 61 } 66 62 67 63 /** 68 * Test method for {@code PlatformHookWindows#removeInsecureCertificates}69 * @throws Exception if an error occurs70 */71 @Test72 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 occurs88 */89 @Test90 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 /**107 64 * Test method for {@code PlatformHookWindows#afterPrefStartupHook} 108 65 */ 109 66 @Test