Ticket #14649: 14649.diff
File 14649.diff, 10.6 KB (added by , 8 years ago) |
---|
-
src/org/openstreetmap/josm/io/CertificateAmendment.java
15 15 import java.security.KeyStore; 16 16 import java.security.KeyStoreException; 17 17 import java.security.MessageDigest; 18 import java.security.NoSuchAlgorithmException; 19 import java.security.cert.CertificateEncodingException; 20 import java.security.cert.CertificateException; 18 21 import java.security.cert.CertificateFactory; 19 22 import java.security.cert.PKIXParameters; 20 23 import java.security.cert.TrustAnchor; … … 39 42 */ 40 43 public final class CertificateAmendment { 41 44 42 private static final String[] CERT_AMEND = { 43 "resource://data/security/DST_Root_CA_X3.pem" 45 /** 46 * A certificate amendment. 47 * @since 11940 48 */ 49 public static class CertAmend { 50 private final String id; 51 private final String sha256; 52 53 CertAmend(String path, String sha256) { 54 this.id = path; 55 this.sha256 = sha256; 56 } 57 58 /** 59 * Returns the certificate identifier. 60 * @return path for JOSM embedded certificate, alias for platform certificate 61 */ 62 public final String getId() { 63 return id; 64 } 65 66 /** 67 * Returns the SHA-256 hash. 68 * @return the SHA-256 hash, in hexadecimal 69 */ 70 public final String getSha256() { 71 return sha256; 72 } 73 } 74 75 /** 76 * Certificates embedded in JOSM 77 */ 78 private static final CertAmend[] CERT_AMEND = { 79 new CertAmend("resource://data/security/DST_Root_CA_X3.pem", 80 "0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739") 44 81 }; 45 82 46 private static final String[] SHA_HASHES = { 47 "0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739" 83 /** 84 * Certificates looked into platform native keystore and not embedded in JOSM. 85 * Identifiers must match Windows keystore aliases for efficient search. 86 */ 87 private static final CertAmend[] PLATFORM_CERT_AMEND = { 88 new CertAmend("Staat der Nederlanden Root CA - G2", 89 "668c83947da63b724bece1743c31a0e6aed0db8ec5b31be377bb784f91b6716f"), 90 new CertAmend("Government of Netherlands G3", 91 "3c4fb0b95ab8b30032f432b86f535fe172c185d0fd39865837cf36187fa6f428") 48 92 }; 49 93 50 94 private CertificateAmendment() { … … 65 109 keyStore.load(is, "changeit".toCharArray()); 66 110 } 67 111 112 MessageDigest md = MessageDigest.getInstance("SHA-256"); 68 113 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 69 114 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 )); 115 // Add embedded certificates. Exit in case of error 116 for (CertAmend certAmend : CERT_AMEND) { 117 try (CachedFile certCF = new CachedFile(certAmend.id)) { 118 X509Certificate cert = (X509Certificate) cf.generateCertificate( 119 new ByteArrayInputStream(certCF.getByteContent())); 120 if (checkAndAddCertificate(md, cert, certAmend, keyStore)) { 121 certificateAdded = true; 84 122 } 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); 123 } 124 } 125 126 try { 127 // Try to add platform certificates. Do not exit in case of error (embedded certificates may be OK) 128 for (CertAmend certAmend : PLATFORM_CERT_AMEND) { 129 X509Certificate cert = Main.platform.getX509Certificate(certAmend); 130 if (checkAndAddCertificate(md, cert, certAmend, keyStore)) { 91 131 certificateAdded = true; 92 132 } 93 133 } 134 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { 135 Main.error(e); 94 136 } 95 137 96 138 if (certificateAdded) { … … 102 144 } 103 145 } 104 146 147 private static boolean checkAndAddCertificate(MessageDigest md, X509Certificate cert, CertAmend certAmend, KeyStore keyStore) 148 throws CertificateEncodingException, KeyStoreException, InvalidAlgorithmParameterException { 149 if (cert != null) { 150 String sha256 = Utils.toHexString(md.digest(cert.getEncoded())); 151 if (!certAmend.sha256.equals(sha256)) { 152 throw new IllegalStateException( 153 tr("Error adding certificate {0} - certificate fingerprint mismatch. Expected {1}, was {2}", 154 certAmend.id, certAmend.sha256, sha256)); 155 } 156 if (certificateIsMissing(keyStore, cert)) { 157 if (Main.isDebugEnabled()) { 158 Main.debug(tr("Adding certificate for TLS connections: {0}", cert.getSubjectX500Principal().getName())); 159 } 160 String alias = "josm:" + new File(certAmend.id).getName(); 161 keyStore.setCertificateEntry(alias, cert); 162 return true; 163 } 164 } 165 return false; 166 } 167 105 168 /** 106 169 * Check if the certificate is missing and needs to be added to the keystore. 107 170 * @param keyStore the keystore -
src/org/openstreetmap/josm/tools/PlatformHook.java
8 8 import java.security.KeyStoreException; 9 9 import java.security.NoSuchAlgorithmException; 10 10 import java.security.cert.CertificateException; 11 import java.security.cert.X509Certificate; 11 12 import java.util.List; 12 13 14 import org.openstreetmap.josm.io.CertificateAmendment.CertAmend; 15 13 16 /** 14 17 * This interface allows platform (operating system) dependent code 15 18 * to be bundled into self-contained classes. … … 153 156 } 154 157 155 158 /** 159 * Returns the {@code X509Certificate} matching the given certificate amendment information. 160 * @param certAmend certificate amendment 161 * @return the {@code X509Certificate} matching the given certificate amendment information, or {@code null} 162 * @throws KeyStoreException in case of error 163 * @throws IOException in case of error 164 * @throws CertificateException in case of error 165 * @throws NoSuchAlgorithmException in case of error 166 * @since 11940 167 */ 168 default X509Certificate getX509Certificate(CertAmend certAmend) 169 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { 170 return null; 171 } 172 173 /** 156 174 * Returns the platform-dependent default cache directory. 157 175 * @return the platform-dependent default cache directory 158 176 * @since 7829 -
src/org/openstreetmap/josm/tools/PlatformHookWindows.java
44 44 import java.security.KeyFactory; 45 45 import java.security.KeyStore; 46 46 import java.security.KeyStoreException; 47 import java.security.MessageDigest; 47 48 import java.security.NoSuchAlgorithmException; 48 49 import java.security.NoSuchProviderException; 49 50 import java.security.PublicKey; 50 51 import java.security.SignatureException; 52 import java.security.cert.Certificate; 51 53 import java.security.cert.CertificateException; 54 import java.security.cert.X509Certificate; 52 55 import java.security.spec.InvalidKeySpecException; 53 56 import java.security.spec.X509EncodedKeySpec; 54 57 import java.util.ArrayList; … … 63 66 64 67 import org.openstreetmap.josm.Main; 65 68 import org.openstreetmap.josm.data.Preferences; 69 import org.openstreetmap.josm.io.CertificateAmendment.CertAmend; 66 70 67 71 /** 68 69 70 72 * {@code PlatformHook} implementation for Microsoft Windows systems. 73 * @since 1023 74 */ 71 75 public class PlatformHookWindows implements PlatformHook { 72 76 73 77 /** … … 353 357 } 354 358 355 359 @Override 360 public X509Certificate getX509Certificate(CertAmend certAmend) 361 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { 362 KeyStore ks = getRootKeystore(); 363 // Search by alias (fast) 364 Certificate result = ks.getCertificate(certAmend.getId()); 365 if (result instanceof X509Certificate) { 366 return (X509Certificate) result; 367 } 368 // If not found, search by SHA-256 (slower) 369 MessageDigest md = MessageDigest.getInstance("SHA-256"); 370 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) { 371 result = ks.getCertificate(aliases.nextElement()); 372 if (result instanceof X509Certificate 373 && certAmend.getSha256().equalsIgnoreCase(Utils.toHexString(md.digest(result.getEncoded())))) { 374 return (X509Certificate) result; 375 } 376 } 377 // Not found 378 return null; 379 } 380 381 @Override 356 382 public File getDefaultCacheDirectory() { 357 383 String p = System.getenv("LOCALAPPDATA"); 358 384 if (p == null || p.isEmpty()) {