Ignore:
Timestamp:
2014-07-28T16:40:19+02:00 (10 years ago)
Author:
Don-vip
Message:

see #10230, see #10033 - add "Install/uninstall certificate" buttons in remote control preferences (Windows only)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/remotecontrol/RemoteControlHttpsServer.java

    r7338 r7343  
    2121import java.security.KeyPairGenerator;
    2222import java.security.KeyStore;
     23import java.security.KeyStoreException;
    2324import java.security.NoSuchAlgorithmException;
    2425import java.security.PrivateKey;
    2526import java.security.SecureRandom;
    2627import java.security.cert.Certificate;
     28import java.security.cert.CertificateException;
    2729import java.security.cert.X509Certificate;
    2830import java.util.Arrays;
     
    9193     * @since 7335
    9294     */
    93     public StringProperty KEYSTORE_PASSWORD = new StringProperty("remotecontrol.https.keystore.password", "");
     95    public static final StringProperty KEYSTORE_PASSWORD = new StringProperty("remotecontrol.https.keystore.password", "");
    9496
    9597    /**
     
    9799     * @since 7335
    98100     */
    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";
    100108
    101109    /**
     
    194202    }
    195203
     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
    196272    private void initialize() {
    197273        if (!initOK) {
    198274            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;
    267294            } catch (IOException | GeneralSecurityException e) {
    268295                Main.error(e);
    269296            }
    270297        }
     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;
    271317    }
    272318
Note: See TracChangeset for help on using the changeset viewer.