source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/RemoteControlHttpsServer.java@ 7206

Last change on this file since 7206 was 7206, checked in by Don-vip, 10 years ago

see #10033 - allow remote control to work from osm.org in https on Windows systems by adding updated JOSM localhost certificate to Windows Root Certificates keystore

File size: 9.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.io.IOException;
8import java.io.InputStream;
9import java.net.BindException;
10import java.net.InetAddress;
11import java.net.ServerSocket;
12import java.net.Socket;
13import java.net.SocketException;
14import java.security.Key;
15import java.security.KeyManagementException;
16import java.security.KeyStore;
17import java.security.KeyStoreException;
18import java.security.NoSuchAlgorithmException;
19import java.security.PrivateKey;
20import java.security.UnrecoverableEntryException;
21import java.security.cert.Certificate;
22import java.security.cert.CertificateException;
23import java.util.Arrays;
24import java.util.Enumeration;
25
26import javax.net.ssl.KeyManagerFactory;
27import javax.net.ssl.SSLContext;
28import javax.net.ssl.SSLServerSocket;
29import javax.net.ssl.SSLServerSocketFactory;
30import javax.net.ssl.SSLSocket;
31import javax.net.ssl.TrustManagerFactory;
32
33import org.openstreetmap.josm.Main;
34
35/**
36 * Simple HTTPS server that spawns a {@link RequestProcessor} for every secure connection.
37 *
38 * @since 6941
39 */
40public class RemoteControlHttpsServer extends Thread {
41
42 /** The server socket */
43 private ServerSocket server;
44
45 private static RemoteControlHttpsServer instance;
46 private boolean initOK = false;
47 private SSLContext sslContext;
48
49 private static final String KEYSTORE_PATH = "/data/josm.keystore";
50 private static final String KEYSTORE_PASSWORD = "josm_ssl";
51
52 private void initialize() {
53 if (!initOK) {
54 try {
55 // Create new keystore
56 KeyStore ks = KeyStore.getInstance("JKS");
57 char[] password = KEYSTORE_PASSWORD.toCharArray();
58
59 // Load keystore generated with Java 7 keytool as follows:
60 // keytool -genkeypair -storepass josm_ssl -keypass josm_ssl -alias josm_localhost -dname "CN=localhost, OU=JOSM, O=OpenStreetMap"
61 // -ext san=ip:127.0.0.1 -keyalg RSA -validity 1825
62 try (InputStream in = RemoteControlHttpsServer.class.getResourceAsStream(KEYSTORE_PATH)) {
63 if (in == null) {
64 Main.error(tr("Unable to find JOSM keystore at {0}. Remote control will not be available on HTTPS.", KEYSTORE_PATH));
65 } else {
66 ks.load(in, password);
67
68 if (Main.isDebugEnabled()) {
69 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) {
70 Main.debug("Alias in keystore: "+aliases.nextElement());
71 }
72 }
73
74 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
75 kmf.init(ks, password);
76
77 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
78 tmf.init(ks);
79
80 sslContext = SSLContext.getInstance("TLS");
81 sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
82
83 if (Main.isDebugEnabled()) {
84 Main.debug("SSL Context protocol: " + sslContext.getProtocol());
85 Main.debug("SSL Context provider: " + sslContext.getProvider());
86 }
87
88 Enumeration<String> aliases = ks.aliases();
89 if (aliases.hasMoreElements()) {
90 String aliasKey = aliases.nextElement();
91 Key key = ks.getKey(aliasKey, password);
92 Certificate[] chain = ks.getCertificateChain(aliasKey);
93 Main.platform.setupHttpsCertificate(new KeyStore.PrivateKeyEntry((PrivateKey) key, chain));
94 }
95
96 initOK = true;
97 }
98 }
99 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
100 IOException | KeyManagementException | UnrecoverableEntryException e) {
101 Main.error(e);
102 }
103 }
104 }
105
106 /**
107 * Starts or restarts the HTTPS server
108 */
109 public static void restartRemoteControlHttpsServer() {
110 int port = Main.pref.getInteger("remote.control.https.port", 8112);
111 try {
112 stopRemoteControlHttpsServer();
113
114 instance = new RemoteControlHttpsServer(port);
115 if (instance.initOK) {
116 instance.start();
117 }
118 } catch (BindException ex) {
119 Main.warn(marktr("Cannot start remotecontrol https server on port {0}: {1}"),
120 Integer.toString(port), ex.getLocalizedMessage());
121 } catch (IOException ioe) {
122 Main.error(ioe);
123 } catch (NoSuchAlgorithmException e) {
124 Main.error(e);
125 }
126 }
127
128 /**
129 * Stops the HTTPS server
130 */
131 public static void stopRemoteControlHttpsServer() {
132 if (instance != null) {
133 try {
134 instance.stopServer();
135 instance = null;
136 } catch (IOException ioe) {
137 Main.error(ioe);
138 }
139 }
140 }
141
142 /**
143 * Constructs a new {@code RemoteControlHttpsServer}.
144 * @param port The port this server will listen on
145 * @throws IOException when connection errors
146 * @throws NoSuchAlgorithmException if the JVM does not support TLS (can not happen)
147 */
148 public RemoteControlHttpsServer(int port) throws IOException, NoSuchAlgorithmException {
149 super("RemoteControl HTTPS Server");
150 this.setDaemon(true);
151
152 initialize();
153
154 // Create SSL Server factory
155 SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
156 if (Main.isDebugEnabled()) {
157 Main.debug("SSL factory - Supported Cipher suites: "+Arrays.toString(factory.getSupportedCipherSuites()));
158 }
159
160 // Start the server socket with only 1 connection.
161 // Also make sure we only listen
162 // on the local interface so nobody from the outside can connect!
163 // NOTE: On a dual stack machine with old Windows OS this may not listen on both interfaces!
164 this.server = factory.createServerSocket(port, 1,
165 InetAddress.getByName(Main.pref.get("remote.control.host", "localhost")));
166
167 if (Main.isDebugEnabled() && server instanceof SSLServerSocket) {
168 SSLServerSocket sslServer = (SSLServerSocket) server;
169 Main.debug("SSL server - Enabled Cipher suites: "+Arrays.toString(sslServer.getEnabledCipherSuites()));
170 Main.debug("SSL server - Enabled Protocols: "+Arrays.toString(sslServer.getEnabledProtocols()));
171 Main.debug("SSL server - Enable Session Creation: "+sslServer.getEnableSessionCreation());
172 Main.debug("SSL server - Need Client Auth: "+sslServer.getNeedClientAuth());
173 Main.debug("SSL server - Want Client Auth: "+sslServer.getWantClientAuth());
174 Main.debug("SSL server - Use Client Mode: "+sslServer.getUseClientMode());
175 }
176 }
177
178 /**
179 * The main loop, spawns a {@link RequestProcessor} for each connection.
180 */
181 @Override
182 public void run() {
183 Main.info(marktr("RemoteControl::Accepting secure connections on port {0}"),
184 Integer.toString(server.getLocalPort()));
185 while (true) {
186 try {
187 @SuppressWarnings("resource")
188 Socket request = server.accept();
189 if (Main.isDebugEnabled() && request instanceof SSLSocket) {
190 SSLSocket sslSocket = (SSLSocket) request;
191 Main.debug("SSL socket - Enabled Cipher suites: "+Arrays.toString(sslSocket.getEnabledCipherSuites()));
192 Main.debug("SSL socket - Enabled Protocols: "+Arrays.toString(sslSocket.getEnabledProtocols()));
193 Main.debug("SSL socket - Enable Session Creation: "+sslSocket.getEnableSessionCreation());
194 Main.debug("SSL socket - Need Client Auth: "+sslSocket.getNeedClientAuth());
195 Main.debug("SSL socket - Want Client Auth: "+sslSocket.getWantClientAuth());
196 Main.debug("SSL socket - Use Client Mode: "+sslSocket.getUseClientMode());
197 Main.debug("SSL socket - Session: "+sslSocket.getSession());
198 }
199 RequestProcessor.processRequest(request);
200 } catch (SocketException se) {
201 if (!server.isClosed()) {
202 Main.error(se);
203 }
204 } catch (IOException ioe) {
205 Main.error(ioe);
206 }
207 }
208 }
209
210 /**
211 * Stops the HTTPS server.
212 *
213 * @throws IOException if any I/O error occurs
214 */
215 public void stopServer() throws IOException {
216 server.close();
217 Main.info(marktr("RemoteControl::Server (https) stopped."));
218 }
219}
Note: See TracBrowser for help on using the repository browser.