001: package net.suberic.pooka.ssl;
002:
003: import java.io.*;
004:
005: import java.security.cert.*;
006: import javax.net.ssl.*;
007:
008: /**
009: * This wraps the default X509TrustManager so that we can handle untrusted
010: * certificate chains.
011: */
012: public class PookaTrustManager implements X509TrustManager {
013:
014: X509TrustManager wrappedManager = null;
015:
016: String certificateRepositoryFile = null;
017:
018: java.util.Set rejectedCerts = new java.util.HashSet();
019:
020: java.util.Set trustedCerts = new java.util.HashSet();
021:
022: boolean mUseCertFile = true;
023:
024: /**
025: * Creates a new TrustManager that wraps the given manager.
026: */
027: public PookaTrustManager(TrustManager[] pWrappedManagers,
028: String pCertFile) {
029: this (pWrappedManagers, pCertFile, true);
030: }
031:
032: /**
033: * Creates a new TrustManager that wraps the given manager.
034: */
035: public PookaTrustManager(TrustManager[] pWrappedManagers,
036: String pCertFile, boolean pUseCertFile) {
037: super ();
038:
039: mUseCertFile = pUseCertFile;
040: if (mUseCertFile)
041: certificateRepositoryFile = pCertFile;
042:
043: for (int i = 0; i < pWrappedManagers.length; i++) {
044: if (pWrappedManagers[i] instanceof X509TrustManager)
045: wrappedManager = (X509TrustManager) pWrappedManagers[i];
046: }
047:
048: loadAccepted();
049: }
050:
051: /**
052: * Returns whether or not the client with the given certificates is
053: * trusted or not.
054: */
055: public void checkClientTrusted(X509Certificate[] cert,
056: String authType) throws CertificateException {
057: if (wrappedManager != null) {
058: CertificateException trustException = null;
059: try {
060: wrappedManager.checkClientTrusted(cert, authType);
061: } catch (CertificateException e) {
062: trustException = e;
063: }
064: if (trustException != null) {
065: if (localIsTrusted(cert))
066: return;
067: else
068: throw trustException;
069: }
070: }
071: // if the respones from the wrappedManager was false, or if there is no
072: // wrappedManager, then check out local db.
073: else if (!localIsTrusted(cert))
074: throw new CertificateException("Certificate not trusted.");
075: }
076:
077: /**
078: * Returns whether or not the server with the given certificates is
079: * trusted or not.
080: */
081: public void checkServerTrusted(X509Certificate[] cert,
082: String authType) throws CertificateException {
083: if (wrappedManager != null) {
084: CertificateException trustException = null;
085: try {
086: wrappedManager.checkServerTrusted(cert, authType);
087: } catch (CertificateException e) {
088: trustException = e;
089: }
090:
091: if (trustException != null) {
092: // if this isn't acceptable by default, ask.
093: if (localIsTrusted(cert))
094: return;
095: else
096: throw trustException;
097: }
098: } else if (!localIsTrusted(cert))
099: throw new CertificateException("Certificate not trusted.");
100: }
101:
102: /**
103: * Return an array of certificate authority certificates
104: * which are trusted for authenticating peers.
105: */
106: public X509Certificate[] getAcceptedIssuers() {
107: return wrappedManager.getAcceptedIssuers();
108: }
109:
110: /**
111: * Checks to see if this certificate is in the local certificate store.
112: * If it's not, then we ask if it should be.
113: */
114: public boolean localIsTrusted(X509Certificate[] cert) {
115: if (cert == null || cert.length < 1)
116: return false;
117:
118: boolean found = false;
119:
120: boolean rejected = false;
121:
122: for (int i = 0; !found && !rejected && i < cert.length; i++) {
123: if (trustedCerts.contains(cert[i]))
124: found = true;
125: else if (rejectedCerts.contains(cert[i]))
126: rejected = true;
127: }
128:
129: if (found)
130: return true;
131: else if (rejected)
132: return false;
133:
134: // if it hasn't been checked already, then ask.
135:
136: boolean response = askIsTrusted(cert);
137:
138: if (response) {
139: addToTrusted(cert);
140: } else {
141: addToRejected(cert);
142: }
143:
144: return response;
145: }
146:
147: /**
148: * Interactively figures out whether or not we want to trust this
149: * (default untrusted) certificate.
150: */
151: public boolean askIsTrusted(X509Certificate[] cert) {
152: X509Certificate certToPrint = null;
153: for (int i = 0; i < cert.length && certToPrint == null; i++) {
154: if (cert[i] != null)
155: certToPrint = cert[i];
156: }
157:
158: int response = -1;
159: if (certToPrint != null) {
160: StringBuffer msg = new StringBuffer(
161: "The following certificates are not trusted. Accpet them anyway?\n\n");
162: msg.append("Issuer: ");
163: msg.append(certToPrint.getIssuerDN().getName());
164: msg.append("\n");
165: response = net.suberic.pooka.Pooka.getUIFactory()
166: .showConfirmDialog(msg.toString(),
167: "Accpet SSL certificate?",
168: javax.swing.JOptionPane.YES_NO_OPTION);
169: } else {
170: response = net.suberic.pooka.Pooka
171: .getUIFactory()
172: .showConfirmDialog(
173: "The certificate(s) for this server are not trusted. Accpet them anyway?",
174: "Accpet SSL certificate?",
175: javax.swing.JOptionPane.YES_NO_OPTION);
176: }
177:
178: if (response == javax.swing.JOptionPane.YES_OPTION)
179: return true;
180: else
181: return false;
182: }
183:
184: /**
185: * Adds the given certificate(s) to the local trusted store.
186: */
187: public void addToTrusted(X509Certificate[] cert) {
188: if (cert != null) {
189: BufferedWriter fw = null;
190:
191: if (mUseCertFile) {
192: if (certificateRepositoryFile != null
193: && !certificateRepositoryFile.equals("")) {
194: try {
195: // see if we can open the file.
196: fw = new BufferedWriter(new FileWriter(
197: certificateRepositoryFile, true));
198: } catch (IOException ioe) {
199: final Exception e = ioe;
200: javax.swing.SwingUtilities
201: .invokeLater(new Runnable() {
202: public void run() {
203: net.suberic.pooka.Pooka
204: .getUIFactory()
205: .showError(
206: "Error opening SSL certificate file: "
207: + certificateRepositoryFile,
208: e);
209: }
210: });
211: }
212: } else {
213: // don't give warning if we're not using a local certificate file.
214: javax.swing.SwingUtilities
215: .invokeLater(new Runnable() {
216: public void run() {
217: net.suberic.pooka.Pooka
218: .getUIFactory()
219: .showError(
220: "Warning: no certificate file set.\nCertificate will only be accepted for this session.\nGo to Configuation->Preferences->SSL to set a certificate file.");
221: }
222: });
223: }
224: }
225:
226: try {
227: for (int i = 0; i < cert.length; i++) {
228: if (cert[i] != null) {
229: trustedCerts.add(cert[i]);
230:
231: if (fw != null) {
232: fw.write("-----BEGIN CERTIFICATE-----");
233: fw.newLine();
234: fw.write(new sun.misc.BASE64Encoder()
235: .encode(cert[i].getEncoded()));
236: fw.newLine();
237: fw.write("-----END CERTIFICATE-----");
238: fw.newLine();
239: }
240: }
241: }
242:
243: fw.flush();
244: } catch (Exception e) {
245: // FIXME
246: } finally {
247: if (fw != null) {
248: try {
249: fw.close();
250: } catch (Exception e) {
251: }
252: }
253: }
254: }
255:
256: }
257:
258: /**
259: * Adds the given certificate(s) to the rejected list.
260: */
261: public void addToRejected(X509Certificate[] cert) {
262: if (cert != null) {
263: for (int i = 0; i < cert.length; i++) {
264: if (cert[i] != null)
265: rejectedCerts.add(cert[i]);
266: }
267: }
268: }
269:
270: /**
271: * Loads the already-accepted certificated from the local file.
272: */
273: public void loadAccepted() {
274: FileInputStream fis = null;
275: if (mUseCertFile && certificateRepositoryFile != null) {
276: try {
277: fis = new FileInputStream(certificateRepositoryFile);
278: DataInputStream dis = new DataInputStream(fis);
279: CertificateFactory cf = CertificateFactory
280: .getInstance("X.509");
281: byte[] bytes = new byte[dis.available()];
282: dis.readFully(bytes);
283: ByteArrayInputStream bais = new ByteArrayInputStream(
284: bytes);
285: while (bais.available() > 0) {
286: Certificate cert = cf.generateCertificate(bais);
287:
288: trustedCerts.add(cert);
289: }
290: } catch (Exception ioe) {
291: // FIXME -- nothing for now.
292: } finally {
293: try {
294: if (fis != null)
295: fis.close();
296: } catch (Exception e) {
297:
298: }
299: }
300: }
301:
302: }
303: }
|