1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package davmail.http;
20
21 import davmail.BundleMessage;
22 import davmail.Settings;
23 import davmail.ui.AcceptCertificateDialog;
24 import davmail.ui.tray.DavGatewayTray;
25
26 import javax.net.ssl.TrustManager;
27 import javax.net.ssl.TrustManagerFactory;
28 import javax.net.ssl.X509TrustManager;
29 import java.awt.*;
30 import java.io.BufferedReader;
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.security.*;
34 import java.security.cert.CertificateEncodingException;
35 import java.security.cert.CertificateException;
36 import java.security.cert.X509Certificate;
37 import java.text.SimpleDateFormat;
38
39
40
41
42 public class DavGatewayX509TrustManager implements X509TrustManager {
43 private final X509TrustManager standardTrustManager;
44
45
46
47
48
49
50
51 public DavGatewayX509TrustManager() throws NoSuchAlgorithmException, KeyStoreException {
52 TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
53 factory.init((KeyStore) null);
54 TrustManager[] trustManagers = factory.getTrustManagers();
55 if (trustManagers.length == 0) {
56 throw new NoSuchAlgorithmException("No trust manager found");
57 }
58 this.standardTrustManager = (X509TrustManager) trustManagers[0];
59 }
60
61 public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
62 try {
63
64 this.standardTrustManager.checkServerTrusted(x509Certificates, authType);
65 } catch (CertificateException e) {
66 if ((x509Certificates != null) && (x509Certificates.length > 0)) {
67 userCheckServerTrusted(x509Certificates);
68 } else {
69 throw e;
70 }
71 }
72 }
73
74 public void checkClientTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
75 this.standardTrustManager.checkClientTrusted(x509Certificates, authType);
76 }
77
78 public X509Certificate[] getAcceptedIssuers() {
79 return this.standardTrustManager.getAcceptedIssuers();
80 }
81
82 protected void userCheckServerTrusted(final X509Certificate[] x509Certificates) throws CertificateException {
83 String acceptedCertificateHash = Settings.getProperty("davmail.server.certificate.hash");
84 String certificateHash = getFormattedHash(x509Certificates[0]);
85
86 if (acceptedCertificateHash != null && !acceptedCertificateHash.isEmpty()
87 && acceptedCertificateHash.equalsIgnoreCase(certificateHash)) {
88 DavGatewayTray.debug(new BundleMessage("LOG_FOUND_ACCEPTED_CERTIFICATE", acceptedCertificateHash));
89 } else {
90 boolean isCertificateTrusted;
91 if (Settings.getBooleanProperty("davmail.server") || GraphicsEnvironment.isHeadless()) {
92
93 isCertificateTrusted = isCertificateTrusted(x509Certificates[0]);
94 } else {
95 isCertificateTrusted = AcceptCertificateDialog.isCertificateTrusted(x509Certificates[0]);
96 }
97 if (!isCertificateTrusted) {
98 throw new CertificateException("User rejected certificate");
99 }
100
101 Settings.saveProperty("davmail.server.certificate.hash", certificateHash);
102 }
103 }
104
105 @SuppressWarnings({"UseOfSystemOutOrSystemErr"})
106 protected boolean isCertificateTrusted(X509Certificate certificate) {
107 BufferedReader inReader = new BufferedReader(new InputStreamReader(System.in));
108 String answer = null;
109 String yes = BundleMessage.format("UI_ANSWER_YES");
110 String no = BundleMessage.format("UI_ANSWER_NO");
111 StringBuilder buffer = new StringBuilder();
112 buffer.append(BundleMessage.format("UI_SERVER_CERTIFICATE")).append(":\n");
113 buffer.append(BundleMessage.format("UI_ISSUED_TO")).append(": ")
114 .append(DavGatewayX509TrustManager.getRDN(certificate.getSubjectX500Principal())).append('\n');
115 buffer.append(BundleMessage.format("UI_ISSUED_BY")).append(": ")
116 .append(getRDN(certificate.getIssuerX500Principal())).append('\n');
117 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
118 String notBefore = formatter.format(certificate.getNotBefore());
119 buffer.append(BundleMessage.format("UI_VALID_FROM")).append(": ").append(notBefore).append('\n');
120 String notAfter = formatter.format(certificate.getNotAfter());
121 buffer.append(BundleMessage.format("UI_VALID_UNTIL")).append(": ").append(notAfter).append('\n');
122 buffer.append(BundleMessage.format("UI_SERIAL")).append(": ").append(getFormattedSerial(certificate)).append('\n');
123 String sha1Hash = DavGatewayX509TrustManager.getFormattedHash(certificate);
124 buffer.append(BundleMessage.format("UI_FINGERPRINT")).append(": ").append(sha1Hash).append('\n');
125 buffer.append('\n');
126 buffer.append(BundleMessage.format("UI_UNTRUSTED_CERTIFICATE")).append('\n');
127 try {
128 while (!yes.equals(answer) && !no.equals(answer)) {
129 System.out.println(buffer);
130 answer = inReader.readLine();
131 if (answer == null) {
132 answer = no;
133 }
134 answer = answer.toLowerCase();
135 }
136 } catch (IOException e) {
137 System.err.println(e+" "+e.getMessage());
138 }
139 return yes.equals(answer);
140 }
141
142
143
144
145
146
147
148 public static String getRDN(Principal principal) {
149 String dn = principal.getName();
150 int start = dn.indexOf('=');
151 int end = dn.indexOf(',');
152 if (start >= 0 && end >= 0) {
153 return dn.substring(start + 1, end);
154 } else {
155 return dn;
156 }
157 }
158
159
160
161
162
163
164
165 public static String getFormattedSerial(X509Certificate certificate) {
166 StringBuilder builder = new StringBuilder();
167 String serial = certificate.getSerialNumber().toString(16);
168 for (int i = 0; i < serial.length(); i++) {
169 if (i > 0 && i % 2 == 0) {
170 builder.append(' ');
171 }
172 builder.append(serial.charAt(i));
173 }
174 return builder.toString().toUpperCase();
175 }
176
177
178
179
180
181
182
183 public static String getFormattedHash(X509Certificate certificate) {
184 String sha1Hash;
185 try {
186 MessageDigest md = MessageDigest.getInstance("SHA1");
187 byte[] digest = md.digest(certificate.getEncoded());
188 sha1Hash = formatHash(digest);
189 } catch (NoSuchAlgorithmException | CertificateEncodingException nsa) {
190 sha1Hash = nsa.getMessage();
191 }
192 return sha1Hash;
193 }
194
195
196
197
198
199
200
201 protected static String formatHash(byte[] buffer) {
202 StringBuilder builder = new StringBuilder();
203 for (int i = 0; i < buffer.length; i++) {
204 if (i > 0) {
205 builder.append(':');
206 }
207 builder.append(String.format("%02x", buffer[i] & 0xFF));
208
209 }
210 return builder.toString().toUpperCase();
211 }
212 }