source: josm/trunk/src/org/openstreetmap/josm/io/CertificateAmendment.java@ 11903

Last change on this file since 11903 was 11903, checked in by stoecker, 7 years ago

drop StartCom as Chrome does no longer trust it even for older certs

  • Property svn:eol-style set to native
File size: 5.1 KB
Line 
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.ByteArrayInputStream;
7import java.io.File;
8import java.io.IOException;
9import java.io.InputStream;
10import java.nio.file.Files;
11import java.nio.file.Path;
12import java.nio.file.Paths;
13import java.security.GeneralSecurityException;
14import java.security.InvalidAlgorithmParameterException;
15import java.security.KeyStore;
16import java.security.KeyStoreException;
17import java.security.MessageDigest;
18import java.security.cert.CertificateFactory;
19import java.security.cert.PKIXParameters;
20import java.security.cert.TrustAnchor;
21import java.security.cert.X509Certificate;
22import java.util.Objects;
23
24import javax.net.ssl.SSLContext;
25import javax.net.ssl.TrustManagerFactory;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.tools.Utils;
29
30/**
31 * Class to add missing root certificates to the list of trusted certificates
32 * for TLS connections.
33 *
34 * The added certificates are deemed trustworthy by the main web browsers and
35 * operating systems, but not included in some distributions of Java.
36 *
37 * The certificates are added in-memory at each start, nothing is written to disk.
38 * @since 9995
39 */
40public final class CertificateAmendment {
41
42 private static final String[] CERT_AMEND = {
43 "resource://data/security/DST_Root_CA_X3.pem"
44 };
45
46 private static final String[] SHA_HASHES = {
47 "0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739"
48 };
49
50 private CertificateAmendment() {
51 // Hide default constructor for utility classes
52 }
53
54 /**
55 * Add missing root certificates to the list of trusted certificates for TLS connections.
56 * @throws IOException if an I/O error occurs
57 * @throws GeneralSecurityException if a security error occurs
58 */
59 public static void addMissingCertificates() throws IOException, GeneralSecurityException {
60 if (!Main.pref.getBoolean("tls.add-missing-certificates", true))
61 return;
62 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
63 Path cacertsPath = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts");
64 try (InputStream is = Files.newInputStream(cacertsPath)) {
65 keyStore.load(is, "changeit".toCharArray());
66 }
67
68 CertificateFactory cf = CertificateFactory.getInstance("X.509");
69 boolean certificateAdded = false;
70 for (int i = 0; i < CERT_AMEND.length; i++) {
71 try (CachedFile certCF = new CachedFile(CERT_AMEND[i])) {
72 byte[] certBytes = certCF.getByteContent();
73 ByteArrayInputStream certIS = new ByteArrayInputStream(certBytes);
74 X509Certificate cert = (X509Certificate) cf.generateCertificate(certIS);
75 MessageDigest md = MessageDigest.getInstance("SHA-256");
76 String sha1 = Utils.toHexString(md.digest(cert.getEncoded()));
77 if (!SHA_HASHES[i].equals(sha1)) {
78 throw new IllegalStateException(
79 tr("Error adding certificate {0} - certificate fingerprint mismatch. Expected {1}, was {2}",
80 CERT_AMEND[i],
81 SHA_HASHES[i],
82 sha1
83 ));
84 }
85 if (certificateIsMissing(keyStore, cert)) {
86 if (Main.isDebugEnabled()) {
87 Main.debug(tr("Adding certificate for TLS connections: {0}", cert.getSubjectX500Principal().getName()));
88 }
89 String alias = "josm:" + new File(CERT_AMEND[i]).getName();
90 keyStore.setCertificateEntry(alias, cert);
91 certificateAdded = true;
92 }
93 }
94 }
95
96 if (certificateAdded) {
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 }
103 }
104
105 /**
106 * Check if the certificate is missing and needs to be added to the keystore.
107 * @param keyStore the keystore
108 * @param crt the certificate
109 * @return true, if the certificate is not contained in the keystore
110 * @throws InvalidAlgorithmParameterException if the keystore does not contain at least one trusted certificate entry
111 * @throws KeyStoreException if the keystore has not been initialized
112 */
113 private static boolean certificateIsMissing(KeyStore keyStore, X509Certificate crt)
114 throws KeyStoreException, InvalidAlgorithmParameterException {
115 PKIXParameters params = new PKIXParameters(keyStore);
116 String id = crt.getSubjectX500Principal().getName();
117 for (TrustAnchor ta : params.getTrustAnchors()) {
118 X509Certificate cert = ta.getTrustedCert();
119 if (Objects.equals(id, cert.getSubjectX500Principal().getName()))
120 return false;
121 }
122 return true;
123 }
124}
Note: See TracBrowser for help on using the repository browser.