--- http11/src/java/org/apache/coyote/http11/Http11Processor.java (revision 354820) +++ http11/src/java/org/apache/coyote/http11/Http11Processor.java (working copy) @@ -1048,6 +1048,10 @@ if (sslO != null) request.setAttribute (SSLSupport.SESSION_ID_KEY, sslO); + sslO = sslSupport.isClientCertificateTrusted(); + if (sslO != null) { + request.setAttribute(SSLSupport.CLIENT_CERTIFICATE_TRUSTED,sslO); + } } } catch (Exception e) { log.warn(sm.getString("http11processor.socket.ssl"), e); --- util/java/org/apache/tomcat/util/net/jsse/JSSE13Factory.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE13Factory.java (working copy) @@ -30,14 +30,17 @@ class JSSE13Factory implements JSSEFactory { + JSSE13AllTrustingX509TrustManager atm; + JSSE13Factory() { + atm = new JSSE13AllTrustingX509TrustManager(); } public ServerSocketFactory getSocketFactory() { - return new JSSE13SocketFactory(); + return new JSSE13SocketFactory(atm); } public SSLSupport getSSLSupport(Socket socket) { - return new JSSESupport((SSLSocket)socket); + return new JSSE13Support((SSLSocket)socket,atm); } } --- util/java/org/apache/tomcat/util/net/jsse/JSSESupport.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSESupport.java (working copy) @@ -40,7 +40,7 @@ Parts cribbed from CertificatesValve */ -class JSSESupport implements SSLSupport { +abstract class JSSESupport implements SSLSupport { private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(JSSESupport.class); @@ -173,6 +173,7 @@ return buf.toString(); } + public abstract Boolean isClientCertificateTrusted() throws IOException; } --- util/java/org/apache/tomcat/util/net/jsse/JSSE14Factory.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE14Factory.java (working copy) @@ -30,14 +30,17 @@ class JSSE14Factory implements JSSEFactory { + protected JSSE14AllTrustingX509TrustManager atm; + JSSE14Factory() { + atm = new JSSE14AllTrustingX509TrustManager(); } public ServerSocketFactory getSocketFactory() { - return new JSSE14SocketFactory(); + return new JSSE14SocketFactory(atm); } public SSLSupport getSSLSupport(Socket socket) { - return new JSSE14Support((SSLSocket)socket); + return new JSSE14Support((SSLSocket)socket,atm); } } --- util/java/org/apache/tomcat/util/net/jsse/JSSE15Factory.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE15Factory.java (working copy) @@ -35,7 +35,7 @@ } public ServerSocketFactory getSocketFactory() { - return new JSSE15SocketFactory(); + return new JSSE15SocketFactory(atm); } } --- util/java/org/apache/tomcat/util/net/jsse/JSSE13Support.java (revision 0) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE13Support.java (revision 0) @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.util.net.jsse; + +import java.io.IOException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLSocket; + +public class JSSE13Support extends JSSESupport { + + protected JSSE13AllTrustingX509TrustManager atm; + + public JSSE13Support(SSLSocket sock,JSSE13AllTrustingX509TrustManager atm) { + super(sock); + this.atm = atm; + } + + public Boolean isClientCertificateTrusted() throws IOException { + if (atm.isInitialized()) { + X509Certificate[] chain = (X509Certificate[]) this.getPeerCertificateChain(); + if (chain != null && chain.length >= 1) { + return new Boolean(atm.isClientTrusted(chain)); + } + } + return null; + } + +} --- util/java/org/apache/tomcat/util/net/jsse/JSSE14Support.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE14Support.java (working copy) @@ -21,6 +21,7 @@ import java.io.InputStream; import java.net.SocketException; import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -52,8 +53,11 @@ Listener listener = new Listener(); - public JSSE14Support(SSLSocket sock){ + protected JSSE14AllTrustingX509TrustManager atm; + + public JSSE14Support(SSLSocket sock,JSSE14AllTrustingX509TrustManager atm){ super(sock); + this.atm = atm; sock.addHandshakeCompletedListener(listener); } @@ -143,6 +147,24 @@ return x509Certs; } + public Boolean isClientCertificateTrusted() throws IOException { + if (atm.isInitialized()) { + try { + X509Certificate[] chain = (X509Certificate[]) this.getPeerCertificateChain(); + // get the authentication type from the client certificate + if (chain != null && chain.length > 0) { + String authType = chain[0].getPublicKey().getAlgorithm(); + atm.forceCheckClientTrusted(chain,authType); + } else { + return null; + } + } catch (CertificateException ce) { + return new Boolean(false); + } + return new Boolean(true); + } + return null; + } private static class Listener implements HandshakeCompletedListener { volatile boolean completed = false; --- util/java/org/apache/tomcat/util/net/jsse/JSSE13SocketFactory.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE13SocketFactory.java (working copy) @@ -49,9 +49,12 @@ * Flag for client authentication */ protected boolean clientAuth = false; + + protected JSSE13AllTrustingX509TrustManager atm; - public JSSE13SocketFactory () { + public JSSE13SocketFactory (JSSE13AllTrustingX509TrustManager atm) { super(); + this.atm = atm; } /** @@ -89,6 +92,13 @@ // Certificate encoding algorithm (e.g., SunX509) String algorithm = (String)attributes.get("algorithm"); if (algorithm == null) algorithm = defaultAlgorithm; + + String acceptUntrustedCertStr = (String)attributes.get("acceptUntrustedCertificates"); + boolean acceptUntrustedCert = false; + if ("true".equals(acceptUntrustedCertStr) || + "yes".equals(acceptUntrustedCertStr)) { + acceptUntrustedCert = true; + } // Set up KeyManager, which will extract server key com.sun.net.ssl.KeyManagerFactory kmf = @@ -114,6 +124,30 @@ tmf.init(trustStore); tm = tmf.getTrustManagers(); } + + if (acceptUntrustedCert) { + + /* + * Get the first instance of an com.sun.net.ssl.X509TrustManager and wrap the + * JSSE13PlugableX509TrustManager around it. + * Depends on the current implementation of SSLContext, which + * only supports X509TrustManagers. + */ + if (tm == null) { + com.sun.net.ssl.TrustManagerFactory tmf = com.sun.net.ssl.TrustManagerFactory.getInstance("SunX509"); + tmf.init((KeyStore) null); + tm = tmf.getTrustManagers(); + } + if (tm != null) { + for (int i = 0; i < tm.length;i++) { + if (tm[i] instanceof com.sun.net.ssl.X509TrustManager) { + atm.init((com.sun.net.ssl.X509TrustManager) tm[i]); + tm[i] = atm; + break; + } + } + } + } // Create and init SSLContext com.sun.net.ssl.SSLContext context = --- util/java/org/apache/tomcat/util/net/jsse/JSSE14SocketFactory.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE14SocketFactory.java (working copy) @@ -29,6 +29,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; import org.apache.tomcat.util.res.StringManager; @@ -65,8 +66,10 @@ */ protected boolean wantClientAuth = false; - public JSSE14SocketFactory () { - super(); + JSSE14AllTrustingX509TrustManager atm; + + public JSSE14SocketFactory (JSSE14AllTrustingX509TrustManager atm) { + this.atm = atm; } /** @@ -104,11 +107,45 @@ if( trustAlgorithm == null ) { trustAlgorithm = algorithm; } + + String acceptUntrustedCertStr = (String)attributes.get("acceptUntrustedCertificates"); + boolean acceptUntrustedCert = false; + if ("true".equals(acceptUntrustedCertStr) || + "yes".equals(acceptUntrustedCertStr)) { + acceptUntrustedCert = true; + } + + TrustManager[] tms = getTrustManagers(keystoreType, trustAlgorithm); + + if (acceptUntrustedCert) { + + /* + * Get the first instance of an X509TrustManager and wrap the + * JSSE14PlugableX509TrustManager around it. + * Depends on the current implementation of SSLContext, which + * only supports X509TrustManagers. + */ + if (tms == null) { + TrustManagerFactory tmf = TrustManagerFactory.getInstance(trustAlgorithm); + tmf.init((KeyStore) null); + tms = tmf.getTrustManagers(); + } + if (tms != null) { + for (int i = 0; i < tms.length;i++) { + if (tms[i] instanceof X509TrustManager) { + atm.init((javax.net.ssl.X509TrustManager) tms[i]); + tms[i] = atm; + break; + } + } + } + } + // Create and init SSLContext SSLContext context = SSLContext.getInstance(protocol); context.init(getKeyManagers(keystoreType, algorithm, (String) attributes.get("keyAlias")), - getTrustManagers(keystoreType, trustAlgorithm), + tms, new SecureRandom()); // create proxy --- util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java (working copy) @@ -49,8 +49,8 @@ private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(JSSE15SocketFactory.class); - public JSSE15SocketFactory() { - super(); + public JSSE15SocketFactory(JSSE14AllTrustingX509TrustManager atm) { + super(atm); } --- util/java/org/apache/tomcat/util/net/jsse/JSSE13AllTrustingX509TrustManager.java (revision 0) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE13AllTrustingX509TrustManager.java (revision 0) @@ -0,0 +1,68 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.util.net.jsse; + + +import java.security.cert.X509Certificate; + +import com.sun.net.ssl.X509TrustManager; + +/** + * Wrapper for an X509TrustManager: Accepts all client certificates, even expired. + * + * @author Armin Häberling + */ +public class JSSE13AllTrustingX509TrustManager implements X509TrustManager { + + protected X509TrustManager tm; + protected boolean initialized = false; + + JSSE13AllTrustingX509TrustManager() { + } + + void init(X509TrustManager tm) { + this.tm = tm; + initialized = true; + } + + public boolean isInitialized() { + return initialized; + } + + public boolean isClientTrusted(X509Certificate[] chain) { + return true; + } + + public boolean isServerTrusted(X509Certificate[] chain) { + if (tm != null) { + return tm.isServerTrusted(chain); + } + return true; + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public boolean forceIsClientTrusted(X509Certificate[] chain) { + if (tm != null) { + return tm.isClientTrusted(chain); + } + return true; + } + +} --- util/java/org/apache/tomcat/util/net/jsse/JSSE14AllTrustingX509TrustManager.java (revision 0) +++ util/java/org/apache/tomcat/util/net/jsse/JSSE14AllTrustingX509TrustManager.java (revision 0) @@ -0,0 +1,69 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.util.net.jsse; + + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +/** + * Wrapper for an X509TrustManager: Accepts all client certificates, even expired. + * + * @author Armin Häberling + */ +public class JSSE14AllTrustingX509TrustManager implements X509TrustManager { + + protected X509TrustManager tm; + protected boolean initialized = false; + + JSSE14AllTrustingX509TrustManager() { + } + + void init(X509TrustManager tm) { + this.tm = tm; + initialized = true; + } + + public boolean isInitialized() { + return initialized; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + if (tm != null) { + tm.checkServerTrusted(chain,authType); + } + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void forceCheckClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + if (tm != null) { + tm.checkClientTrusted(chain,authType); + } + } + +} --- util/java/org/apache/tomcat/util/net/puretls/PureTLSSupport.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/puretls/PureTLSSupport.java (working copy) @@ -133,6 +133,10 @@ return HexUtils.convert(ssl_session); } + public Boolean isClientCertificateTrusted() { + return null; + } + } --- util/java/org/apache/tomcat/util/net/SSLSupport.java (revision 354820) +++ util/java/org/apache/tomcat/util/net/SSLSupport.java (working copy) @@ -46,6 +46,12 @@ * This one is a Tomcat extension to the Servlet spec. */ public static final String SESSION_ID_KEY = "javax.servlet.request.ssl_session"; + + /** + * The Request attribute key which returns if the client certificate is trusted + * Only used if untrusted certificates are accepted + */ + public static final String CLIENT_CERTIFICATE_TRUSTED = "javax.servlet.request.ClientCertificateTrusted"; /** * A mapping table to determine the number of effective bits in the key @@ -104,7 +110,14 @@ */ public String getSessionId() throws IOException; + /** + * Check if the client certificate is trusted + * @throws IOException + */ + public Boolean isClientCertificateTrusted() throws IOException; + + /** * Simple data class that represents the cipher being used, along with the * corresponding effective key size. The specified phrase must appear in the * name of the cipher suite to be recognized.