1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.tomcat.util.net.jsse;
19
20 import java.io.ByteArrayInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.SocketException;
24 import java.security.cert.Certificate;
25 import java.security.cert.CertificateFactory;
26
27 import javax.net.ssl.HandshakeCompletedEvent;
28 import javax.net.ssl.HandshakeCompletedListener;
29 import javax.net.ssl.SSLException;
30 import javax.net.ssl.SSLSession;
31 import javax.net.ssl.SSLSocket;
32 import javax.security.cert.X509Certificate;
33
34 import org.apache.tomcat.util.net.SSLSupport;
35
36 /** JSSESupport
37
38 Concrete implementation class for JSSE
39 Support classes.
40
41 This will only work with JDK 1.2 and up since it
42 depends on JDK 1.2's certificate support
43
44 @author EKR
45 @author Craig R. McClanahan
46 @author Filip Hanik
47 Parts cribbed from JSSECertCompat
48 Parts cribbed from CertificatesValve
49 */
50
51 class JSSESupport implements SSLSupport {
52
53 private static org.apache.juli.logging.Log log =
54 org.apache.juli.logging.LogFactory.getLog(JSSESupport.class);
55
56 protected SSLSocket ssl;
57 protected SSLSession session;
58
59 Listener listener = new Listener();
60
61 JSSESupport(SSLSocket sock){
62 ssl=sock;
63 session = sock.getSession();
64 sock.addHandshakeCompletedListener(listener);
65 }
66
67 JSSESupport(SSLSession session) {
68 this.session = session;
69 }
70
71 public String getCipherSuite() throws IOException {
72 // Look up the current SSLSession
73 if (session == null)
74 return null;
75 return session.getCipherSuite();
76 }
77
78 public Object[] getPeerCertificateChain()
79 throws IOException {
80 return getPeerCertificateChain(false);
81 }
82
83 protected java.security.cert.X509Certificate [] getX509Certificates(SSLSession session)
84 throws IOException {
85 Certificate [] certs=null;
86 try {
87 certs = session.getPeerCertificates();
88 } catch( Throwable t ) {
89 log.debug("Error getting client certs",t);
90 return null;
91 }
92 if( certs==null ) return null;
93
94 java.security.cert.X509Certificate [] x509Certs =
95 new java.security.cert.X509Certificate[certs.length];
96 for(int i=0; i < certs.length; i++) {
97 if (certs[i] instanceof java.security.cert.X509Certificate ) {
98 // always currently true with the JSSE 1.1.x
99 x509Certs[i] = (java.security.cert.X509Certificate) certs[i];
100 } else {
101 try {
102 byte [] buffer = certs[i].getEncoded();
103 CertificateFactory cf =
104 CertificateFactory.getInstance("X.509");
105 ByteArrayInputStream stream =
106 new ByteArrayInputStream(buffer);
107 x509Certs[i] = (java.security.cert.X509Certificate) cf.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()
184 throws IOException {
185 // Look up the current SSLSession
186 SSLSupport.CipherData c_aux[]=ciphers;
187 if (session == null)
188 return null;
189 Integer keySize = (Integer) session.getValue(KEY_SIZE_KEY);
190 if (keySize == null) {
191 int size = 0;
192 String cipherSuite = session.getCipherSuite();
193 for (int i = 0; i < c_aux.length; i++) {
194 if (cipherSuite.indexOf(c_aux[i].phrase) >= 0) {
195 size = c_aux[i].keySize;
196 break;
197 }
198 }
199 keySize = new Integer(size);
200 session.putValue(KEY_SIZE_KEY, keySize);
201 }
202 return keySize;
203 }
204
205 public String getSessionId()
206 throws IOException {
207 // Look up the current SSLSession
208 if (session == null)
209 return null;
210 // Expose ssl_session (getId)
211 byte [] ssl_session = session.getId();
212 if ( ssl_session == null)
213 return null;
214 StringBuffer buf=new StringBuffer("");
215 for(int x=0; x<ssl_session.length; x++) {
216 String digit=Integer.toHexString((int)ssl_session[x]);
217 if (digit.length()<2) buf.append('0');
218 if (digit.length()>2) digit=digit.substring(digit.length()-2);
219 buf.append(digit);
220 }
221 return buf.toString();
222 }
223
224
225 private static class Listener implements HandshakeCompletedListener {
226 volatile boolean completed = false;
227 public void handshakeCompleted(HandshakeCompletedEvent event) {
228 completed = true;
229 }
230 void reset() {
231 completed = false;
232 }
233 }
234
235 }
236