File Source: SSLCertificateDialogModel.java
/*
P/P * Method: com.dmdirc.ui.core.dialogs.sslcertificate.SSLCertificateDialogModel__static_init
*/
1 /*
2 * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 package com.dmdirc.ui.core.dialogs.sslcertificate;
24
25 import com.dmdirc.CertificateManager;
26 import com.dmdirc.CertificateManager.CertificateDoesntMatchHostException;
27 import com.dmdirc.CertificateManager.CertificateNotTrustedException;
28
29 import java.security.cert.CertificateException;
30 import java.security.cert.CertificateExpiredException;
31 import java.security.cert.CertificateNotYetValidException;
32 import java.security.cert.CertificateParsingException;
33 import java.security.cert.X509Certificate;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Map;
37
38 /**
39 * Model for SSL certificate dialogs.
40 *
41 * @since 0.6.3m1
42 * @author chris
43 */
44 public class SSLCertificateDialogModel {
45
46 /** The certificate chain that we're displaying information about. */
47 private final X509Certificate[] chain;
48
49 /** The certificate manager for the connection attempt. */
50 private final CertificateManager manager;
51
52 /** The list of problems found with the certs, if any. */
53 private final List<CertificateException> problems;
54
55 /** The text to use if a field isn't present on the certificate. */
56 private static final String NOTPRESENT = "(not present on certificate)";
57
58 /**
59 * Creates a new SSLCertificateDialogModel for the specified chain.
60 *
61 * @param chain The chain of certificates to display info on
62 * @param problems A list of problems with the certificates, if any
63 * @param manager The certificate manager responsible for the certs
64 */
65 public SSLCertificateDialogModel(final X509Certificate[] chain,
66 final List<CertificateException> problems,
/*
P/P * Method: void com.dmdirc.ui.core.dialogs.sslcertificate.SSLCertificateDialogModel(X509Certificate[], List, CertificateManager)
*
* Postconditions:
* this.chain == chain
* init'ed(this.chain)
* this.manager == manager
* init'ed(this.manager)
* this.problems == problems
* init'ed(this.problems)
*/
67 final CertificateManager manager) {
68 this.chain = chain;
69 this.problems = problems;
70 this.manager = manager;
71 }
72
73 /**
74 * Retrieves the certificate chain that's under question.
75 *
76 * @return A list of {@link CertificateChainEntry}s corresponding to the
77 * certificate chain being questioned.
78 */
79 public List<CertificateChainEntry> getCertificateChain() {
/*
P/P * Method: List getCertificateChain()
*
* Preconditions:
* this.chain != null
* this.chain.length <= 232-1
* (soft) this.chain[...] != null
* (soft) this.manager != null
*
* Presumptions:
* com.dmdirc.CertificateManager:getDNFieldsFromCert(...)@94 != null
*
* Postconditions:
* return_value == &new ArrayList(getCertificateChain#1)
* new ArrayList(getCertificateChain#1) num objects == 1
*/
80 final List<CertificateChainEntry> res = new ArrayList<CertificateChainEntry>();
81
82 boolean first = true;
83
84 for (X509Certificate cert : chain) {
85 boolean invalid = first && !manager.isValidHost(cert);
86 first = false;
87
88 try {
89 cert.checkValidity();
90 } catch (CertificateException ex) {
91 invalid |= true;
92 }
93
94 res.add(new CertificateChainEntry(CertificateManager
95 .getDNFieldsFromCert(cert).get("CN"),
96 manager.isTrusted(cert), invalid));
97 }
98
99 return res;
100 }
101
102 /**
103 * Retrieves displayable information about the certificate with the
104 * specified index in the chain.
105 *
106 * @param index The index of the certificate to request information on
107 * @return A list of lists of {@link CertificateInformationEntry}s.
108 */
109 public List<List<CertificateInformationEntry>> getCertificateInfo(final int index) {
/*
P/P * Method: List getCertificateInfo(int)
*
* Preconditions:
* index >= 0
* this.chain != null
* this.chain.length >= 1
* index < this.chain.length
* (soft) this.chain[...] != null
* (soft) this.manager != null
*
* Presumptions:
* com.dmdirc.CertificateManager:getDNFieldsFromCert(...)@134 != null
* java.security.cert.X509Certificate:getNotAfter(...)@128 != null
* java.security.cert.X509Certificate:getNotBefore(...)@126 != null
* java.security.cert.X509Certificate:getSerialNumber(...)@150 != null
*
* Postconditions:
* return_value == &new ArrayList(getCertificateInfo#1)
* new ArrayList(getCertificateInfo#1) num objects == 1
*/
110 final List<List<CertificateInformationEntry>> res
111 = new ArrayList<List<CertificateInformationEntry>>();
112 final X509Certificate cert = chain[index];
113 List<CertificateInformationEntry> group;
114
115 boolean tooOld = false, tooNew = false;
116
117 try {
118 cert.checkValidity();
119 } catch (CertificateExpiredException ex) {
120 tooOld = true;
121 } catch (CertificateNotYetValidException ex) {
122 tooNew = true;
123 }
124
125 group = new ArrayList<CertificateInformationEntry>();
126 group.add(new CertificateInformationEntry("Valid from",
127 cert.getNotBefore().toString(), tooNew, false));
128 group.add(new CertificateInformationEntry("Valid to",
129 cert.getNotAfter().toString(), tooOld, false));
130 res.add(group);
131
132 final boolean wrongName = index == 0 && !manager.isValidHost(cert);
133 final String names = getAlternateNames(cert);
134 final Map<String, String> fields = CertificateManager.getDNFieldsFromCert(cert);
135
136 group = new ArrayList<CertificateInformationEntry>();
137 addCertField(fields, group, "Common name", "CN", wrongName);
138
139 group.add(new CertificateInformationEntry("Alternate names",
140 names == null ? NOTPRESENT : names, wrongName, names == null));
141
142 addCertField(fields, group, "Organisation", "O", false);
143 addCertField(fields, group, "Unit", "OU", false);
144 addCertField(fields, group, "Locality", "L", false);
145 addCertField(fields, group, "State", "ST", false);
146 addCertField(fields, group, "Country", "C", false);
147 res.add(group);
148
149 group = new ArrayList<CertificateInformationEntry>();
150 group.add(new CertificateInformationEntry("Serial number",
151 cert.getSerialNumber().toString(), false, false));
152 group.add(new CertificateInformationEntry("Algorithm",
153 cert.getSigAlgName(), false, false));
154 group.add(new CertificateInformationEntry("SSL version",
155 String.valueOf(cert.getVersion()), false, false));
156 res.add(group);
157
158 return res;
159 }
160
161 protected String getAlternateNames(final X509Certificate cert) {
/*
P/P * Method: String getAlternateNames(X509Certificate)
*
* Preconditions:
* (soft) cert != null
*
* Presumptions:
* java.security.cert.X509Certificate:getSubjectAlternativeNames(...)@169 != null
* java.util.Iterator:next(...)@169 != null
* java.util.List:get(...)@170 != null
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* return_value in Addr_Set{null,&java.lang.StringBuilder:toString(...)}
*
* Test Vectors:
* java.lang.Integer:intValue(...)@170: {-231..1, 3..6, 8..232-1}, {7}
* java.lang.StringBuilder:length(...)@174: {-231..0}, {1..232-1}
* java.security.cert.X509Certificate:getSubjectAlternativeNames(...)@165: Inverse{null}, Addr_Set{null}
*/
162 final StringBuilder res = new StringBuilder();
163
164 try {
165 if (cert.getSubjectAlternativeNames() == null) {
166 return null;
167 }
168
169 for (List<?> entry : cert.getSubjectAlternativeNames()) {
170 final int type = ((Integer) entry.get(0)).intValue();
171
172 // DNS or IP
173 if (type == 2 || type == 7) {
174 if (res.length() > 0) {
175 res.append(", ");
176 }
177
178 res.append(entry.get(1));
179 }
180 }
181 } catch (CertificateParsingException ex) {
182 // Do nothing
183 }
184
185 return res.toString();
186 }
187
188 /**
189 * Adds a field to the specified group.
190 *
191 * @param fields The fields extracted from the certiciate
192 * @param group The group to add an entry to
193 * @param title The user-friendly title of the field
194 * @param field The name of the field to look for
195 * @param invalid Whether or not the field is a cause for concern
196 */
197 protected void addCertField(final Map<String, String> fields,
198 final List<CertificateInformationEntry> group, final String title,
199 final String field, final boolean invalid) {
/*
P/P * Method: void addCertField(Map, List, String, String, bool)
*
* Preconditions:
* fields != null
* group != null
*/
200 group.add(new CertificateInformationEntry(title,
201 fields.containsKey(field) ? fields.get(field) : NOTPRESENT, invalid,
202 !fields.containsKey(field)));
203 }
204
205 /**
206 * Retrieves a list of summary elements to describe the overall status
207 * of the certificate chain.
208 *
209 * @return A list of summary entries
210 */
211 public List<CertificateSummaryEntry> getSummary() {
/*
P/P * Method: List getSummary()
*
* Preconditions:
* this.problems != null
*
* Postconditions:
* return_value == &new ArrayList(getSummary#1)
* new ArrayList(getSummary#1) num objects == 1
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@216: {0}, {1}
*/
212 final List<CertificateSummaryEntry> res = new ArrayList<CertificateSummaryEntry>();
213
214 boolean outofdate = false, wronghost = false, nottrusted = false;
215
216 for (CertificateException ex : problems) {
217 if (ex instanceof CertificateExpiredException
218 || ex instanceof CertificateNotYetValidException) {
219 outofdate = true;
220 } else if (ex instanceof CertificateDoesntMatchHostException) {
221 wronghost = true;
222 } else if (ex instanceof CertificateNotTrustedException) {
223 nottrusted = true;
224 }
225 }
226
227 if (outofdate) {
228 res.add(new CertificateSummaryEntry("One or more certificates are " +
229 "not within their validity period", false));
230 } else {
231 res.add(new CertificateSummaryEntry("All certificates are " +
232 "within their validity period", true));
233 }
234
235 if (nottrusted) {
236 res.add(new CertificateSummaryEntry("The certificate is not issued "
237 + "by a trusted authority", false));
238 } else {
239 res.add(new CertificateSummaryEntry("The certificate chain is "
240 + "trusted", true));
241 }
242
243 if (wronghost) {
244 res.add(new CertificateSummaryEntry("The certificate is not issued "
245 + "to the host you are connecting to", false));
246 } else {
247 res.add(new CertificateSummaryEntry("The certificate is issued "
248 + "to the host you are connecting to", true));
249 }
250
251 return res;
252 }
253
254 /**
255 * Determines whether or not a response is required from the user about
256 * this certificate chain.
257 *
258 * @return True if a response is required, false otherwise
259 */
260 public boolean needsResponse() {
/*
P/P * Method: bool needsResponse()
*
* Preconditions:
* this.problems != null
*
* Postconditions:
* init'ed(return_value)
*/
261 return !problems.isEmpty();
262 }
263
264 /**
265 * Retrieves the name of the server to which the user is trying to connect.
266 *
267 * @return The name of the server that the user is trying to connect to
268 */
269 public String getServerName() {
/*
P/P * Method: String getServerName()
*
* Preconditions:
* this.manager != null
*
* Postconditions:
* init'ed(return_value)
*/
270 return manager.getServerName();
271 }
272
273 /**
274 * Performs the specified action on the certificate chain/connection.
275 * Should only be called once per instance, and only if
276 * {@link #needsResponse()} returns true.
277 *
278 * @param action The action to be performed
279 */
280 public void performAction(final CertificateAction action) {
/*
P/P * Method: void performAction(CertificateAction)
*
* Preconditions:
* this.manager != null
* this.problems != null
*
* Presumptions:
* java.util.List:isEmpty(...)@261 == 0
*/
281 if (!needsResponse()) {
282 throw new IllegalStateException("Can't perform action when "
283 + "no action is needed");
284 }
285
286 manager.setAction(action);
287 }
288
289 }
SofCheck Inspector Build Version : 2.17854
| SSLCertificateDialogModel.java |
2009-Jun-25 01:54:24 |
| SSLCertificateDialogModel.class |
2009-Sep-02 17:04:12 |