001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.tomcat.util.net.jsse;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.net.SocketException;
023: import java.security.cert.Certificate;
024: import java.security.cert.CertificateFactory;
025: import java.security.cert.X509Certificate;
026:
027: import javax.net.ssl.HandshakeCompletedEvent;
028: import javax.net.ssl.HandshakeCompletedListener;
029: import javax.net.ssl.SSLException;
030: import javax.net.ssl.SSLPeerUnverifiedException;
031: import javax.net.ssl.SSLSession;
032: import javax.net.ssl.SSLSocket;
033:
034: /* JSSESupport
035:
036: Concrete implementation class for JSSE
037: Support classes.
038:
039: This will only work with JDK 1.2 and up since it
040: depends on JDK 1.2's certificate support
041:
042: @author EKR
043: @author Craig R. McClanahan
044: Parts cribbed from JSSECertCompat
045: Parts cribbed from CertificatesValve
046: */
047:
048: class JSSE14Support extends JSSESupport {
049:
050: private static org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
051: .getLog(JSSE14Support.class);
052:
053: Listener listener = new Listener();
054:
055: public JSSE14Support(SSLSocket sock) {
056: super (sock);
057: sock.addHandshakeCompletedListener(listener);
058: }
059:
060: protected void handShake() throws IOException {
061: if (ssl.getWantClientAuth()) {
062: logger.debug("No client cert sent for want");
063: } else {
064: ssl.setNeedClientAuth(true);
065: }
066: synchronousHandshake(ssl);
067: }
068:
069: /**
070: * JSSE in JDK 1.4 has an issue/feature that requires us to do a
071: * read() to get the client-cert. As suggested by Andreas
072: * Sterbenz
073: */
074: private void synchronousHandshake(SSLSocket socket)
075: throws IOException {
076: InputStream in = socket.getInputStream();
077: int oldTimeout = socket.getSoTimeout();
078: socket.setSoTimeout(1000);
079: byte[] b = new byte[0];
080: listener.reset();
081: socket.startHandshake();
082: int maxTries = 60; // 60 * 1000 = example 1 minute time out
083: for (int i = 0; i < maxTries; i++) {
084: if (logger.isTraceEnabled())
085: logger.trace("Reading for try #" + i);
086: try {
087: int x = in.read(b);
088: } catch (SSLException sslex) {
089: logger.info("SSL Error getting client Certs", sslex);
090: throw sslex;
091: } catch (IOException e) {
092: // ignore - presumably the timeout
093: }
094: if (listener.completed) {
095: break;
096: }
097: }
098: socket.setSoTimeout(oldTimeout);
099: if (listener.completed == false) {
100: throw new SocketException("SSL Cert handshake timeout");
101: }
102: }
103:
104: /** Return the X509certificates or null if we can't get them.
105: * XXX We should allow unverified certificates
106: */
107: protected X509Certificate[] getX509Certificates(SSLSession session)
108: throws IOException {
109: Certificate[] certs = null;
110: try {
111: certs = session.getPeerCertificates();
112: } catch (Throwable t) {
113: logger.debug("Error getting client certs", t);
114: return null;
115: }
116: if (certs == null)
117: return null;
118:
119: X509Certificate[] x509Certs = new X509Certificate[certs.length];
120: for (int i = 0; i < certs.length; i++) {
121: if (certs[i] instanceof X509Certificate) {
122: // always currently true with the JSSE 1.1.x
123: x509Certs[i] = (X509Certificate) certs[i];
124: } else {
125: try {
126: byte[] buffer = certs[i].getEncoded();
127: CertificateFactory cf = CertificateFactory
128: .getInstance("X.509");
129: ByteArrayInputStream stream = new ByteArrayInputStream(
130: buffer);
131: x509Certs[i] = (X509Certificate) cf
132: .generateCertificate(stream);
133: } catch (Exception ex) {
134: logger.info("Error translating cert " + certs[i],
135: ex);
136: return null;
137: }
138: }
139: if (logger.isTraceEnabled())
140: logger.trace("Cert #" + i + " = " + x509Certs[i]);
141: }
142: if (x509Certs.length < 1)
143: return null;
144: return x509Certs;
145: }
146:
147: private static class Listener implements HandshakeCompletedListener {
148: volatile boolean completed = false;
149:
150: public void handshakeCompleted(HandshakeCompletedEvent event) {
151: completed = true;
152: }
153:
154: void reset() {
155: completed = false;
156: }
157: }
158:
159: }
|