001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.lib.collab.xmpp.httpbind.providers;
042:
043: import java.io.FileNotFoundException;
044: import java.io.IOException;
045: import java.net.HttpURLConnection;
046: import java.net.URL;
047: import java.net.URLConnection;
048: import java.security.KeyManagementException;
049: import java.security.KeyStoreException;
050: import java.security.NoSuchAlgorithmException;
051: import java.security.cert.CertificateException;
052: import java.security.cert.X509Certificate;
053: import java.util.HashMap;
054: import java.util.Map;
055: import javax.net.ssl.HostnameVerifier;
056: import javax.net.ssl.HttpsURLConnection;
057: import javax.net.ssl.SSLSession;
058: import javax.net.ssl.SSLSocketFactory;
059: import org.netbeans.lib.collab.CollaborationException;
060: import org.netbeans.lib.collab.CollaborationSessionListener;
061: import org.netbeans.lib.collab.SecureSessionListener;
062: import org.netbeans.lib.collab.util.CertificateVerify;
063: import org.netbeans.lib.collab.util.JavaxX509TrustManager;
064: import org.netbeans.lib.collab.xmpp.httpbind.ConnectionProvider;
065: import org.netbeans.lib.collab.xmpp.httpbind.HTTPSessionController;
066:
067: /**
068: *
069: * @author Mridul Muralidharan
070: */
071: public class HTTPSConnectionProvider extends ConnectionProviderImpl {
072: private CollaborationSessionListener sessionListener = null;
073: private SSLSocketFactory sslSocketFactory;
074: private HostnameVerifier testVerifier = new TestHostnameVerifier();
075:
076: public static final int TRUSTED_CHAIN = 1;
077: public static final int UNTRUSTED_CHAIN = 2;
078: public static final int UNKNOWN_CHAIN = 4;
079:
080: private static final String SSL_CHAIN_MAP = "com.sun.im.service.xmpp.httpbind.providers.HTTPSConnectionProvider.certficatechains";
081:
082: public HttpURLConnection openConnection(URL destination)
083: throws IOException {
084: HttpsURLConnection connection = null;
085: URLConnection _conn = destination.openConnection();
086: connection = (HttpsURLConnection) _conn;
087: connection.setSSLSocketFactory(getSSLSocketFactory());
088: connection.setHostnameVerifier(testVerifier);
089: setConnProperties(connection);
090: return connection;
091: }
092:
093: public ConnectionProvider createInstance(Map connParams)
094: throws CollaborationException {
095: try {
096: HTTPSConnectionProvider conn = new HTTPSConnectionProvider();
097: conn.setProperties(connParams);
098: conn.initListener(connParams);
099: conn.initProvider();
100: return conn;
101: } catch (Exception ex) {
102: throw new CollaborationException(ex);
103: }
104: }
105:
106: protected void initProvider() throws NoSuchAlgorithmException,
107: KeyStoreException, FileNotFoundException,
108: KeyManagementException, IOException, CertificateException {
109:
110: JavaxX509TrustManager trustManager = new JavaxX509TrustManager(
111: new CertificateVerify() {
112: public boolean doYouTrustCertificate(
113: X509Certificate[] chain) {
114: if (HTTPSessionController.isDebugOn()) {
115: HTTPSessionController
116: .debug("Certificate chain : "
117: + chain + "listener : "
118: + getSessionListener()
119: +
120: //", class : " + getSessionListener().getClass() +
121: " , is valid listener : "
122: + (getSessionListener() instanceof SecureSessionListener));
123: }
124: if (getSessionListener() instanceof SecureSessionListener) {
125: int trust = getCertificateTrust(chain);
126:
127: // Remove this dependency later !
128: if (HTTPSessionController.isDebugOn()) {
129: HTTPSessionController
130: .debug("Trust value : " + trust);
131: }
132:
133: if (UNKNOWN_CHAIN == trust) {
134: boolean callbackTrust = ((SecureSessionListener) getSessionListener())
135: .onX509Certificate(chain);
136: trust = callbackTrust ? TRUSTED_CHAIN
137: : UNTRUSTED_CHAIN;
138: setCertificateTrust(chain, trust);
139: }
140:
141: return TRUSTED_CHAIN == trust;
142: }
143: return false;
144: }
145: }, "SSLv3");
146: if (HTTPSessionController.isDebugOn()) {
147: HTTPSessionController.debug("trustManager : "
148: + trustManager);
149: HTTPSessionController.debug("SocketFactory : "
150: + trustManager.getSocketFactory());
151: }
152: setSSLSocketFactory(trustManager.getSocketFactory());
153: }
154:
155: private void initListener(Map connParams) {
156: setSessionListener((CollaborationSessionListener) connParams
157: .get(SESSION_LISTENER));
158: }
159:
160: protected CollaborationSessionListener getSessionListener() {
161: return sessionListener;
162: }
163:
164: protected void setSessionListener(
165: CollaborationSessionListener sessionListener) {
166: this .sessionListener = sessionListener;
167: }
168:
169: protected void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
170: this .sslSocketFactory = sslSocketFactory;
171: }
172:
173: protected SSLSocketFactory getSSLSocketFactory() {
174: return sslSocketFactory;
175: }
176:
177: private class CertificateChainHolder {
178: private X509Certificate[] chain = null;
179: private int hash = 0;
180:
181: public CertificateChainHolder(X509Certificate[] chain) {
182: this .chain = chain;
183: hash = generateCheapHash();
184: }
185:
186: public int hashCode() {
187: return hash;
188: }
189:
190: public boolean equals(Object obj) {
191: X509Certificate[] tchain = null;
192:
193: if (obj == this ) {
194: return true;
195: }
196:
197: if (null == obj || !(obj instanceof CertificateChainHolder)) {
198: return false;
199: }
200:
201: tchain = ((CertificateChainHolder) obj).chain;
202: if (tchain.length != chain.length) {
203: return false;
204: }
205:
206: int count = 0;
207: while (count < chain.length) {
208: if (chain[count] != tchain[count]
209: && (!chain[count].equals(tchain[count]))) {
210: return false;
211: }
212: count++;
213: }
214: return true;
215: }
216:
217: // This has been written in the assumption that the
218: // hashCode is implemented in the X509Certificate impl
219: private int generateCheapHash() {
220: int count = 0;
221: int retval = 0;
222:
223: while (count < chain.length) {
224: retval ^= chain[count].hashCode();
225: count++;
226: }
227: return retval;
228: }
229: }
230:
231: protected int getCertificateTrust(X509Certificate[] chain) {
232: Map props = getProperties();
233: synchronized (props) {
234: // El Cheapo hash
235: Map map = (Map) props.get(SSL_CHAIN_MAP);
236:
237: if (null != map) {
238: CertificateChainHolder holder = new CertificateChainHolder(
239: chain);
240: Integer val = (Integer) map.get(holder);
241:
242: if (null != val) {
243: return val.intValue();
244: }
245: }
246: }
247:
248: return UNKNOWN_CHAIN;
249: }
250:
251: protected void setCertificateTrust(X509Certificate[] chain,
252: int trust) {
253: Map props = getProperties();
254: synchronized (props) {
255: // El Cheapo hash
256: Map map = (Map) props.get(SSL_CHAIN_MAP);
257:
258: if (null != map) {
259: CertificateChainHolder holder = new CertificateChainHolder(
260: chain);
261: assert (!map.containsKey(holder));
262:
263: map.put(holder, new Integer(trust));
264: } else {
265: map = new HashMap();
266: CertificateChainHolder holder = new CertificateChainHolder(
267: chain);
268: map.put(holder, new Integer(trust));
269: props.put(SSL_CHAIN_MAP, map);
270: }
271: }
272:
273: return;
274: }
275:
276: // When I was testing with http://nicp103.india.sun.com:8888/
277: // The peerhost reported was the IP !
278: // We should do something more intelligent here - a dns lookup
279: // to verify cred is a bad idea IMHO.
280: class TestHostnameVerifier implements HostnameVerifier {
281: public boolean verify(String hostname, SSLSession session) {
282:
283: if (HTTPSessionController.isDebugOn()) {
284: HTTPSessionController
285: .debug("HostnameVerifier : host : " + hostname
286: + " , peerhost : "
287: + session.getPeerHost());
288: }
289:
290: int count = 0;
291: String[] vals = session.getValueNames();
292:
293: while (count < vals.length) {
294: String val = vals[count];
295: if (HTTPSessionController.isDebugOn()) {
296: HTTPSessionController.debug("value : " + val
297: + " , obj : " + session.getValue(val));
298: }
299: count++;
300: }
301:
302: //return hostname.equalsIgnoreCase(session.getPeerHost());
303: return true;
304: }
305: }
306: }
|