Ticket #12264: certificate-amendment.diff

File certificate-amendment.diff, 6.9 KB (added by bastiK, 10 years ago)
  • src/org/openstreetmap/josm/io/CertificateAmendment.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.io;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.io.File;
     7import java.io.FileInputStream;
     8import java.io.IOException;
     9import java.io.InputStream;
     10import java.nio.file.Paths;
     11import java.security.InvalidAlgorithmParameterException;
     12import java.security.KeyManagementException;
     13import java.security.KeyStore;
     14import java.security.KeyStoreException;
     15import java.security.NoSuchAlgorithmException;
     16import java.security.cert.CertificateException;
     17import java.security.cert.CertificateFactory;
     18import java.security.cert.PKIXParameters;
     19import java.security.cert.TrustAnchor;
     20import java.security.cert.X509Certificate;
     21import java.util.Arrays;
     22import java.util.Collection;
     23import java.util.List;
     24import java.util.Objects;
     25
     26import javax.net.ssl.SSLContext;
     27import javax.net.ssl.TrustManagerFactory;
     28
     29import org.openstreetmap.josm.Main;
     30
     31/**
     32 * Class to add missing root certificates to the list of trusted certificates
     33 * for TLS connections.
     34 *
     35 * The added certificates are deemed trustworthy by the main web browsers and
     36 * operating systems, but not included in some distributions of Java.
     37 */
     38public class CertificateAmendment {
     39
     40    public static final List<String> DEFAULT_CERT_AMEND = Arrays.asList(
     41            "/etc/ssl/certs/DST_Root_CA_X3.pem");       // FIXME: ship file and change to resource://...
     42    public static final Collection<String> CERT_AMEND =
     43            Main.pref.getCollection("tls.trusted-certificates.amendments", DEFAULT_CERT_AMEND);
     44
     45    /**
     46     * Add missing root certificates to the list of trusted certificates for TLS connections.
     47     * @throws IOException if an I/O error occurs
     48     */
     49    public static void addMissingCertificates() throws IOException {
     50        KeyStore keyStore;
     51        try {
     52            keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
     53        } catch (KeyStoreException ex) {
     54            throw new IOException(ex);
     55        }
     56        String cacertsPath = Main.pref.get("tls.cacerts-file",
     57                Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts").toString());
     58        String password = Main.pref.get("tls.cacerts.pw", "changeit");
     59        try {
     60            keyStore.load(new FileInputStream(cacertsPath), password.toCharArray());
     61        } catch (NoSuchAlgorithmException ex) {
     62            throw new RuntimeException(ex);
     63        } catch (CertificateException ex) {
     64            throw new IOException(ex);
     65        }
     66
     67        boolean certificateAdded = false;
     68        for (String certPath : CERT_AMEND) {
     69            CertificateFactory cf;
     70            try {
     71                cf = CertificateFactory.getInstance("X.509");
     72            } catch (CertificateException ex) {
     73                throw new RuntimeException(ex);
     74            }
     75            try (CachedFile certCF = new CachedFile(certPath); InputStream certIS = certCF.getInputStream()) {
     76                X509Certificate cert;
     77                try {
     78                    cert = (X509Certificate) cf.generateCertificate(certIS);
     79                } catch (CertificateException ex) {
     80                    throw new IOException(ex);
     81                }
     82                if (certificateIsMissing(keyStore, cert)) {
     83                    Main.debug(tr("Adding certificate for TLS connections: {0}", cert.getSubjectX500Principal().getName()));
     84                    String alias = "josm:" + new File(certPath).getName();
     85                    try {
     86                        keyStore.setCertificateEntry(alias, cert);
     87                    } catch (KeyStoreException ex) {
     88                        throw new AssertionError(ex);
     89                    }
     90                    certificateAdded = true;
     91                }
     92            }
     93        }
     94
     95        if (certificateAdded) {
     96            try {
     97                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
     98                tmf.init(keyStore);
     99                SSLContext sslContext = SSLContext.getInstance("TLS");
     100                sslContext.init(null, tmf.getTrustManagers(), null);
     101                SSLContext.setDefault(sslContext);
     102            } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException ex) {
     103                throw new RuntimeException(ex);
     104            }
     105        }
     106    }
     107
     108    /**
     109     * Check if the certificate is missing and needs to be added to the keystore.
     110     * @param keyStore the keystore
     111     * @param crt the certificate
     112     * @return true, if the certificate is not contained in the keystore
     113     */
     114    private static boolean certificateIsMissing(KeyStore keyStore, X509Certificate crt) {
     115        String id = crt.getSubjectX500Principal().getName();
     116        PKIXParameters params;
     117        try {
     118            params = new PKIXParameters(keyStore);
     119        } catch (KeyStoreException ex) {
     120            throw new AssertionError(ex);
     121        } catch (InvalidAlgorithmParameterException ex) {
     122            throw new RuntimeException(ex);
     123        }
     124        for (TrustAnchor ta : params.getTrustAnchors()) {
     125            X509Certificate cert = ta.getTrustedCert();
     126            if (Objects.equals(id, cert.getSubjectX500Principal().getName()))
     127                return false;
     128        }
     129        return true;
     130    }
     131}
  • src/org/openstreetmap/josm/gui/MainApplication.java

    Property changes on: src/org/openstreetmap/josm/io/CertificateAmendment.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    5757import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
    5858import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
    5959import org.openstreetmap.josm.gui.util.GuiHelper;
     60import org.openstreetmap.josm.io.CertificateAmendment;
    6061import org.openstreetmap.josm.io.DefaultProxySelector;
    6162import org.openstreetmap.josm.io.MessageNotifier;
    6263import org.openstreetmap.josm.io.OnlineResource;
     
    400401            }
    401402        }
    402403
     404        try {
     405            CertificateAmendment.addMissingCertificates();
     406        } catch (IOException ex) {
     407            ex.printStackTrace();
     408            Main.warn(getErrorMessage(Utils.getRootCause(ex)));
     409        }
    403410        Authenticator.setDefault(DefaultAuthenticator.getInstance());
    404411        DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
    405412        ProxySelector.setDefault(proxySelector);