Changeset 7343 in josm
- Timestamp:
- 2014-07-28T16:40:19+02:00 (11 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/MainApplication.java
r7335 r7343 445 445 // Check for insecure certificates to remove. 446 446 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) neither startupHook (need to be called before remote control) 447 ((PlatformHookWindows)Main.platform).removeInsecureCertificates();447 PlatformHookWindows.removeInsecureCertificates(); 448 448 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) { 449 449 error(e); -
trunk/src/org/openstreetmap/josm/gui/preferences/remotecontrol/RemoteControlPreference.java
r7335 r7343 9 9 import java.awt.event.ActionEvent; 10 10 import java.awt.event.ActionListener; 11 import java.io.IOException; 12 import java.security.GeneralSecurityException; 13 import java.security.KeyStore; 14 import java.security.KeyStoreException; 15 import java.security.NoSuchAlgorithmException; 16 import java.security.cert.CertificateException; 11 17 import java.util.LinkedHashMap; 12 18 import java.util.Map; … … 15 21 import javax.swing.BorderFactory; 16 22 import javax.swing.Box; 23 import javax.swing.JButton; 17 24 import javax.swing.JCheckBox; 18 25 import javax.swing.JLabel; 26 import javax.swing.JOptionPane; 19 27 import javax.swing.JPanel; 20 28 import javax.swing.JSeparator; … … 31 39 import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler; 32 40 import org.openstreetmap.josm.tools.GBC; 41 import org.openstreetmap.josm.tools.PlatformHookWindows; 33 42 34 43 /** … … 61 70 private JCheckBox enableRemoteControl; 62 71 private JCheckBox enableHttpsSupport; 63 private JCheckBox loadInNewLayer = new JCheckBox(tr("Download objects to new layer")); 64 private JCheckBox alwaysAskUserConfirm = new JCheckBox(tr("Confirm all Remote Control actions manually")); 72 73 private JButton installCertificate; 74 private JButton uninstallCertificate; 75 76 private final JCheckBox loadInNewLayer = new JCheckBox(tr("Download objects to new layer")); 77 private final JCheckBox alwaysAskUserConfirm = new JCheckBox(tr("Confirm all Remote Control actions manually")); 65 78 66 79 @Override … … 91 104 remote.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 5)); 92 105 93 enableHttpsSupport = new JCheckBox(tr("Enable HTTPS support"), RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.get()); 106 boolean https = RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.get(); 107 108 enableHttpsSupport = new JCheckBox(tr("Enable HTTPS support"), https); 94 109 wrapper.add(enableHttpsSupport, GBC.eol().fill(GBC.HORIZONTAL)); 110 111 // Certificate installation only available on Windows for now, see #10033 112 if (Main.isPlatformWindows()) { 113 installCertificate = new JButton(tr("Install...")); 114 uninstallCertificate = new JButton(tr("Uninstall...")); 115 installCertificate.setToolTipText(tr("Install JOSM localhost certificate to system/browser root keystores")); 116 uninstallCertificate.setToolTipText(tr("Uninstall JOSM localhost certificate from system/browser root keystores")); 117 wrapper.add(new JLabel(tr("Certificate:")), GBC.std().insets(15, 5, 0, 0)); 118 wrapper.add(installCertificate, GBC.std().insets(5, 5, 0, 0)); 119 wrapper.add(uninstallCertificate, GBC.eol().insets(5, 5, 0, 0)); 120 enableHttpsSupport.addActionListener(new ActionListener() { 121 @Override 122 public void actionPerformed(ActionEvent e) { 123 installCertificate.setEnabled(enableHttpsSupport.isSelected()); 124 } 125 }); 126 installCertificate.addActionListener(new ActionListener() { 127 @Override 128 public void actionPerformed(ActionEvent e) { 129 try { 130 boolean changed = RemoteControlHttpsServer.setupPlatform( 131 RemoteControlHttpsServer.loadJosmKeystore()); 132 String msg = changed ? 133 tr("Certificate has been successfully installed.") : 134 tr("Certificate is already installed. Nothing to do."); 135 Main.info(msg); 136 JOptionPane.showMessageDialog(wrapper, msg); 137 } catch (IOException | GeneralSecurityException ex) { 138 Main.error(ex); 139 } 140 } 141 }); 142 uninstallCertificate.addActionListener(new ActionListener() { 143 @Override 144 public void actionPerformed(ActionEvent e) { 145 try { 146 String msg; 147 KeyStore ks = PlatformHookWindows.getRootKeystore(); 148 if (ks.containsAlias(RemoteControlHttpsServer.ENTRY_ALIAS)) { 149 Main.info(tr("Removing certificate {0} from root keystore.", RemoteControlHttpsServer.ENTRY_ALIAS)); 150 ks.deleteEntry(RemoteControlHttpsServer.ENTRY_ALIAS); 151 msg = tr("Certificate has been successfully uninstalled."); 152 } else { 153 msg = tr("Certificate is not installed. Nothing to do."); 154 } 155 Main.info(msg); 156 JOptionPane.showMessageDialog(wrapper, msg); 157 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException ex) { 158 Main.error(ex); 159 } 160 } 161 }); 162 installCertificate.setEnabled(https); 163 } 164 95 165 wrapper.add(new JSeparator(), GBC.eop().fill(GBC.HORIZONTAL).insets(15, 5, 15, 5)); 96 166 … … 116 186 // 'setEnabled(false)' does not work for JLabel with html text, so do it manually 117 187 // FIXME: use QuadStateCheckBox to make checkboxes unset when disabled 188 if (installCertificate != null && uninstallCertificate != null) { 189 // Install certificate button is enabled if HTTPS is also enabled 190 installCertificate.setEnabled(enableRemoteControl.isSelected() && enableHttpsSupport.isSelected()); 191 // Uninstall certificate button is always enabled 192 uninstallCertificate.setEnabled(true); 193 } 118 194 } 119 195 }; -
trunk/src/org/openstreetmap/josm/io/remotecontrol/RemoteControlHttpsServer.java
r7338 r7343 21 21 import java.security.KeyPairGenerator; 22 22 import java.security.KeyStore; 23 import java.security.KeyStoreException; 23 24 import java.security.NoSuchAlgorithmException; 24 25 import java.security.PrivateKey; 25 26 import java.security.SecureRandom; 26 27 import java.security.cert.Certificate; 28 import java.security.cert.CertificateException; 27 29 import java.security.cert.X509Certificate; 28 30 import java.util.Arrays; … … 91 93 * @since 7335 92 94 */ 93 public StringProperty KEYSTORE_PASSWORD = new StringProperty("remotecontrol.https.keystore.password", ""); 95 public static final StringProperty KEYSTORE_PASSWORD = new StringProperty("remotecontrol.https.keystore.password", ""); 94 96 95 97 /** … … 97 99 * @since 7335 98 100 */ 99 public StringProperty KEYENTRY_PASSWORD = new StringProperty("remotecontrol.https.keyentry.password", ""); 101 public static final StringProperty KEYENTRY_PASSWORD = new StringProperty("remotecontrol.https.keyentry.password", ""); 102 103 /** 104 * Unique alias used to store JOSM localhost entry, both in JOSM keystore and system/browser keystores. 105 * @since 7343 106 */ 107 public static final String ENTRY_ALIAS = "josm_localhost"; 100 108 101 109 /** … … 194 202 } 195 203 204 /** 205 * Setup the JOSM internal keystore, used to store HTTPS certificate and private key. 206 * @return Path to the (initialized) JOSM keystore 207 * @throws IOException if an I/O error occurs 208 * @throws GeneralSecurityException if a security error occurs 209 * @since 7343 210 */ 211 public static Path setupJosmKeystore() throws IOException, GeneralSecurityException { 212 213 char[] storePassword = KEYSTORE_PASSWORD.get().toCharArray(); 214 char[] entryPassword = KEYENTRY_PASSWORD.get().toCharArray(); 215 216 Path dir = Paths.get(RemoteControl.getRemoteControlDir()); 217 Path path = dir.resolve(KEYSTORE_FILENAME); 218 Files.createDirectories(dir); 219 220 if (!Files.exists(path)) { 221 Main.debug("No keystore found, creating a new one"); 222 223 // Create new keystore like previous one generated with JDK keytool as follows: 224 // keytool -genkeypair -storepass josm_ssl -keypass josm_ssl -alias josm_localhost -dname "CN=localhost, OU=JOSM, O=OpenStreetMap" 225 // -ext san=ip:127.0.0.1 -keyalg RSA -validity 1825 226 227 KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); 228 generator.initialize(2048); 229 KeyPair pair = generator.generateKeyPair(); 230 231 X509Certificate cert = generateCertificate("CN=localhost, OU=JOSM, O=OpenStreetMap", pair, 1825, "SHA256withRSA", 232 "dns:localhost,ip:127.0.0.1,ip:::1,uri:https://127.0.0.1:"+HTTPS_PORT+",uri:https://::1:"+HTTPS_PORT); 233 234 KeyStore ks = KeyStore.getInstance("JKS"); 235 ks.load(null, null); 236 237 // Generate new passwords. See https://stackoverflow.com/a/41156/2257172 238 SecureRandom random = new SecureRandom(); 239 KEYSTORE_PASSWORD.put(new BigInteger(130, random).toString(32)); 240 KEYENTRY_PASSWORD.put(new BigInteger(130, random).toString(32)); 241 242 storePassword = KEYSTORE_PASSWORD.get().toCharArray(); 243 entryPassword = KEYENTRY_PASSWORD.get().toCharArray(); 244 245 ks.setKeyEntry(ENTRY_ALIAS, pair.getPrivate(), entryPassword, new Certificate[]{cert}); 246 ks.store(Files.newOutputStream(path, StandardOpenOption.CREATE), storePassword); 247 } 248 return path; 249 } 250 251 /** 252 * Loads the JOSM keystore. 253 * @return the (initialized) JOSM keystore 254 * @throws IOException if an I/O error occurs 255 * @throws GeneralSecurityException if a security error occurs 256 * @since 7343 257 */ 258 public static KeyStore loadJosmKeystore() throws IOException, GeneralSecurityException { 259 try (InputStream in = Files.newInputStream(setupJosmKeystore())) { 260 KeyStore ks = KeyStore.getInstance("JKS"); 261 ks.load(in, KEYSTORE_PASSWORD.get().toCharArray()); 262 263 if (Main.isDebugEnabled()) { 264 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) { 265 Main.debug("Alias in JOSM keystore: "+aliases.nextElement()); 266 } 267 } 268 return ks; 269 } 270 } 271 196 272 private void initialize() { 197 273 if (!initOK) { 198 274 try { 199 char[] storePassword = KEYSTORE_PASSWORD.get().toCharArray(); 200 char[] entryPassword = KEYENTRY_PASSWORD.get().toCharArray(); 201 202 Path dir = Paths.get(RemoteControl.getRemoteControlDir()); 203 Path path = dir.resolve(KEYSTORE_FILENAME); 204 Files.createDirectories(dir); 205 206 if (!Files.exists(path)) { 207 Main.debug("No keystore found, creating a new one"); 208 209 // Create new keystore like previous one generated with JDK keytool as follows: 210 // keytool -genkeypair -storepass josm_ssl -keypass josm_ssl -alias josm_localhost -dname "CN=localhost, OU=JOSM, O=OpenStreetMap" 211 // -ext san=ip:127.0.0.1 -keyalg RSA -validity 1825 212 213 KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); 214 generator.initialize(2048); 215 KeyPair pair = generator.generateKeyPair(); 216 217 X509Certificate cert = generateCertificate("CN=localhost, OU=JOSM, O=OpenStreetMap", pair, 1825, "SHA256withRSA", 218 "dns:localhost,ip:127.0.0.1,ip:::1,uri:https://127.0.0.1:"+HTTPS_PORT+",uri:https://::1:"+HTTPS_PORT); 219 220 KeyStore ks = KeyStore.getInstance("JKS"); 221 ks.load(null, null); 222 223 // Generate new passwords. See https://stackoverflow.com/a/41156/2257172 224 SecureRandom random = new SecureRandom(); 225 KEYSTORE_PASSWORD.put(new BigInteger(130, random).toString(32)); 226 KEYENTRY_PASSWORD.put(new BigInteger(130, random).toString(32)); 227 228 storePassword = KEYSTORE_PASSWORD.get().toCharArray(); 229 entryPassword = KEYENTRY_PASSWORD.get().toCharArray(); 230 231 ks.setKeyEntry("josm_localhost", pair.getPrivate(), entryPassword, new Certificate[]{cert}); 232 ks.store(Files.newOutputStream(path, StandardOpenOption.CREATE), storePassword); 233 } 234 235 try (InputStream in = Files.newInputStream(path)) { 236 // Load keystore 237 KeyStore ks = KeyStore.getInstance("JKS"); 238 ks.load(in, storePassword); 239 240 if (Main.isDebugEnabled()) { 241 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) { 242 Main.debug("Alias in keystore: "+aliases.nextElement()); 243 } 244 } 245 246 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 247 kmf.init(ks, entryPassword); 248 249 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 250 tmf.init(ks); 251 252 sslContext = SSLContext.getInstance("TLS"); 253 sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 254 255 if (Main.isTraceEnabled()) { 256 Main.trace("SSL Context protocol: " + sslContext.getProtocol()); 257 Main.trace("SSL Context provider: " + sslContext.getProvider()); 258 } 259 260 Enumeration<String> aliases = ks.aliases(); 261 if (aliases.hasMoreElements()) { 262 Main.platform.setupHttpsCertificate(new KeyStore.TrustedCertificateEntry(ks.getCertificate(aliases.nextElement()))); 263 } 264 265 initOK = true; 266 } 275 KeyStore ks = loadJosmKeystore(); 276 277 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 278 kmf.init(ks, KEYENTRY_PASSWORD.get().toCharArray()); 279 280 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 281 tmf.init(ks); 282 283 sslContext = SSLContext.getInstance("TLS"); 284 sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 285 286 if (Main.isTraceEnabled()) { 287 Main.trace("SSL Context protocol: " + sslContext.getProtocol()); 288 Main.trace("SSL Context provider: " + sslContext.getProvider()); 289 } 290 291 setupPlatform(ks); 292 293 initOK = true; 267 294 } catch (IOException | GeneralSecurityException e) { 268 295 Main.error(e); 269 296 } 270 297 } 298 } 299 300 /** 301 * Setup the platform-dependant certificate stuff. 302 * @param josmKs The JOSM keystore, containing localhost certificate and private key. 303 * @return {@code true} if something has changed as a result of the call (certificate installation, etc.) 304 * @throws KeyStoreException if the keystore has not been initialized (loaded) 305 * @throws NoSuchAlgorithmException in case of error 306 * @throws CertificateException in case of error 307 * @throws IOException in case of error 308 * @since 7343 309 */ 310 public static boolean setupPlatform(KeyStore josmKs) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { 311 Enumeration<String> aliases = josmKs.aliases(); 312 if (aliases.hasMoreElements()) { 313 return Main.platform.setupHttpsCertificate(ENTRY_ALIAS, 314 new KeyStore.TrustedCertificateEntry(josmKs.getCertificate(aliases.nextElement()))); 315 } 316 return false; 271 317 } 272 318 -
trunk/src/org/openstreetmap/josm/tools/PlatformHook.java
r7335 r7343 109 109 /** 110 110 * Setup system keystore to add JOSM HTTPS certificate (for remote control). 111 * @param entryAlias The entry alias to use 111 112 * @param trustedCert the JOSM certificate for localhost 113 * @return {@code true} if something has changed as a result of the call (certificate installation, etc.) 112 114 * @throws KeyStoreException in case of error 113 115 * @throws IOException in case of error 114 116 * @throws CertificateException in case of error 115 117 * @throws NoSuchAlgorithmException in case of error 116 * @since 7 206118 * @since 7343 117 119 */ 118 public voidsetupHttpsCertificate(KeyStore.TrustedCertificateEntry trustedCert)120 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert) 119 121 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException; 120 122 } -
trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java
r7335 r7343 358 358 359 359 @Override 360 public voidsetupHttpsCertificate(KeyStore.TrustedCertificateEntry trustedCert)360 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert) 361 361 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { 362 362 // TODO setup HTTPS certificate on Unix systems 363 return false; 363 364 } 364 365 } -
trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java
r7342 r7343 181 181 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given 182 182 * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT" 183 * @since 7343 183 184 */ 184 p rivateKeyStore getWindowsKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {185 public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException { 185 186 KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT); 186 187 ks.load(null, null); … … 196 197 * @since 7335 197 198 */ 198 public void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { 199 public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { 199 200 // 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) 200 201 PublicKey insecurePubKey = null; … … 205 206 return; 206 207 } 207 KeyStore ks = get WindowsKeystore();208 KeyStore ks = getRootKeystore(); 208 209 Enumeration<String> en = ks.aliases(); 209 210 Collection<String> insecureCertificates = new ArrayList<>(); … … 249 250 250 251 @Override 251 public voidsetupHttpsCertificate(KeyStore.TrustedCertificateEntry trustedCert)252 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert) 252 253 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { 253 KeyStore ks = getWindowsKeystore(); 254 Enumeration<String> en = ks.aliases(); 255 256 while (en.hasMoreElements()) { 257 String alias = en.nextElement(); 258 // Look for certificate to install 259 if (ks.getCertificate(alias).equals(trustedCert.getTrustedCertificate())) { 260 // JOSM certificate found, return 261 Main.debug("JOSM certificate found: "+alias); 262 return; 263 } 254 KeyStore ks = getRootKeystore(); 255 // Look for certificate to install 256 String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate()); 257 if (alias != null) { 258 // JOSM certificate found, return 259 Main.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias)); 260 return false; 264 261 } 265 262 // JOSM certificate not found, install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox 266 263 Main.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT)); 267 ks.setEntry("josm_localhost", trustedCert, null); 264 ks.setEntry(entryAlias, trustedCert, null); 265 return true; 268 266 } 269 267 }
Note:
See TracChangeset
for help on using the changeset viewer.