001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.tomcat.util.net.jsse;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.net.SocketException;
024: import java.security.cert.Certificate;
025: import java.security.cert.CertificateFactory;
026:
027: import javax.net.ssl.HandshakeCompletedEvent;
028: import javax.net.ssl.HandshakeCompletedListener;
029: import javax.net.ssl.SSLException;
030: import javax.net.ssl.SSLSession;
031: import javax.net.ssl.SSLSocket;
032: import javax.security.cert.X509Certificate;
033:
034: import org.apache.tomcat.util.net.SSLSupport;
035:
036: /** JSSESupport
037:
038: Concrete implementation class for JSSE
039: Support classes.
040:
041: This will only work with JDK 1.2 and up since it
042: depends on JDK 1.2's certificate support
043:
044: @author EKR
045: @author Craig R. McClanahan
046: @author Filip Hanik
047: Parts cribbed from JSSECertCompat
048: Parts cribbed from CertificatesValve
049: */
050:
051: class JSSESupport implements SSLSupport {
052:
053: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
054: .getLog(JSSESupport.class);
055:
056: protected SSLSocket ssl;
057: protected SSLSession session;
058:
059: Listener listener = new Listener();
060:
061: JSSESupport(SSLSocket sock) {
062: ssl = sock;
063: session = sock.getSession();
064: sock.addHandshakeCompletedListener(listener);
065: }
066:
067: JSSESupport(SSLSession session) {
068: this .session = session;
069: }
070:
071: public String getCipherSuite() throws IOException {
072: // Look up the current SSLSession
073: if (session == null)
074: return null;
075: return session.getCipherSuite();
076: }
077:
078: public Object[] getPeerCertificateChain() throws IOException {
079: return getPeerCertificateChain(false);
080: }
081:
082: protected java.security.cert.X509Certificate[] getX509Certificates(
083: SSLSession session) throws IOException {
084: Certificate[] certs = null;
085: try {
086: certs = session.getPeerCertificates();
087: } catch (Throwable t) {
088: log.debug("Error getting client certs", t);
089: return null;
090: }
091: if (certs == null)
092: return null;
093:
094: java.security.cert.X509Certificate[] x509Certs = new java.security.cert.X509Certificate[certs.length];
095: for (int i = 0; i < certs.length; i++) {
096: if (certs[i] instanceof java.security.cert.X509Certificate) {
097: // always currently true with the JSSE 1.1.x
098: x509Certs[i] = (java.security.cert.X509Certificate) certs[i];
099: } else {
100: try {
101: byte[] buffer = certs[i].getEncoded();
102: CertificateFactory cf = CertificateFactory
103: .getInstance("X.509");
104: ByteArrayInputStream stream = new ByteArrayInputStream(
105: buffer);
106: x509Certs[i] = (java.security.cert.X509Certificate) cf
107: .generateCertificate(stream);
108: } catch (Exception ex) {
109: log.info("Error translating cert " + certs[i], ex);
110: return null;
111: }
112: }
113: if (log.isTraceEnabled())
114: log.trace("Cert #" + i + " = " + x509Certs[i]);
115: }
116: if (x509Certs.length < 1)
117: return null;
118: return x509Certs;
119: }
120:
121: public Object[] getPeerCertificateChain(boolean force)
122: throws IOException {
123: // Look up the current SSLSession
124: if (session == null)
125: return null;
126:
127: // Convert JSSE's certificate format to the ones we need
128: X509Certificate[] jsseCerts = null;
129: try {
130: jsseCerts = session.getPeerCertificateChain();
131: } catch (Exception bex) {
132: // ignore.
133: }
134: if (jsseCerts == null)
135: jsseCerts = new X509Certificate[0];
136: if (jsseCerts.length <= 0 && force) {
137: session.invalidate();
138: handShake();
139: session = ssl.getSession();
140: }
141: return getX509Certificates(session);
142: }
143:
144: protected void handShake() throws IOException {
145: if (ssl.getWantClientAuth()) {
146: log.debug("No client cert sent for want");
147: } else {
148: ssl.setNeedClientAuth(true);
149: }
150:
151: InputStream in = ssl.getInputStream();
152: int oldTimeout = ssl.getSoTimeout();
153: ssl.setSoTimeout(1000);
154: byte[] b = new byte[0];
155: listener.reset();
156: ssl.startHandshake();
157: int maxTries = 60; // 60 * 1000 = example 1 minute time out
158: for (int i = 0; i < maxTries; i++) {
159: if (log.isTraceEnabled())
160: log.trace("Reading for try #" + i);
161: try {
162: int x = in.read(b);
163: } catch (SSLException sslex) {
164: log.info("SSL Error getting client Certs", sslex);
165: throw sslex;
166: } catch (IOException e) {
167: // ignore - presumably the timeout
168: }
169: if (listener.completed) {
170: break;
171: }
172: }
173: ssl.setSoTimeout(oldTimeout);
174: if (listener.completed == false) {
175: throw new SocketException("SSL Cert handshake timeout");
176: }
177:
178: }
179:
180: /**
181: * Copied from <code>org.apache.catalina.valves.CertificateValve</code>
182: */
183: public Integer getKeySize() throws IOException {
184: // Look up the current SSLSession
185: SSLSupport.CipherData c_aux[] = ciphers;
186: if (session == null)
187: return null;
188: Integer keySize = (Integer) session.getValue(KEY_SIZE_KEY);
189: if (keySize == null) {
190: int size = 0;
191: String cipherSuite = session.getCipherSuite();
192: for (int i = 0; i < c_aux.length; i++) {
193: if (cipherSuite.indexOf(c_aux[i].phrase) >= 0) {
194: size = c_aux[i].keySize;
195: break;
196: }
197: }
198: keySize = new Integer(size);
199: session.putValue(KEY_SIZE_KEY, keySize);
200: }
201: return keySize;
202: }
203:
204: public String getSessionId() throws IOException {
205: // Look up the current SSLSession
206: if (session == null)
207: return null;
208: // Expose ssl_session (getId)
209: byte[] ssl_session = session.getId();
210: if (ssl_session == null)
211: return null;
212: StringBuffer buf = new StringBuffer("");
213: for (int x = 0; x < ssl_session.length; x++) {
214: String digit = Integer.toHexString((int) ssl_session[x]);
215: if (digit.length() < 2)
216: buf.append('0');
217: if (digit.length() > 2)
218: digit = digit.substring(digit.length() - 2);
219: buf.append(digit);
220: }
221: return buf.toString();
222: }
223:
224: private static class Listener implements HandshakeCompletedListener {
225: volatile boolean completed = false;
226:
227: public void handshakeCompleted(HandshakeCompletedEvent event) {
228: completed = true;
229: }
230:
231: void reset() {
232: completed = false;
233: }
234: }
235:
236: }
|