- Timestamp:
- 2017-04-17T14:07:04+02:00 (8 years ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/io/CertificateAmendment.java
r11903 r11943 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; … … 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 … … 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 )); 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); 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)) { 91 121 certificateAdded = true; 92 122 } 93 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)) { 131 certificateAdded = true; 132 } 133 } 134 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | IllegalStateException e) { 135 Main.error(e); 94 136 } 95 137 … … 101 143 SSLContext.setDefault(sslContext); 102 144 } 145 } 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; 103 166 } 104 167 -
trunk/src/org/openstreetmap/josm/tools/PlatformHook.java
r11642 r11943 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; 13 14 import org.openstreetmap.josm.io.CertificateAmendment.CertAmend; 12 15 13 16 /** … … 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 -
trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java
r11939 r11943 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; … … 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 … … 351 355 ks.setEntry(entryAlias, trustedCert, null); 352 356 return true; 357 } 358 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; 353 379 } 354 380 -
trunk/test/unit/org/openstreetmap/josm/JOSMFixture.java
r10899 r11943 22 22 import org.openstreetmap.josm.io.OsmApi; 23 23 import org.openstreetmap.josm.tools.I18n; 24 import org.openstreetmap.josm.tools.JosmRuntimeException; 24 25 import org.openstreetmap.josm.tools.Logging; 25 26 … … 111 112 CertificateAmendment.addMissingCertificates(); 112 113 } catch (IOException | GeneralSecurityException ex) { 113 throw new RuntimeException(ex);114 throw new JosmRuntimeException(ex); 114 115 } 115 116 -
trunk/test/unit/org/openstreetmap/josm/io/CertificateAmendmentTest.java
r11923 r11943 26 26 @Rule 27 27 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 28 public JOSMTestRules test = new JOSMTestRules() ;28 public JOSMTestRules test = new JOSMTestRules().platform().https(); 29 29 30 30 /** … … 82 82 } 83 83 84 /** 85 * Test Dutch government. 86 * @throws IOException in case of I/O error 87 */ 88 @Test 89 public void testDutchGovernment() throws IOException { 90 connect("https://geodata.nationaalgeoregister.nl", true); 91 } 92 84 93 private static void connect(String url, boolean shouldWork) throws IOException { 85 94 URLConnection connection = new URL(url).openConnection(); -
trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
r11777 r11943 4 4 import java.io.File; 5 5 import java.io.IOException; 6 import java.security.GeneralSecurityException; 6 7 import java.text.MessageFormat; 7 8 import java.util.TimeZone; … … 17 18 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 18 19 import org.openstreetmap.josm.gui.util.GuiHelper; 20 import org.openstreetmap.josm.io.CertificateAmendment; 19 21 import org.openstreetmap.josm.io.OsmApi; 20 22 import org.openstreetmap.josm.io.OsmApiInitializationException; 21 23 import org.openstreetmap.josm.io.OsmTransferCanceledException; 22 24 import org.openstreetmap.josm.tools.I18n; 25 import org.openstreetmap.josm.tools.JosmRuntimeException; 23 26 import org.openstreetmap.josm.tools.Logging; 24 27 import org.openstreetmap.josm.tools.MemoryManagerTest; … … 46 49 private boolean allowMemoryManagerLeaks; 47 50 private boolean useMapStyles; 51 private boolean useHttps; 48 52 49 53 /** … … 137 141 public JOSMTestRules projection() { 138 142 useProjection = true; 143 return this; 144 } 145 146 /** 147 * Set up HTTPS certificates 148 * @return this instance, for easy chaining 149 */ 150 public JOSMTestRules https() { 151 useHttps = true; 139 152 return this; 140 153 } … … 222 235 } 223 236 237 // Set Platform 238 if (platform) { 239 Main.determinePlatformHook(); 240 } 241 242 if (useHttps) { 243 try { 244 CertificateAmendment.addMissingCertificates(); 245 } catch (IOException | GeneralSecurityException ex) { 246 throw new JosmRuntimeException(ex); 247 } 248 } 249 224 250 if (useProjection) { 225 251 Main.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator … … 241 267 throw new InitializationError(e); 242 268 } 243 }244 245 // Set Platform246 if (platform) {247 Main.determinePlatformHook();248 269 } 249 270
Note:
See TracChangeset
for help on using the changeset viewer.