001: /* ConfigurableX509TrustManager
002: *
003: * Created on Feb 18, 2004
004: *
005: * Copyright (C) 2004 Internet Archive.
006: *
007: * This file is part of the Heritrix web crawler (crawler.archive.org).
008: *
009: * Heritrix is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU Lesser Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * any later version.
013: *
014: * Heritrix is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: * GNU Lesser Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser Public License
020: * along with Heritrix; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: */
023: package org.archive.httpclient;
024:
025: import java.security.KeyStore;
026: import java.security.KeyStoreException;
027: import java.security.NoSuchAlgorithmException;
028: import java.security.cert.CertificateException;
029: import java.security.cert.X509Certificate;
030: import java.util.Arrays;
031: import java.util.List;
032: import java.util.logging.Logger;
033:
034: import javax.net.ssl.TrustManager;
035: import javax.net.ssl.TrustManagerFactory;
036: import javax.net.ssl.X509TrustManager;
037:
038: /**
039: * A configurable trust manager built on X509TrustManager.
040: *
041: * If set to 'open' trust, the default, will get us into sites for whom we do
042: * not have the CA or any of intermediary CAs that go to make up the cert chain
043: * of trust. Will also get us past selfsigned and expired certs. 'loose'
044: * trust will get us into sites w/ valid certs even if they are just
045: * selfsigned. 'normal' is any valid cert not including selfsigned. 'strict'
046: * means cert must be valid and the cert DN must match server name.
047: *
048: * <p>Based on pointers in
049: * <a href="http://jakarta.apache.org/commons/httpclient/sslguide.html">SSL
050: * Guide</a>,
051: * and readings done in <a
052: * href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#Introduction">JSSE
053: * Guide</a>.
054: *
055: * <p>TODO: Move to an ssl subpackage when we have other classes other than
056: * just this one.
057: *
058: * @author stack
059: * @version $Id: ConfigurableX509TrustManager.java 4232 2006-05-15 21:52:30Z stack-sf $
060: */
061: public class ConfigurableX509TrustManager implements X509TrustManager {
062: /**
063: * Logging instance.
064: */
065: protected static Logger logger = Logger
066: .getLogger("org.archive.httpclient.ConfigurableX509TrustManager");
067:
068: /**
069: * Trust anything given us.
070: *
071: * Default setting.
072: *
073: * <p>See <a href="http://javaalmanac.com/egs/javax.net.ssl/TrustAll.html">
074: * e502. Disabling Certificate Validation in an HTTPS Connection</a> from
075: * the java almanac for how to trust all.
076: */
077: public final static String OPEN = "open";
078:
079: /**
080: * Trust any valid cert including self-signed certificates.
081: */
082: public final static String LOOSE = "loose";
083:
084: /**
085: * Normal jsse behavior.
086: *
087: * Seemingly any certificate that supplies valid chain of trust.
088: */
089: public final static String NORMAL = "normal";
090:
091: /**
092: * Strict trust.
093: *
094: * Ensure server has same name as cert DN.
095: */
096: public final static String STRICT = "strict";
097:
098: /**
099: * All the levels of trust as an array from babe-in-the-wood to strict.
100: */
101: public static String[] LEVELS_AS_ARRAY = { OPEN, LOOSE, NORMAL,
102: STRICT };
103:
104: /**
105: * Levels as a list.
106: */
107: private static List LEVELS = Arrays.asList(LEVELS_AS_ARRAY);
108:
109: /**
110: * Default setting for trust level.
111: */
112: public final static String DEFAULT = OPEN;
113:
114: /**
115: * Trust level.
116: */
117: private String trustLevel = DEFAULT;
118:
119: /**
120: * An instance of the SUNX509TrustManager that we adapt variously
121: * depending upon passed configuration.
122: *
123: * We have it do all the work we don't want to.
124: */
125: private X509TrustManager standardTrustManager = null;
126:
127: public ConfigurableX509TrustManager()
128: throws NoSuchAlgorithmException, KeyStoreException {
129: this (DEFAULT);
130: }
131:
132: /**
133: * Constructor.
134: *
135: * @param level Level of trust to effect.
136: *
137: * @throws NoSuchAlgorithmException
138: * @throws KeyStoreException
139: */
140: public ConfigurableX509TrustManager(String level)
141: throws NoSuchAlgorithmException, KeyStoreException {
142: super ();
143: TrustManagerFactory factory = TrustManagerFactory
144: .getInstance(TrustManagerFactory.getDefaultAlgorithm());
145:
146: // Pass in a null (Trust) KeyStore. Null says use the 'default'
147: // 'trust' keystore (KeyStore class is used to hold keys and to hold
148: // 'trusts' (certs)). See 'X509TrustManager Interface' in this doc:
149: // http://java.sun.com
150: // /j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#Introduction
151: factory.init((KeyStore) null);
152: TrustManager[] trustmanagers = factory.getTrustManagers();
153: if (trustmanagers.length == 0) {
154: throw new NoSuchAlgorithmException(TrustManagerFactory
155: .getDefaultAlgorithm()
156: + " trust manager not supported");
157: }
158: this .standardTrustManager = (X509TrustManager) trustmanagers[0];
159:
160: this .trustLevel = (LEVELS.contains(level.toLowerCase())) ? level
161: : DEFAULT;
162: }
163:
164: public void checkClientTrusted(X509Certificate[] certificates,
165: String type) throws CertificateException {
166: if (this .trustLevel.equals(OPEN)) {
167: return;
168: }
169:
170: this .standardTrustManager
171: .checkClientTrusted(certificates, type);
172: }
173:
174: public void checkServerTrusted(X509Certificate[] certificates,
175: String type) throws CertificateException {
176: if (this .trustLevel.equals(OPEN)) {
177: return;
178: }
179:
180: try {
181: this .standardTrustManager.checkServerTrusted(certificates,
182: type);
183: if (this .trustLevel.equals(STRICT)) {
184: logger.severe(STRICT + " not implemented.");
185: }
186: } catch (CertificateException e) {
187: if (this .trustLevel.equals(LOOSE) && certificates != null
188: && certificates.length == 1) {
189: // If only one cert and its valid and it caused a
190: // CertificateException, assume its selfsigned.
191: X509Certificate certificate = certificates[0];
192: certificate.checkValidity();
193: } else {
194: // If we got to here, then we're probably NORMAL. Rethrow.
195: throw e;
196: }
197: }
198: }
199:
200: public X509Certificate[] getAcceptedIssuers() {
201: return this.standardTrustManager.getAcceptedIssuers();
202: }
203: }
|