Просмотр исходного кода

Merge pull request #809 from csmith/certs

Use HostChecker in CertificateManager.
pull/811/head
Greg Holmes 7 лет назад
Родитель
Сommit
20b0ef1551

+ 10
- 82
src/main/java/com/dmdirc/tls/CertificateManager.java Просмотреть файл

17
 
17
 
18
 package com.dmdirc.tls;
18
 package com.dmdirc.tls;
19
 
19
 
20
+import com.dmdirc.config.provider.AggregateConfigProvider;
21
+import com.dmdirc.config.provider.ConfigProvider;
20
 import com.dmdirc.events.ServerCertificateProblemEncounteredEvent;
22
 import com.dmdirc.events.ServerCertificateProblemEncounteredEvent;
21
 import com.dmdirc.events.ServerCertificateProblemResolvedEvent;
23
 import com.dmdirc.events.ServerCertificateProblemResolvedEvent;
22
-import com.dmdirc.interfaces.Connection;
23
 import com.dmdirc.events.eventbus.EventBus;
24
 import com.dmdirc.events.eventbus.EventBus;
24
-import com.dmdirc.config.provider.AggregateConfigProvider;
25
-import com.dmdirc.config.provider.ConfigProvider;
26
-
25
+import com.dmdirc.interfaces.Connection;
27
 import java.io.FileInputStream;
26
 import java.io.FileInputStream;
28
 import java.io.FileNotFoundException;
27
 import java.io.FileNotFoundException;
29
 import java.io.IOException;
28
 import java.io.IOException;
32
 import java.security.KeyStore;
31
 import java.security.KeyStore;
33
 import java.security.KeyStoreException;
32
 import java.security.KeyStoreException;
34
 import java.security.cert.CertificateException;
33
 import java.security.cert.CertificateException;
35
-import java.security.cert.CertificateParsingException;
36
 import java.security.cert.PKIXParameters;
34
 import java.security.cert.PKIXParameters;
37
 import java.security.cert.TrustAnchor;
35
 import java.security.cert.TrustAnchor;
38
 import java.security.cert.X509Certificate;
36
 import java.security.cert.X509Certificate;
47
 import java.util.Set;
45
 import java.util.Set;
48
 import java.util.concurrent.Semaphore;
46
 import java.util.concurrent.Semaphore;
49
 import java.util.stream.Collectors;
47
 import java.util.stream.Collectors;
50
-
51
 import javax.naming.InvalidNameException;
48
 import javax.naming.InvalidNameException;
52
 import javax.naming.ldap.LdapName;
49
 import javax.naming.ldap.LdapName;
53
 import javax.naming.ldap.Rdn;
50
 import javax.naming.ldap.Rdn;
54
 import javax.net.ssl.KeyManager;
51
 import javax.net.ssl.KeyManager;
55
 import javax.net.ssl.KeyManagerFactory;
52
 import javax.net.ssl.KeyManagerFactory;
56
 import javax.net.ssl.X509TrustManager;
53
 import javax.net.ssl.X509TrustManager;
57
-
58
 import org.slf4j.Logger;
54
 import org.slf4j.Logger;
59
 import org.slf4j.LoggerFactory;
55
 import org.slf4j.LoggerFactory;
60
 
56
 
90
     private final ConfigProvider userSettings;
86
     private final ConfigProvider userSettings;
91
     /** Locator to use to find a system keystore. */
87
     /** Locator to use to find a system keystore. */
92
     private final KeyStoreLocator keyStoreLocator;
88
     private final KeyStoreLocator keyStoreLocator;
89
+    /** Checker to use for hostnames. */
90
+    private final CertificateHostChecker hostChecker;
93
 
91
 
94
     /**
92
     /**
95
      * Creates a new certificate manager for a client connecting to the specified server.
93
      * Creates a new certificate manager for a client connecting to the specified server.
111
         this.userSettings = userSettings;
109
         this.userSettings = userSettings;
112
         this.eventBus = eventBus;
110
         this.eventBus = eventBus;
113
         this.keyStoreLocator = new KeyStoreLocator();
111
         this.keyStoreLocator = new KeyStoreLocator();
112
+        this.hostChecker = new CertificateHostChecker();
114
 
113
 
115
         loadTrustedCAs();
114
         loadTrustedCAs();
116
     }
115
     }
206
         return TrustResult.UNTRUSTED_GENERAL;
205
         return TrustResult.UNTRUSTED_GENERAL;
207
     }
206
     }
208
 
207
 
209
-    /**
210
-     * Determines whether the given certificate has a valid CN or alternate name for this server's
211
-     * hostname.
212
-     *
213
-     * @param certificate The certificate to be validated
214
-     *
215
-     * @return True if the certificate is valid for this server's host, false otherwise
216
-     */
217
-    public boolean isValidHost(final X509Certificate certificate) {
218
-        final Map<String, String> fields = getDNFieldsFromCert(certificate);
219
-        if (fields.containsKey("CN") && isMatchingServerName(fields.get("CN"))) {
220
-            return true;
221
-        }
222
-
223
-        try {
224
-            if (certificate.getSubjectAlternativeNames() != null) {
225
-                for (List<?> entry : certificate.getSubjectAlternativeNames()) {
226
-                    final int type = (Integer) entry.get(0);
227
-
228
-                    // DNS or IP
229
-                    if ((type == 2 || type == 7) && isMatchingServerName((String) entry.get(1))) {
230
-                        return true;
231
-                    }
232
-                }
233
-            }
234
-        } catch (CertificateParsingException ex) {
235
-            return false;
236
-        }
237
-
238
-        return false;
239
-    }
240
-
241
-    /**
242
-     * Checks whether the specified target matches the server name this certificate manager was
243
-     * initialised with.
244
-     *
245
-     * Target names may contain wildcards per RFC2818.
246
-     *
247
-     * @since 0.6.5
248
-     * @param target The target to compare to our server name
249
-     *
250
-     * @return True if the target matches, false otherwise
251
-     */
252
-    protected boolean isMatchingServerName(final String target) {
253
-        final String[] targetParts = target.split("\\.");
254
-        final String[] serverParts = serverName.split("\\.");
255
-
256
-        if (targetParts.length != serverParts.length) {
257
-            // Fail fast if they don't match
258
-            return false;
259
-        }
260
-
261
-        for (int i = 0; i < serverParts.length; i++) {
262
-            if (!serverParts[i].matches("\\Q" + targetParts[i].replace("*", "\\E.*\\Q") + "\\E")) {
263
-                return false;
264
-            }
265
-        }
266
-
267
-        return true;
268
-    }
269
-
270
     @Override
208
     @Override
271
     public void checkServerTrusted(final X509Certificate[] chain, final String authType)
209
     public void checkServerTrusted(final X509Certificate[] chain, final String authType)
272
             throws CertificateException {
210
             throws CertificateException {
273
         this.chain = Arrays.copyOf(chain, chain.length);
211
         this.chain = Arrays.copyOf(chain, chain.length);
274
         problems.clear();
212
         problems.clear();
275
 
213
 
276
-        checkHost(chain);
214
+        if (!hostChecker.isValidFor(chain[0], serverName)) {
215
+            problems.add(new CertificateDoesntMatchHostException(
216
+                    "Certificate was not issued to " + serverName));
217
+        }
277
 
218
 
278
         if (checkIssuer(chain)) {
219
         if (checkIssuer(chain)) {
279
             problems.clear();
220
             problems.clear();
343
         return manual;
284
         return manual;
344
     }
285
     }
345
 
286
 
346
-    /**
347
-     * Checks that the host of the leaf certificate is the same as the server we are connected to.
348
-     *
349
-     * @param chain The chain of certificates to check.
350
-     */
351
-    private void checkHost(final X509Certificate... chain) {
352
-        // Check that the cert is issued to the correct host
353
-        if (!isValidHost(chain[0])) {
354
-            problems.add(new CertificateDoesntMatchHostException(
355
-                    "Certificate was not issued to " + serverName));
356
-        }
357
-    }
358
-
359
     /**
287
     /**
360
      * Gets the chain of certificates currently being validated, if any.
288
      * Gets the chain of certificates currently being validated, if any.
361
      *
289
      *

+ 23
- 17
src/main/java/com/dmdirc/ui/core/dialogs/sslcertificate/SSLCertificateDialogModel.java Просмотреть файл

19
 
19
 
20
 import com.dmdirc.tls.CertificateAction;
20
 import com.dmdirc.tls.CertificateAction;
21
 import com.dmdirc.tls.CertificateDoesntMatchHostException;
21
 import com.dmdirc.tls.CertificateDoesntMatchHostException;
22
+import com.dmdirc.tls.CertificateHostChecker;
22
 import com.dmdirc.tls.CertificateManager;
23
 import com.dmdirc.tls.CertificateManager;
23
 import com.dmdirc.tls.CertificateNotTrustedException;
24
 import com.dmdirc.tls.CertificateNotTrustedException;
24
 
25
 
47
     private final CertificateManager manager;
48
     private final CertificateManager manager;
48
     /** The list of problems found with the certs, if any. */
49
     /** The list of problems found with the certs, if any. */
49
     private final Collection<CertificateException> problems;
50
     private final Collection<CertificateException> problems;
51
+    /** Checker to use for hostnames. */
52
+    private final CertificateHostChecker hostChecker;
50
 
53
 
51
     /**
54
     /**
52
      * Creates a new SSLCertificateDialogModel for the specified chain.
55
      * Creates a new SSLCertificateDialogModel for the specified chain.
61
         this.chain = chain;
64
         this.chain = chain;
62
         this.problems = problems;
65
         this.problems = problems;
63
         this.manager = manager;
66
         this.manager = manager;
67
+        this.hostChecker = new CertificateHostChecker();
64
     }
68
     }
65
 
69
 
66
     /**
70
     /**
75
         boolean first = true;
79
         boolean first = true;
76
 
80
 
77
         for (X509Certificate cert : chain) {
81
         for (X509Certificate cert : chain) {
78
-            boolean invalid = first && !manager.isValidHost(cert);
82
+            boolean invalid = first && !hostChecker.isValidFor(cert, manager.getServerName());
79
             first = false;
83
             first = false;
80
 
84
 
81
             try {
85
             try {
123
                 cert.getNotAfter().toString(), tooOld, false));
127
                 cert.getNotAfter().toString(), tooOld, false));
124
         res.add(group);
128
         res.add(group);
125
 
129
 
126
-        final boolean wrongName = index == 0 && !manager.isValidHost(cert);
130
+        final boolean wrongName = index == 0 && !hostChecker.isValidFor(cert, manager.getServerName());
127
         final String names = getAlternateNames(cert);
131
         final String names = getAlternateNames(cert);
128
         final Map<String, String> fields = CertificateManager.getDNFieldsFromCert(cert);
132
         final Map<String, String> fields = CertificateManager.getDNFieldsFromCert(cert);
129
 
133
 
160
      *
164
      *
161
      * @return A comma-separated list of alternate names
165
      * @return A comma-separated list of alternate names
162
      */
166
      */
163
-    protected String getAlternateNames(final X509Certificate cert) {
167
+    private String getAlternateNames(final X509Certificate cert) {
164
         final StringBuilder res = new StringBuilder();
168
         final StringBuilder res = new StringBuilder();
165
 
169
 
166
         try {
170
         try {
196
      * @param field   The name of the field to look for
200
      * @param field   The name of the field to look for
197
      * @param invalid Whether or not the field is a cause for concern
201
      * @param invalid Whether or not the field is a cause for concern
198
      */
202
      */
199
-    protected void addCertField(final Map<String, String> fields,
200
-            final List<CertificateInformationEntry> group, final String title,
201
-            final String field, final boolean invalid) {
202
-        group.add(new CertificateInformationEntry(title,
203
-                fields.containsKey(field) ? fields.get(field) : NOTPRESENT, invalid,
203
+    private void addCertField(
204
+            final Map<String, String> fields,
205
+            final List<CertificateInformationEntry> group,
206
+            final String title,
207
+            final String field,
208
+            final boolean invalid) {
209
+        group.add(new CertificateInformationEntry(title, fields.getOrDefault(field, NOTPRESENT), invalid,
204
                 !fields.containsKey(field)));
210
                 !fields.containsKey(field)));
205
     }
211
     }
206
 
212
 
212
     public List<CertificateSummaryEntry> getSummary() {
218
     public List<CertificateSummaryEntry> getSummary() {
213
         final List<CertificateSummaryEntry> res = new ArrayList<>();
219
         final List<CertificateSummaryEntry> res = new ArrayList<>();
214
 
220
 
215
-        boolean outofdate = false;
216
-        boolean wronghost = false;
217
-        boolean nottrusted = false;
221
+        boolean outOfDate = false;
222
+        boolean wrongHost = false;
223
+        boolean notTrusted = false;
218
 
224
 
219
         for (CertificateException ex : problems) {
225
         for (CertificateException ex : problems) {
220
             if (ex instanceof CertificateExpiredException
226
             if (ex instanceof CertificateExpiredException
221
                     || ex instanceof CertificateNotYetValidException) {
227
                     || ex instanceof CertificateNotYetValidException) {
222
-                outofdate = true;
228
+                outOfDate = true;
223
             } else if (ex instanceof CertificateDoesntMatchHostException) {
229
             } else if (ex instanceof CertificateDoesntMatchHostException) {
224
-                wronghost = true;
230
+                wrongHost = true;
225
             } else if (ex instanceof CertificateNotTrustedException) {
231
             } else if (ex instanceof CertificateNotTrustedException) {
226
-                nottrusted = true;
232
+                notTrusted = true;
227
             }
233
             }
228
         }
234
         }
229
 
235
 
230
-        if (outofdate) {
236
+        if (outOfDate) {
231
             res.add(new CertificateSummaryEntry("One or more certificates are "
237
             res.add(new CertificateSummaryEntry("One or more certificates are "
232
                     + "not within their validity period", false));
238
                     + "not within their validity period", false));
233
         } else {
239
         } else {
235
                     + "within their validity period", true));
241
                     + "within their validity period", true));
236
         }
242
         }
237
 
243
 
238
-        if (nottrusted) {
244
+        if (notTrusted) {
239
             res.add(new CertificateSummaryEntry("The certificate is not issued "
245
             res.add(new CertificateSummaryEntry("The certificate is not issued "
240
                     + "by a trusted authority", false));
246
                     + "by a trusted authority", false));
241
         } else {
247
         } else {
243
                     + "trusted", true));
249
                     + "trusted", true));
244
         }
250
         }
245
 
251
 
246
-        if (wronghost) {
252
+        if (wrongHost) {
247
             res.add(new CertificateSummaryEntry("The certificate is not issued "
253
             res.add(new CertificateSummaryEntry("The certificate is not issued "
248
                     + "to the host you are connecting to", false));
254
                     + "to the host you are connecting to", false));
249
         } else {
255
         } else {

Загрузка…
Отмена
Сохранить