View Javadoc

1   /**
2    * Copyright (C) 2008 Atlassian
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.atlassian.theplugin.util;
18  
19  import com.atlassian.theplugin.commons.configuration.GeneralConfigurationBean;
20  import com.atlassian.theplugin.commons.remoteapi.rest.AbstractHttpSession;
21  import com.atlassian.theplugin.idea.ui.CertMessageDialog;
22  
23  import javax.net.ssl.TrustManager;
24  import javax.net.ssl.TrustManagerFactory;
25  import javax.net.ssl.X509TrustManager;
26  import java.awt.*;
27  import java.lang.reflect.InvocationTargetException;
28  import java.security.KeyStore;
29  import java.security.KeyStoreException;
30  import java.security.NoSuchAlgorithmException;
31  import java.security.cert.CertificateException;
32  import java.security.cert.CertificateExpiredException;
33  import java.security.cert.CertificateNotYetValidException;
34  import java.security.cert.X509Certificate;
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.HashSet;
38  
39  public final class PluginTrustManager implements X509TrustManager {
40  	private static final Collection<String> ALREADY_REJECTED_CERTS = new HashSet<String>();
41  	private static Collection<String> temporarilyAcceptedCerts =
42  			Collections.synchronizedCollection(new HashSet<String>());
43  
44  	private final GeneralConfigurationBean configuration;
45  	private X509TrustManager standardTrustManager;
46  
47  	private static ThreadLocal<String> url = new ThreadLocal<String>();
48  
49  	public static String getUrl() {
50  		return url.get();
51  	}
52  
53  	public PluginTrustManager(GeneralConfigurationBean configuration) throws NoSuchAlgorithmException, KeyStoreException {
54  		this.configuration = configuration;
55  		TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
56  		factory.init((KeyStore) null);
57  		TrustManager[] trustmanagers = factory.getTrustManagers();
58  
59  		//looking for a X509TrustManager instance
60  		for (TrustManager trustmanager : trustmanagers) {
61  			if (trustmanager instanceof X509TrustManager) {
62  				standardTrustManager = (X509TrustManager) trustmanager;
63  				return;
64  			}
65  		}
66  
67  		if (standardTrustManager == null) {
68  			throw new NoSuchAlgorithmException("cannot retrieve default trust manager found");
69  		}
70  	}
71  
72  	//checkClientTrusted
73  	public void checkClientTrusted(X509Certificate[] chain, String authType)
74  			throws CertificateException {
75  		standardTrustManager.checkClientTrusted(chain, authType);
76  	}
77  
78  
79  	private boolean isSelfSigned(X509Certificate certificate) {
80  		return certificate.getSubjectDN().equals(certificate.getIssuerDN());
81  	}
82  
83  	//checkServerTrusted
84  	public void checkServerTrusted(final X509Certificate[] chain, String authType) throws CertificateException {
85  		try {
86  			standardTrustManager.checkServerTrusted(chain, authType);
87  		} catch (final CertificateException
88  				e) {
89  
90  			String strCert = chain[0].toString();
91  			if (ALREADY_REJECTED_CERTS.contains(strCert)) {
92  				throw e;
93  			}
94  
95  			if (checkChain(chain, configuration.getCerts())
96  					||
97  					checkChain(chain, temporarilyAcceptedCerts)) {
98  				return;
99  			}
100 
101 			String message = e.getMessage();
102 			message = message.substring(message.lastIndexOf(":") + 1);
103 			if (isSelfSigned(chain[0])) {
104 				message = "Self-signed certificate";
105 			}
106 			try {
107 				chain[0].checkValidity();
108 			} catch (CertificateExpiredException e1) {
109 				message = "Certificate expired";
110 			} catch (CertificateNotYetValidException e1) {
111 				message = "Certificate not yet valid";
112 			}
113 			final String server = AbstractHttpSession.getUrl().getHost();
114 
115 			// check if it should be accepted
116 			final int[] accepted = new int[]{0}; // 0 rejected 1 accepted temporarily 2 - accepted perm.
117 			synchronized (PluginTrustManager.class) {
118 				try {
119 					final String message1 = message;
120 					EventQueue.invokeAndWait(new Runnable() {
121 						public void run() {
122 							CertMessageDialog dialog = new CertMessageDialog(server, message1, chain);
123 							dialog.show();
124 							if (dialog.isOK()) {
125 								if (dialog.isTemporarily()) {
126 									accepted[0] = 1;
127 									return;
128 								}
129 								accepted[0] = 2;
130 							}
131 						}
132 					});
133 				} catch (InterruptedException e1) {
134 					// swallow
135 				} catch (InvocationTargetException e1) {
136 					// swallow
137 				}
138 			}
139 
140 			switch (accepted[0]) {
141 				case 1:
142 					temporarilyAcceptedCerts.add(strCert);
143 					break;
144 				case 2:
145 					synchronized (configuration) {
146 						// taken once again because something could change in the state
147 						configuration.addCert(strCert);
148 					}
149 					break;
150 				default:
151 					synchronized (ALREADY_REJECTED_CERTS) {
152 						ALREADY_REJECTED_CERTS.add(strCert);
153 					}
154 					throw e;
155 			}
156 		}
157 	}
158 
159 	private boolean checkChain(X509Certificate[] chain, Collection<String> certs) {
160 		for (X509Certificate cert : chain) {
161 			if (certs.contains(cert.toString())) {
162 				return true;
163 			}
164 		}
165 		return false;
166 	}
167 
168 	//getAcceptedIssuers
169 	public X509Certificate[] getAcceptedIssuers() {
170 		return standardTrustManager.getAcceptedIssuers();
171 	}
172 
173 }