##### Coverage Report - com.sun.grizzly.util.SSLUtils

Classes in this File | Line Coverage | Branch Coverage | Complexity
SSLUtils |
43 % | 92/215
41 % | 51/125
| 0.0;0
SSLUtils$1 |
100 % | 2/2
N/A | N/A
| 0.0;0

1 |  |
     /*
2 |  |
      *
3 |  |
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 |  |
      *
5 |  |
      * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
6 |  |
      *
7 |  |
      * The contents of this file are subject to the terms of either the GNU
8 |  |
      * General Public License Version 2 only ("GPL") or the Common Development
9 |  |
      * and Distribution License("CDDL") (collectively, the "License").  You
10 |  |
      * may not use this file except in compliance with the License. You can obtain
11 |  |
      * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
12 |  |
      * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
13 |  |
      * language governing permissions and limitations under the License.
14 |  |
      *
15 |  |
      * When distributing the software, include this License Header Notice in each
16 |  |
      * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
17 |  |
      * Sun designates this particular file as subject to the "Classpath" exception
18 |  |
      * as provided by Sun in the GPL Version 2 section of the License file that
19 |  |
      * accompanied this code.  If applicable, add the following below the License
20 |  |
      * Header, with the fields enclosed by brackets [] replaced by your own
21 |  |
      * identifying information: "Portions Copyrighted [year]
22 |  |
      * [name of copyright owner]"
23 |  |
      *
24 |  |
      * Contributor(s):
25 |  |
      *
26 |  |
      * If you wish your version of this file to be governed by only the CDDL or
27 |  |
      * only the GPL Version 2, indicate your decision by adding "[Contributor]
28 |  |
      * elects to include this software in this distribution under the [CDDL or GPL
29 |  |
      * Version 2] license."  If you don't indicate a single choice of license, a
30 |  |
      * recipient has the option to distribute your version of this file under
31 |  |
      * either the CDDL, the GPL Version 2 or to extend the choice of license to
32 |  |
      * its licensees as provided above.  However, if you add GPL Version 2 code
33 |  |
      * and therefore, elected the GPL Version 2 license, then the option applies
34 |  |
      * only if the new code is made subject to such option by the copyright
35 |  |
      * holder.
36 |  |
      *
37 |  |
      */
38 |  |
39 |  |
     package com.sun.grizzly.util;
40 |  |
41 |  |
     import com.sun.grizzly.Controller;
42 |  |
     import java.io.ByteArrayInputStream;
43 |  |
     import java.io.EOFException;
44 |  |
     import java.io.IOException;
45 |  |
     import java.nio.ByteBuffer;
46 |  |
     import java.nio.channels.SelectableChannel;
47 |  |
     import java.security.cert.Certificate;
48 |  |
     import java.security.cert.CertificateFactory;
49 |  |
     import java.security.cert.X509Certificate;
50 |  |
     import java.util.logging.Level;
51 |  |
     import java.util.logging.Logger;
52 |  |
     import javax.net.ssl.SSLEngine;
53 |  |
     import javax.net.ssl.SSLEngineResult;
54 |  |
     import javax.net.ssl.SSLEngineResult.HandshakeStatus;
55 |  |
     import javax.net.ssl.SSLEngineResult.Status;
56 |  |
57 |  |
     /**
58 |  |
      * SSL over NIO utility class. The class handle the SSLEngine operations
59 |  |
      * needed to support SSL over NIO.
60 |  |
      *
61 |  |
      * TODO: Create an object that Wrap SSLEngine and its associated buffers.
62 |  |
      *
63 |  |
      * @author Jeanfrancois Arcand
64 |  |
      */
65 | 0 |
     public class SSLUtils {
66 |  |
67 |  |
         /**
68 |  |
          * The maximum size a ByteBuffer can take.
69 |  |
          */
70 |  |
         public final static int MAX_BB_SIZE = 48 * 4096;
71 |  |
72 |  |
73 |  |
         /*
74 |  |
          * An empty ByteBuffer used for handshaking
75 |  |
          */
76 | 1 |
         protected final static ByteBuffer hsBB = ByteBuffer.allocate(0);
77 |  |
78 |  |
79 |  |
         /**
80 |  |
          * The time to wait before timing out when reading bytes
81 |  |
          */
82 | 1 |
         private static int readTimeout = 30000;
83 |  |
84 |  |
85 |  |
         /**
86 |  |
          * Read and decrypt bytes from the underlying SSL connections.
87 |  |
          * @param socketChannel underlying socket channel
88 |  |
          * @param sslEngine{@link SSLEngine}
89 |  |
          * @param byteBuffer buffer for application decrypted data
90 |  |
          * @param inputBB buffer for reading enrypted data from socket
91 |  |
          * @return  number of bytes read
92 |  |
          * @throws java.io.IOException
93 |  |
          */
94 |  |
         public static int doSecureRead(SelectableChannel channel, SSLEngine sslEngine,
95 |  |
                 ByteBuffer byteBuffer, ByteBuffer inputBB) throws IOException {
96 |  |
97 | 100 |
             int initialPosition = byteBuffer.position();
98 | 100 |
             int byteRead = 0;
99 |  |
100 |  |
             // We need to make sure the unwrap worked properly and we have all
101 |  |
             // the packets properly read. If the SSLEngine fail to unwrap all the
102 |  |
             // bytes, the byteBuffer will be empty event if some encrypted bytes
103 |  |
             // are available.
104 | 200 |
             while (byteBuffer.position() == initialPosition){
105 | 100 |
                 int currentRead = SSLUtils.doRead(channel, inputBB,
106 |  |
                         sslEngine,readTimeout);
107 |  |
108 | 100 |
                 if (currentRead > 0) {
109 | 100 |
                     byteRead += currentRead;
110 |  |
                 }
111 |  |
112 | 100 |
                 if (currentRead > 0 || inputBB.position() > 0) {
113 |  |
                     try{
114 | 100 |
                         byteBuffer = SSLUtils.unwrapAll(byteBuffer,
115 |  |
                                 inputBB, sslEngine);
116 |  |
117 | 100 |
                         if (currentRead == -1 &&
118 |  |
                                 byteBuffer.position() == initialPosition) {
119 |  |
                             // if last read was -1 and unwrap decoded nothing then return -1
120 | 0 |
                             byteRead = -1;
121 | 0 |
                             break;
122 |  |
                         }
123 | 0 |
                     } catch (IOException ex){
124 | 0 |
                         Logger logger = Controller.logger();
125 | 0 |
                         if ( logger.isLoggable(Level.FINE) )
126 | 0 |
                             logger.log(Level.FINE,"SSLUtils.unwrapAll",ex);
127 | 0 |
                         return -1;
128 | 100 |
                     }
129 | 0 |
                 } else if (currentRead == -1) {
130 | 0 |
                     byteRead = -1;
131 | 0 |
                     break;
132 |  |
                 } else {
133 |  |
                     break;
134 |  |
                 }
135 | 100 |
             }
136 |  |
137 | 100 |
             return byteRead;
138 |  |
         }
139 |  |
140 |  |
         /**
141 |  |
          * Read encrypted bytes using an{@link SSLEngine}.
142 |  |
          * @param channel The {@link SelectableChannel}
143 |  |
          * @param inputBB The byteBuffer to store encrypted bytes
144 |  |
          * @param sslEngine The{@link SSLEngine} uses to manage the
145 |  |
          *                  SSL operations.
146 |  |
          * @param timeout The Selector.select() timeout value. A value of 0 will
147 |  |
          *                be exectuted as a Selector.selectNow();
148 |  |
          * @return the bytes read.
149 |  |
          */
150 |  |
         public static int doRead(SelectableChannel channel, ByteBuffer inputBB,
151 |  |
                 SSLEngine sslEngine, int timeout) {
152 |  |
153 | 799 |
             if (channel == null) return -1;
154 |  |
155 |  |
             try {
156 | 799 |
                 int bytesRead = Utils.readWithTemporarySelector(channel,
157 |  |
                         inputBB, timeout);
158 |  |
159 | 799 |
                 if (bytesRead == -1) {
160 |  |
                     try {
161 | 0 |
                         sslEngine.closeInbound();
162 | 0 |
                     } catch (IOException ex) {
163 | 0 |
                     }
164 |  |
                 }
165 |  |
166 | 799 |
                 return bytesRead;
167 | 0 |
             } catch (Throwable t){
168 | 0 |
                 Logger logger = Controller.logger();
169 | 0 |
                 if ( logger.isLoggable(Level.FINEST) ){
170 | 0 |
                     logger.log(Level.FINEST,"doRead",t);
171 |  |
                 }
172 | 0 |
                 return -1;
173 |  |
             }
174 |  |
         }
175 |  |
176 |  |
177 |  |
         /**
178 |  |
          * Unwrap all encrypted bytes from <code>inputBB</code> to
179 |  |
          * {@link ByteBuffer} using the{@link SSLEngine}
180 |  |
          * @param byteBuffer the decrypted ByteBuffer
181 |  |
          * @param inputBB the encrypted ByteBuffer
182 |  |
          * @param sslEngine The SSLEngine used to manage the SSL operations.
183 |  |
          * @return the decrypted ByteBuffer
184 |  |
          * @throws java.io.IOException
185 |  |
          */
186 |  |
         public static ByteBuffer unwrapAll(ByteBuffer byteBuffer,
187 |  |
                 ByteBuffer inputBB, SSLEngine sslEngine) throws IOException{
188 |  |
189 | 53183 |
             SSLEngineResult result = null;
190 |  |
             do{
191 |  |
                 try{
192 | 600442 |
                    result = unwrap(byteBuffer,inputBB,sslEngine);
193 | 0 |
                 } catch (Throwable ex){
194 | 0 |
                     Logger logger = Controller.logger();
195 | 0 |
                     if ( logger.isLoggable(Level.FINE) ){
196 | 0 |
                         logger.log(Level.FINE,"unwrap",ex);
197 |  |
                     }
198 | 0 |
                     inputBB.compact();
199 | 600442 |
                 }
200 |  |
201 | 600442 |
                 if (result != null){
202 | 1 |
                     switch (result.getStatus()) {
203 |  |
204 |  |
                         case BUFFER_UNDERFLOW:
205 |  |
                         case CLOSED:
206 |  |
                             // Closed or need more data.
207 | 241 |
                             break;
208 |  |
                         case OK:
209 | 600201 |
                             if (result.getHandshakeStatus()
210 |  |
                                     == HandshakeStatus.NEED_TASK) {
211 | 0 |
                                 executeDelegatedTask(sslEngine);
212 |  |
                             }
213 |  |
                             break;
214 |  |
                         case BUFFER_OVERFLOW:
215 | 0 |
                              byteBuffer = reallocate(byteBuffer);
216 | 0 |
                              break;
217 |  |
                         default:
218 | 0 |
                             throw new
219 | 0 |
                                  IOException("Unwrap error: "+ result.getStatus());
220 |  |
                      }
221 |  |
                  }
222 |  |
             } while (inputBB.position() > 0 && result!= null &&
223 | 600442 |
                     result.getStatus() != Status.BUFFER_UNDERFLOW);
224 | 53183 |
             return byteBuffer;
225 |  |
         }
226 |  |
227 |  |
228 |  |
         /**
229 |  |
          * Unwrap available encrypted bytes from <code>inputBB</code> to
230 |  |
          * {@link ByteBuffer} using the{@link SSLEngine}
231 |  |
          * @param byteBuffer the decrypted ByteBuffer
232 |  |
          * @param inputBB the encrypted ByteBuffer
233 |  |
          * @param sslEngine The SSLEngine used to manage the SSL operations.
234 |  |
          * @return SSLEngineResult of the SSLEngine.unwrap operation.
235 |  |
          * @throws java.io.IOException
236 |  |
          */
237 |  |
         public static SSLEngineResult unwrap(ByteBuffer byteBuffer,
238 |  |
                 ByteBuffer inputBB, SSLEngine sslEngine) throws IOException{
239 |  |
240 |  |
             // Logging block
241 | 601505 |
             if (Controller.logger().isLoggable(Level.FINE)) {
242 | 0 |
                 Controller.logger().log(Level.FINE, "start unwrap. buffer: " +
243 |  |
                         byteBuffer + " secured: " + inputBB);
244 | 0 |
                 if (Controller.logger().isLoggable(Level.FINER)) {
245 | 0 |
                     StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
246 | 0 |
                     StringBuffer buffer = new StringBuffer();
247 | 0 |
                     for (StackTraceElement element : stackTraceElements) {
248 | 0 |
                         buffer.append('\n');
249 | 0 |
                         buffer.append(element);
250 |  |
                     }
251 |  |
252 | 0 |
                     Controller.logger().log(Level.FINER, buffer.toString());
253 |  |
                 }
254 |  |
             }
255 |  |
256 | 601505 |
             inputBB.flip();
257 | 601505 |
             SSLEngineResult result = sslEngine.unwrap(inputBB, byteBuffer);
258 | 601504 |
             inputBB.compact();
259 |  |
260 |  |
             // Logging block
261 | 601504 |
             if (Controller.logger().isLoggable(Level.FINE)) {
262 | 0 |
                 int bytesProduced = result.bytesProduced();
263 | 0 |
                 Controller.logger().log(Level.FINE, "after unwrap. buffer: " +
264 |  |
                         byteBuffer + " secured: " + inputBB + " consumed: " +
265 |  |
                         result.bytesConsumed() + " produced: " + bytesProduced +
266 |  |
                         " status: " + result.getStatus());
267 | 0 |
                 if (bytesProduced > 0 && Controller.logger().isLoggable(Level.FINER)) {
268 | 0 |
                     byteBuffer.position(byteBuffer.position() - bytesProduced);
269 | 0 |
                     byte[] producedBytes = new byte[bytesProduced];
270 | 0 |
                     byteBuffer.get(producedBytes);
271 | 0 |
                     Controller.logger().log(Level.FINER, new String(producedBytes));
272 |  |
                 }
273 |  |
             }
274 |  |
275 | 601504 |
             return result;
276 |  |
         }
277 |  |
278 |  |
279 |  |
         /**
280 |  |
          * Encrypt bytes.
281 |  |
          * @param byteBuffer the decrypted ByteBuffer
282 |  |
          * @param outputBB the encrypted ByteBuffer
283 |  |
          * @param sslEngine The SSLEngine used to manage the SSL operations.
284 |  |
          * @return SSLEngineResult of the SSLEngine.wrap operation.
285 |  |
          * @throws java.io.IOException
286 |  |
          */
287 |  |
         public static SSLEngineResult wrap(ByteBuffer byteBuffer,
288 |  |
                 ByteBuffer outputBB, SSLEngine sslEngine) throws IOException {
289 |  |
290 | 1179 |
             outputBB.clear();
291 | 1179 |
             SSLEngineResult result = sslEngine.wrap(byteBuffer, outputBB);
292 | 1179 |
             outputBB.flip();
293 | 1179 |
             return result;
294 |  |
         }
295 |  |
296 |  |
297 |  |
         /**
298 |  |
          * Resize a ByteBuffer.
299 |  |
          * @param byteBuffer  {@link ByteBuffer} to re-allocate
300 |  |
          * @return  {@link ByteBuffer} reallocted
301 |  |
          * @throws java.io.IOException
302 |  |
          */
303 |  |
         private static ByteBuffer reallocate(ByteBuffer byteBuffer)
304 |  |
                 throws IOException{
305 |  |
306 | 0 |
             if (byteBuffer.capacity() > MAX_BB_SIZE){
307 | 0 |
                 throw new IOException("Unwrap error: BUFFER_OVERFLOW");
308 |  |
             }
309 | 0 |
             ByteBuffer tmp = ByteBuffer.allocate(byteBuffer.capacity() * 2);
310 | 0 |
             byteBuffer.flip();
311 | 0 |
             tmp.put(byteBuffer);
312 | 0 |
             byteBuffer = tmp;
313 | 0 |
             return byteBuffer;
314 |  |
         }
315 |  |
316 |  |
317 |  |
         /**
318 |  |
          * Complete hanshakes operations.
319 |  |
          * @param sslEngine The SSLEngine used to manage the SSL operations.
320 |  |
          * @return SSLEngineResult.HandshakeStatus
321 |  |
          */
322 |  |
         public static SSLEngineResult.HandshakeStatus
323 |  |
                 executeDelegatedTask(SSLEngine sslEngine) {
324 |  |
325 |  |
             Runnable runnable;
326 | 728 |
             while ((runnable = sslEngine.getDelegatedTask()) != null) {
327 | 364 |
                 runnable.run();
328 |  |
             }
329 | 364 |
             return sslEngine.getHandshakeStatus();
330 |  |
         }
331 |  |
332 |  |
333 |  |
         /**
334 |  |
          * Perform an SSL handshake using the SSLEngine.
335 |  |
          * Note: If handshake was done successfully - outputBB will be cleared out,
336 |  |
          *       but this is *not* ready data to be written.
337 |  |
          *
338 |  |
          * @param channel the {@link SelectableChannel}
339 |  |
          * @param byteBuffer The application {@link ByteBuffer}
340 |  |
          * @param inputBB The encrypted input {@link ByteBuffer}
341 |  |
          * @param outputBB The encrypted output {@link ByteBuffer}
342 |  |
          * @param sslEngine The SSLEngine used.
343 |  |
          * @param handshakeStatus The current handshake status
344 |  |
          * @return byteBuffer the new ByteBuffer
345 |  |
          * @throws java.io.IOException
346 |  |
          * @throw IOException if the handshake fail.
347 |  |
          */
348 |  |
         public static ByteBuffer doHandshake(SelectableChannel channel,
349 |  |
                 ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer outputBB,
350 |  |
                 SSLEngine sslEngine, HandshakeStatus handshakeStatus)
351 |  |
                 throws IOException {
352 | 120 |
             return doHandshake(channel, byteBuffer, inputBB, outputBB,
353 |  |
                     sslEngine, handshakeStatus, readTimeout);
354 |  |
         }
355 |  |
356 |  |
357 |  |
         /**
358 |  |
          * Perform an SSL handshake using the SSLEngine.
359 |  |
          * Note: If handshake was done successfully - outputBB will be cleared out,
360 |  |
          *       but this is *not* ready data to be written.
361 |  |
          *
362 |  |
          * @param channel the {@link SelectableChannel}
363 |  |
          * @param byteBuffer The application {@link ByteBuffer}
364 |  |
          * @param inputBB The encrypted input {@link ByteBuffer}
365 |  |
          * @param outputBB The encrypted output {@link ByteBuffer}
366 |  |
          * @param sslEngine The SSLEngine used.
367 |  |
          * @param handshakeStatus The current handshake status
368 |  |
          * @param timeout
369 |  |
          * @return byteBuffer the new ByteBuffer
370 |  |
          * @throws java.io.IOException
371 |  |
          * @throws IOException if the handshake fail.
372 |  |
          */
373 |  |
         public static ByteBuffer doHandshake(SelectableChannel channel,
374 |  |
                 ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer outputBB,
375 |  |
                 SSLEngine sslEngine, HandshakeStatus handshakeStatus,int timeout)
376 |  |
                 throws IOException {
377 | 243 |
             return doHandshake(channel, byteBuffer, inputBB, outputBB,
378 |  |
                     sslEngine, handshakeStatus, timeout, inputBB.position() > 0);
379 |  |
         }
380 |  |
381 |  |
         /**
382 |  |
          * Perform an SSL handshake using the SSLEngine.
383 |  |
          * Note: If handshake was done successfully - outputBB will be cleared out,
384 |  |
          *       but this is *not* ready data to be written.
385 |  |
          *
386 |  |
          * @param channel the {@link SelectableChannel}
387 |  |
          * @param byteBuffer The application {@link ByteBuffer}
388 |  |
          * @param inputBB The encrypted input {@link ByteBuffer}
389 |  |
          * @param outputBB The encrypted output {@link ByteBuffer}
390 |  |
          * @param sslEngine The SSLEngine used.
391 |  |
          * @param handshakeStatus The current handshake status
392 |  |
          * @param timeout
393 |  |
          * @param useReadyBuffer does method need to read data before UNWRAP or use
394 |  |
          *                       a data from inputBB
395 |  |
          * @return byteBuffer the new ByteBuffer
396 |  |
          * @throws java.io.IOException
397 |  |
          * @throws IOException if the handshake fail.
398 |  |
          */
399 |  |
         public static ByteBuffer doHandshake(SelectableChannel channel,
400 |  |
                 ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer outputBB,
401 |  |
                 SSLEngine sslEngine, HandshakeStatus handshakeStatus,
402 |  |
                 int timeout,boolean useReadyBuffer)
403 |  |
                 throws IOException {
404 |  |
405 |  |
             SSLEngineResult result;
406 | 243 |
             int eof = timeout > 0 ? 0 : -1;
407 | 1303 |
             while (handshakeStatus != HandshakeStatus.FINISHED){
408 | 1181 |
                 switch (handshakeStatus) {
409 |  |
                    case NEED_UNWRAP:
410 | 699 |
                         if (!useReadyBuffer) {
411 | 699 |
                             if (doRead(channel, inputBB, sslEngine, timeout) <= eof) {
412 |  |
                                 try {
413 | 0 |
                                     sslEngine.closeInbound();
414 | 0 |
                                 } catch (IOException ex) {
415 | 0 |
                                     Logger logger = Controller.logger();
416 | 0 |
                                     if (logger.isLoggable(Level.FINE)) {
417 | 0 |
                                         logger.log(Level.FINE, "closeInbound", ex);
418 |  |
                                     }
419 | 0 |
                                 }
420 | 0 |
                                 throw new EOFException("Connection closed");
421 |  |
                             }
422 |  |
                         } else {
423 | 0 |
                             useReadyBuffer = false;
424 |  |
                         }
425 |  |
426 | 1427 |
                         while (handshakeStatus == HandshakeStatus.NEED_UNWRAP) {
427 | 1063 |
                             result = unwrap(byteBuffer,inputBB,sslEngine);
428 | 1062 |
                             handshakeStatus = result.getHandshakeStatus();
429 |  |
430 | 1062 |
                             if (result.getStatus() == Status.BUFFER_UNDERFLOW){
431 | 214 |
                                 break;
432 |  |
                             }
433 |  |
434 | 848 |
                             switch (result.getStatus()) {
435 |  |
                                 case OK:
436 | 1 |
                                     switch (handshakeStatus) {
437 |  |
                                         case NOT_HANDSHAKING:
438 | 0 |
                                             throw new IOException("No Hanshake");
439 |  |
440 |  |
                                         case NEED_TASK:
441 | 364 |
                                             handshakeStatus =
442 |  |
                                                     executeDelegatedTask(sslEngine);
443 | 364 |
                                             break;
444 |  |
445 |  |
                                         case FINISHED:
446 | 120 |
                                            return byteBuffer;
447 |  |
                                     }
448 | 728 |
                                     break;
449 |  |
                                 case BUFFER_OVERFLOW:
450 | 0 |
                                     byteBuffer = reallocate(byteBuffer);
451 | 0 |
                                     break;
452 |  |
                                 default:
453 | 0 |
                                     throw new IOException("Handshake exception: " +
454 |  |
                                             result.getStatus());
455 |  |
                             }
456 |  |
                         }
457 |  |
458 | 578 |
                         if (handshakeStatus != HandshakeStatus.NEED_WRAP) {
459 | 214 |
                             break;
460 |  |
                         }
461 |  |
                     case NEED_WRAP:
462 | 846 |
                         result = wrap(hsBB,outputBB,sslEngine);
463 | 846 |
                         handshakeStatus = result.getHandshakeStatus();
464 | 846 |
                         switch (result.getStatus()) {
465 |  |
                             case OK:
466 |  |
467 | 846 |
                                 if (handshakeStatus == HandshakeStatus.NEED_TASK) {
468 | 0 |
                                     handshakeStatus = executeDelegatedTask(sslEngine);
469 |  |
                                 }
470 |  |
471 |  |
                                 // Flush all Server bytes to the client.
472 | 846 |
                                 if (channel != null) {
473 | 846 |
                                     OutputWriter.flushChannel(
474 |  |
                                             channel, outputBB);
475 | 846 |
                                     outputBB.clear();
476 |  |
                                 }
477 |  |
                                 break;
478 |  |
                             default:
479 | 0 |
                                 throw new IOException("Handshaking error: "
480 |  |
                                         + result.getStatus());
481 |  |
                             }
482 |  |
                             break;
483 |  |
                     default:
484 | 0 |
                         throw new RuntimeException("Invalid Handshaking State" +
485 |  |
                                 handshakeStatus);
486 |  |
                 }
487 |  |
             }
488 | 122 |
             return byteBuffer;
489 |  |
         }
490 |  |
491 |  |
492 |  |
         /**
493 |  |
          * Get the peer certificate list by initiating a new handshake.
494 |  |
          * @param channel {@link SelectableChannel}
495 |  |
          * @param needClientAuth
496 |  |
          * @return Object[] An array of X509Certificate.
497 |  |
          * @throws java.io.IOException
498 |  |
          */
499 |  |
         public static Object[] doPeerCertificateChain(SelectableChannel channel,
500 |  |
                 ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer outputBB,
501 |  |
                 SSLEngine sslEngine, boolean needClientAuth, int timeout) throws IOException {
502 |  |
503 | 0 |
             Logger logger = Controller.logger();
504 |  |
505 | 0 |
             Certificate[] certs=null;
506 |  |
             try {
507 | 0 |
                 certs = sslEngine.getSession().getPeerCertificates();
508 | 0 |
             } catch( Throwable t ) {
509 | 0 |
                 if ( logger.isLoggable(Level.FINE))
510 | 0 |
                     logger.log(Level.FINE,"Error getting client certs",t);
511 | 0 |
             }
512 |  |
513 | 0 |
             if (certs == null && needClientAuth){
514 | 0 |
                 sslEngine.getSession().invalidate();
515 | 0 |
                 sslEngine.setNeedClientAuth(true);
516 | 0 |
                 sslEngine.beginHandshake();
517 |  |
518 | 0 |
                 ByteBuffer origBB = byteBuffer;
519 |  |
                 // In case the application hasn't read all the body bytes.
520 | 0 |
                 if ( origBB.position() != origBB.limit() ){
521 | 0 |
                     byteBuffer = ByteBuffer.allocate(origBB.capacity());
522 |  |
                 } else {
523 | 0 |
                     byteBuffer.clear();
524 |  |
                 }
525 | 0 |
                 outputBB.position(0);
526 | 0 |
                 outputBB.limit(0);
527 |  |
528 |  |
                 // We invalidate ssl seesion, so no need for unwrap
529 |  |
                 try{
530 | 0 |
                     doHandshake(channel, byteBuffer, inputBB, outputBB,
531 |  |
                             sslEngine, HandshakeStatus.NEED_WRAP, timeout);
532 | 0 |
                 } catch (Throwable ex){
533 | 0 |
                     if ( logger.isLoggable(Level.FINE))
534 | 0 |
                         logger.log(Level.FINE,"Error during handshake",ex);
535 | 0 |
                     return null;
536 |  |
                 } finally {
537 | 0 |
                     byteBuffer = origBB;
538 | 0 |
                     byteBuffer.clear();
539 | 0 |
                 }
540 |  |
541 |  |
                 try {
542 | 0 |
                     certs = sslEngine.getSession().getPeerCertificates();
543 | 0 |
                 } catch( Throwable t ) {
544 | 0 |
                     if ( logger.isLoggable(Level.FINE))
545 | 0 |
                         logger.log(Level.FINE,"Error getting client certs",t);
546 | 0 |
                 }
547 |  |
             }
548 |  |
549 | 0 |
             if( certs==null ) return null;
550 |  |
551 | 0 |
             X509Certificate[] x509Certs = new X509Certificate[certs.length];
552 | 0 |
             for(int i=0; i < certs.length; i++) {
553 | 0 |
                 if( certs[i] instanceof X509Certificate ) {
554 | 0 |
                     x509Certs[i] = (X509Certificate)certs[i];
555 |  |
                 } else {
556 |  |
                     try {
557 | 0 |
                         byte [] buffer = certs[i].getEncoded();
558 | 0 |
                         CertificateFactory cf =
559 |  |
                         CertificateFactory.getInstance("X.509");
560 | 0 |
                         ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
561 | 0 |
                         x509Certs[i] = (X509Certificate)
562 |  |
                         cf.generateCertificate(stream);
563 | 0 |
                     } catch(Exception ex) {
564 | 0 |
                         logger.log(Level.INFO,"Error translating cert " + certs[i],
565 |  |
                                          ex);
566 | 0 |
                         return null;
567 | 0 |
                     }
568 |  |
                 }
569 |  |
570 | 0 |
                 if(logger.isLoggable(Level.FINE))
571 | 0 |
                     logger.log(Level.FINE,"Cert #" + i + " = " + x509Certs[i]);
572 |  |
             }
573 |  |
574 | 0 |
             if(x509Certs.length < 1)
575 | 0 |
                 return null;
576 |  |
577 | 0 |
             return x509Certs;
578 |  |
         }
579 |  |
580 |  |
581 |  |
         /**
582 |  |
          * Allocate themandatory {@link ByteBuffer}s. Since the ByteBuffer
583 |  |
          * are maintaned on the {@link WorkerThread} lazily, this method
584 |  |
          * makes sure the ByteBuffers are properly allocated and configured.
585 |  |
          */
586 |  |
         public static void allocateThreadBuffers(int defaultBufferSize) {
587 | 53201 |
             final WorkerThread workerThread =
588 |  |
                     (WorkerThread)Thread.currentThread();
589 | 53201 |
             ByteBuffer byteBuffer = workerThread.getByteBuffer();
590 | 53201 |
             ByteBuffer outputBB = workerThread.getOutputBB();
591 | 53201 |
             ByteBuffer inputBB = workerThread.getInputBB();
592 |  |
593 | 53201 |
             int expectedSize = workerThread.getSSLEngine().getSession()
594 |  |
                 .getPacketBufferSize();
595 | 53201 |
             if (defaultBufferSize < expectedSize){
596 | 0 |
                 defaultBufferSize = expectedSize;
597 |  |
             }
598 |  |
599 | 53201 |
             if (inputBB != null && inputBB.capacity() < defaultBufferSize) {
600 | 0 |
                 ByteBuffer newBB = ByteBuffer.allocate(defaultBufferSize);
601 | 0 |
                 inputBB.flip();
602 | 0 |
                 newBB.put(inputBB);
603 | 0 |
                 inputBB = newBB;
604 | 0 |
             } else if (inputBB == null){
605 | 123 |
                 inputBB = ByteBuffer.allocate(defaultBufferSize);
606 |  |
             }
607 |  |
608 | 53201 |
             if (outputBB == null) {
609 | 153 |
                 outputBB = ByteBuffer.allocate(defaultBufferSize);
610 |  |
             }
611 |  |
612 | 53201 |
             if (byteBuffer == null){
613 | 0 |
                 byteBuffer = ByteBuffer.allocate(defaultBufferSize * 2);
614 |  |
             }
615 |  |
616 | 53201 |
             expectedSize = workerThread.getSSLEngine().getSession()
617 |  |
                 .getApplicationBufferSize();
618 | 53201 |
             if ( expectedSize > byteBuffer.capacity() ) {
619 | 20 |
                 ByteBuffer newBB = ByteBuffer.allocate(expectedSize);
620 | 20 |
                 byteBuffer.flip();
621 | 20 |
                 newBB.put(byteBuffer);
622 | 20 |
                 byteBuffer = newBB;
623 |  |
             }
624 |  |
625 | 53201 |
             workerThread.setInputBB(inputBB);
626 | 53201 |
             workerThread.setOutputBB(outputBB);
627 | 53201 |
             workerThread.setByteBuffer(byteBuffer);
628 |  |
629 | 53201 |
             outputBB.position(0);
630 | 53201 |
             outputBB.limit(0);
631 | 53201 |
         }
632 |  |
633 |  |
634 |  |
         public static int getReadTimeout() {
635 | 123 |
             return readTimeout;
636 |  |
         }
637 |  |
638 |  |
639 |  |
         public static void setReadTimeout(int aReadTimeout) {
640 | 0 |
             readTimeout = aReadTimeout;
641 | 0 |
         }
642 |  |
643 |  |
     }