View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.ssl;
21  
22  import java.net.InetSocketAddress;
23  import java.nio.ByteBuffer;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentLinkedQueue;
26  
27  import javax.net.ssl.SSLEngine;
28  import javax.net.ssl.SSLEngineResult;
29  import javax.net.ssl.SSLException;
30  import javax.net.ssl.SSLHandshakeException;
31  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
32  import javax.net.ssl.SSLEngineResult.Status;
33  
34  import org.apache.mina.core.buffer.IoBuffer;
35  import org.apache.mina.core.filterchain.IoFilterEvent;
36  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
37  import org.apache.mina.core.future.DefaultWriteFuture;
38  import org.apache.mina.core.future.WriteFuture;
39  import org.apache.mina.core.session.IoEventType;
40  import org.apache.mina.core.session.IoSession;
41  import org.apache.mina.core.write.DefaultWriteRequest;
42  import org.apache.mina.core.write.WriteRequest;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  /**
47   * A helper class using the SSLEngine API to decrypt/encrypt data.
48   * <p/>
49   * Each connection has a SSLEngine that is used through the lifetime of the connection.
50   * We allocate buffers for use as the outbound and inbound network buffers.
51   * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
52   * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
53   * <p/>
54   * This class is not to be used by any client, it's closely associated with the SSL Filter.
55   * None of its methods are public as they should not be used by any other class but from
56   * the SslFilter class, in the same package
57   *
58   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
59   */
60  /** No qualifier*/
61  class SslHandler {
62      /** A logger for this class */
63      private final static Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
64  
65      /** The SSL Filter which has created this handler */
66      private final SslFilter sslFilter;
67  
68      /** The current session */
69      private final IoSession session;
70  
71      private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
72  
73      private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
74  
75      /** A queue used to stack all the incoming data until the SSL session is established */
76      private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
77  
78      private SSLEngine sslEngine;
79  
80      /**
81       * Encrypted data from the net
82       */
83      private IoBuffer inNetBuffer;
84  
85      /**
86       * Encrypted data to be written to the net
87       */
88      private IoBuffer outNetBuffer;
89  
90      /**
91       * Application cleartext data to be read by application
92       */
93      private IoBuffer appBuffer;
94  
95      /**
96       * Empty buffer used during initial handshake and close operations
97       */
98      private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
99  
100     private SSLEngineResult.HandshakeStatus handshakeStatus;
101 
102     /**
103      * A flag set to true when the first SSL handshake has been completed
104      * This is used to avoid sending a notification to the application handler
105      * when we switch to a SECURE or UNSECURE session.
106      */
107     private boolean firstSSLNegociation;
108 
109     /** A flag set to true when a SSL Handshake has been completed */
110     private boolean handshakeComplete;
111 
112     /** A flag used to indicate to the SslFilter that the buffer
113      * it will write is already encrypted (this will be the case
114      * for data being produced during the handshake). */
115     private boolean writingEncryptedData;
116 
117     /**
118      * Create a new SSL Handler, and initialize it.
119      *
120      * @param sslContext
121      * @throws SSLException
122      */
123     /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) throws SSLException {
124         this.sslFilter = sslFilter;
125         this.session = session;
126     }
127 
128     /**
129      * Initialize the SSL handshake.
130      *
131      * @throws SSLException If the underlying SSLEngine handshake initialization failed
132      */
133     /* no qualifier */void init() throws SSLException {
134         if (sslEngine != null) {
135             // We already have a SSL engine created, no need to create a new one
136             return;
137         }
138 
139         LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
140 
141         InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
142 
143         // Create the SSL engine here
144         if (peer == null) {
145             sslEngine = sslFilter.sslContext.createSSLEngine();
146         } else {
147             sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
148         }
149 
150         // Initialize the engine in client mode if necessary
151         sslEngine.setUseClientMode(sslFilter.isUseClientMode());
152 
153         // Initialize the different SslEngine modes
154         if (!sslEngine.getUseClientMode()) {
155             // Those parameters are only valid when in server mode
156             if (sslFilter.isWantClientAuth()) {
157                 sslEngine.setWantClientAuth(true);
158             }
159 
160             if (sslFilter.isNeedClientAuth()) {
161                 sslEngine.setNeedClientAuth(true);
162             }
163         }
164 
165         // Set the cipher suite to use by this SslEngine instance
166         if (sslFilter.getEnabledCipherSuites() != null) {
167             sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
168         }
169 
170         // Set the list of enabled protocols
171         if (sslFilter.getEnabledProtocols() != null) {
172             sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
173         }
174 
175         // TODO : we may not need to call this method...
176         // However, if we don't call it here, the tests are failing. Why?
177         sslEngine.beginHandshake();
178 
179         handshakeStatus = sslEngine.getHandshakeStatus();
180 
181         // Default value
182         writingEncryptedData = false;
183 
184         // We haven't yet started a SSL negotiation
185         // set the flags accordingly
186         firstSSLNegociation = true;
187         handshakeComplete = false;
188 
189         if (LOGGER.isDebugEnabled()) {
190             LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
191         }
192     }
193 
194     /**
195      * Release allocated buffers.
196      */
197     /* no qualifier */void destroy() {
198         if (sslEngine == null) {
199             return;
200         }
201 
202         // Close inbound and flush all remaining data if available.
203         try {
204             sslEngine.closeInbound();
205         } catch (SSLException e) {
206             LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
207         }
208 
209         if (outNetBuffer != null) {
210             outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
211         } else {
212             createOutNetBuffer(0);
213         }
214         try {
215             do {
216                 outNetBuffer.clear();
217             } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
218         } catch (SSLException e) {
219             // Ignore.
220         } finally {
221             destroyOutNetBuffer();
222         }
223 
224         sslEngine.closeOutbound();
225         sslEngine = null;
226 
227         preHandshakeEventQueue.clear();
228     }
229 
230     private void destroyOutNetBuffer() {
231         outNetBuffer.free();
232         outNetBuffer = null;
233     }
234 
235     /**
236      * @return The SSL filter which has created this handler
237      */
238     /* no qualifier */SslFilter getSslFilter() {
239         return sslFilter;
240     }
241 
242     /* no qualifier */IoSession getSession() {
243         return session;
244     }
245 
246     /**
247      * Check if we are writing encrypted data.
248      */
249     /* no qualifier */boolean isWritingEncryptedData() {
250         return writingEncryptedData;
251     }
252 
253     /**
254      * Check if handshake is completed.
255      */
256     /* no qualifier */boolean isHandshakeComplete() {
257         return handshakeComplete;
258     }
259 
260     /* no qualifier */boolean isInboundDone() {
261         return sslEngine == null || sslEngine.isInboundDone();
262     }
263 
264     /* no qualifier */boolean isOutboundDone() {
265         return sslEngine == null || sslEngine.isOutboundDone();
266     }
267 
268     /**
269      * Check if there is any need to complete handshake.
270      */
271     /* no qualifier */boolean needToCompleteHandshake() {
272         return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
273     }
274 
275     /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
276         preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
277     }
278 
279     /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
280         IoFilterEvent scheduledWrite;
281 
282         while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
283             sslFilter
284                     .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
285         }
286     }
287 
288     /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
289         filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
290     }
291 
292     /**
293      * Push the newly received data into a queue, waiting for the SSL session
294      * to be fully established
295      *
296      * @param nextFilter The next filter to call
297      * @param message The incoming data
298      */
299     /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
300         messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
301     }
302 
303     /* no qualifier */void flushScheduledEvents() {
304         // Fire events only when no lock is hold for this handler.
305         if (Thread.holdsLock(this)) {
306             return;
307         }
308 
309         IoFilterEvent event;
310 
311         // We need synchronization here inevitably because filterWrite can be
312         // called simultaneously and cause 'bad record MAC' integrity error.
313         synchronized (this) {
314             while ((event = filterWriteEventQueue.poll()) != null) {
315                 NextFilter nextFilter = event.getNextFilter();
316                 nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
317             }
318         }
319 
320         while ((event = messageReceivedEventQueue.poll()) != null) {
321             NextFilter nextFilter = event.getNextFilter();
322             nextFilter.messageReceived(session, event.getParameter());
323         }
324     }
325 
326     /**
327      * Call when data are read from net. It will perform the initial hanshake or decrypt
328      * the data if SSL has been initialiaed.
329      * 
330      * @param buf buffer to decrypt
331      * @param nextFilter Next filter in chain
332      * @throws SSLException on errors
333      */
334     /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
335         if (LOGGER.isDebugEnabled()) {
336             if (!isOutboundDone()) {
337                 LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
338             } else {
339                 LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
340             }
341         }
342 
343         // append buf to inNetBuffer
344         if (inNetBuffer == null) {
345             inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
346         }
347 
348         inNetBuffer.put(buf);
349 
350         if (!handshakeComplete) {
351             handshake(nextFilter);
352         } else {
353             // Prepare the net data for reading.
354             inNetBuffer.flip();
355 
356             if (!inNetBuffer.hasRemaining()) {
357                 return;
358             }
359 
360             SSLEngineResult res = unwrap();
361 
362             // prepare to be written again
363             if (inNetBuffer.hasRemaining()) {
364                 inNetBuffer.compact();
365             } else {
366                 inNetBuffer = null;
367             }
368 
369             checkStatus(res);
370 
371             renegotiateIfNeeded(nextFilter, res);
372         }
373 
374         if (isInboundDone()) {
375             // Rewind the MINA buffer if not all data is processed and inbound
376             // is finished.
377             int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
378             buf.position(buf.position() - inNetBufferPosition);
379             inNetBuffer = null;
380         }
381     }
382 
383     /**
384      * Get decrypted application data.
385      * 
386      * @return buffer with data
387      */
388     /* no qualifier */IoBuffer fetchAppBuffer() {
389         IoBuffer appBuffer = this.appBuffer.flip();
390         this.appBuffer = null;
391         return appBuffer;
392     }
393 
394     /**
395      * Get encrypted data to be sent.
396      * 
397      * @return buffer with data
398      */
399     /* no qualifier */IoBuffer fetchOutNetBuffer() {
400         IoBuffer answer = outNetBuffer;
401         if (answer == null) {
402             return emptyBuffer;
403         }
404 
405         outNetBuffer = null;
406         return answer.shrink();
407     }
408 
409     /**
410      * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
411      * 
412      * @param src
413      *            data to encrypt
414      * @throws SSLException
415      *             on errors
416      */
417     /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
418         if (!handshakeComplete) {
419             throw new IllegalStateException();
420         }
421 
422         if (!src.hasRemaining()) {
423             if (outNetBuffer == null) {
424                 outNetBuffer = emptyBuffer;
425             }
426             return;
427         }
428 
429         createOutNetBuffer(src.remaining());
430 
431         // Loop until there is no more data in src
432         while (src.hasRemaining()) {
433 
434             SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
435             if (result.getStatus() == SSLEngineResult.Status.OK) {
436                 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
437                     doTasks();
438                 }
439             } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
440                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
441                 outNetBuffer.limit(outNetBuffer.capacity());
442             } else {
443                 throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
444                         + "outNetBuffer: " + outNetBuffer);
445             }
446         }
447 
448         outNetBuffer.flip();
449     }
450 
451     /**
452      * Start SSL shutdown process.
453      * 
454      * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
455      *         shutdown process is already finished.
456      * @throws SSLException
457      *             on errors
458      */
459     /* no qualifier */boolean closeOutbound() throws SSLException {
460         if (sslEngine == null || sslEngine.isOutboundDone()) {
461             return false;
462         }
463 
464         sslEngine.closeOutbound();
465 
466         createOutNetBuffer(0);
467         SSLEngineResult result;
468         for (;;) {
469             result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
470             if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
471                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
472                 outNetBuffer.limit(outNetBuffer.capacity());
473             } else {
474                 break;
475             }
476         }
477 
478         if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
479             throw new SSLException("Improper close state: " + result);
480         }
481         outNetBuffer.flip();
482         return true;
483     }
484 
485     /**
486      * @param res
487      * @throws SSLException
488      */
489     private void checkStatus(SSLEngineResult res) throws SSLException {
490 
491         SSLEngineResult.Status status = res.getStatus();
492 
493         /*
494          * The status may be:
495          * OK - Normal operation
496          * OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
497          * packet size.
498          * UNDERFLOW - Need to read more data from the socket. It's normal.
499          * CLOSED - The other peer closed the socket. Also normal.
500          */
501         if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
502             throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
503                     + "appBuffer: " + appBuffer);
504         }
505     }
506 
507     /**
508      * Perform any handshaking processing.
509      */
510     /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
511         for (;;) {
512             switch (handshakeStatus) {
513             case FINISHED:
514             case NOT_HANDSHAKING:
515                 if (LOGGER.isDebugEnabled()) {
516                     LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
517                 }
518 
519                 session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
520                 handshakeComplete = true;
521 
522                 // Send the SECURE message only if it's the first SSL handshake
523                 if (firstSSLNegociation && session.containsAttribute(SslFilter.USE_NOTIFICATION)) {
524                     // SESSION_SECURED is fired only when it's the first handshake
525                     firstSSLNegociation = false;
526                     scheduleMessageReceived(nextFilter, SslFilter.SESSION_SECURED);
527                 }
528 
529                 if (LOGGER.isDebugEnabled()) {
530                     if (!isOutboundDone()) {
531                         LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
532                     } else {
533                         LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
534                     }
535                 }
536 
537                 return;
538 
539             case NEED_TASK:
540                 if (LOGGER.isDebugEnabled()) {
541                     LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
542                 }
543 
544                 handshakeStatus = doTasks();
545                 break;
546 
547             case NEED_UNWRAP:
548                 if (LOGGER.isDebugEnabled()) {
549                     LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
550                 }
551                 // we need more data read
552                 SSLEngineResult.Status status = unwrapHandshake(nextFilter);
553 
554                 if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
555                         && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
556                     // We need more data or the session is closed
557                     return;
558                 }
559 
560                 break;
561 
562             case NEED_WRAP:
563                 if (LOGGER.isDebugEnabled()) {
564                     LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
565                 }
566 
567                 // First make sure that the out buffer is completely empty.
568                 // Since we
569                 // cannot call wrap with data left on the buffer
570                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
571                     return;
572                 }
573 
574                 SSLEngineResult result;
575                 createOutNetBuffer(0);
576 
577                 for (;;) {
578                     result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
579                     if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
580                         outNetBuffer.capacity(outNetBuffer.capacity() << 1);
581                         outNetBuffer.limit(outNetBuffer.capacity());
582                     } else {
583                         break;
584                     }
585                 }
586 
587                 outNetBuffer.flip();
588                 handshakeStatus = result.getHandshakeStatus();
589                 writeNetBuffer(nextFilter);
590                 break;
591 
592             default:
593                 String msg = "Invalid Handshaking State" + handshakeStatus
594                         + " while processing the Handshake for session " + session.getId();
595                 LOGGER.error(msg);
596                 throw new IllegalStateException(msg);
597             }
598         }
599     }
600 
601     private void createOutNetBuffer(int expectedRemaining) {
602         // SSLEngine requires us to allocate unnecessarily big buffer
603         // even for small data. *Shrug*
604         int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
605 
606         if (outNetBuffer != null) {
607             outNetBuffer.capacity(capacity);
608         } else {
609             outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
610         }
611     }
612 
613     /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
614         // Check if any net data needed to be writen
615         if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
616             // no; bail out
617             return null;
618         }
619 
620         // set flag that we are writing encrypted data
621         // (used in SSLFilter.filterWrite())
622         writingEncryptedData = true;
623 
624         // write net data
625         WriteFuture writeFuture = null;
626 
627         try {
628             IoBuffer writeBuffer = fetchOutNetBuffer();
629             writeFuture = new DefaultWriteFuture(session);
630             sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
631 
632             // loop while more writes required to complete handshake
633             while (needToCompleteHandshake()) {
634                 try {
635                     handshake(nextFilter);
636                 } catch (SSLException ssle) {
637                     SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
638                     newSsle.initCause(ssle);
639                     throw newSsle;
640                 }
641 
642                 IoBuffer outNetBuffer = fetchOutNetBuffer();
643                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
644                     writeFuture = new DefaultWriteFuture(session);
645                     sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(outNetBuffer, writeFuture));
646                 }
647             }
648         } finally {
649             writingEncryptedData = false;
650         }
651 
652         return writeFuture;
653     }
654 
655     private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
656         // Prepare the net data for reading.
657         if (inNetBuffer != null) {
658             inNetBuffer.flip();
659         }
660 
661         if (inNetBuffer == null || !inNetBuffer.hasRemaining()) {
662             // Need more data.
663             return SSLEngineResult.Status.BUFFER_UNDERFLOW;
664         }
665 
666         SSLEngineResult res = unwrap();
667         handshakeStatus = res.getHandshakeStatus();
668 
669         checkStatus(res);
670 
671         // If handshake finished, no data was produced, and the status is still
672         // ok, try to unwrap more
673         if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED && res.getStatus() == SSLEngineResult.Status.OK
674                 && inNetBuffer.hasRemaining()) {
675             res = unwrap();
676 
677             // prepare to be written again
678             if (inNetBuffer.hasRemaining()) {
679                 inNetBuffer.compact();
680             } else {
681                 inNetBuffer = null;
682             }
683 
684             renegotiateIfNeeded(nextFilter, res);
685         } else {
686             // prepare to be written again
687             if (inNetBuffer.hasRemaining()) {
688                 inNetBuffer.compact();
689             } else {
690                 inNetBuffer = null;
691             }
692         }
693 
694         return res.getStatus();
695     }
696 
697     private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
698         if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
699                 && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
700                 && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
701             // Renegotiation required.
702             handshakeComplete = false;
703             handshakeStatus = res.getHandshakeStatus();
704             handshake(nextFilter);
705         }
706     }
707 
708     /**
709      * Decrypt the incoming buffer and move the decrypted data to an
710      * application buffer.
711      */
712     private SSLEngineResult unwrap() throws SSLException {
713         // We first have to create the application buffer if it does not exist
714         if (appBuffer == null) {
715             appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
716         } else {
717             // We already have one, just add the new data into it
718             appBuffer.expand(inNetBuffer.remaining());
719         }
720 
721         SSLEngineResult res;
722 
723         Status status = null;
724         HandshakeStatus handshakeStatus = null;
725 
726         do {
727             // Decode the incoming data
728             res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
729             status = res.getStatus();
730 
731             // We can be processing the Handshake
732             handshakeStatus = res.getHandshakeStatus();
733 
734             if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
735                 // We have to grow the target buffer, it's too small.
736                 // Then we can call the unwrap method again
737                 appBuffer.capacity(appBuffer.capacity() << 1);
738                 appBuffer.limit(appBuffer.capacity());
739                 continue;
740             }
741         } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
742                 && ((handshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) || (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
743 
744         return res;
745     }
746 
747     /**
748      * Do all the outstanding handshake tasks in the current Thread.
749      */
750     private SSLEngineResult.HandshakeStatus doTasks() {
751         /*
752          * We could run this in a separate thread, but I don't see the need for
753          * this when used from SSLFilter. Use thread filters in MINA instead?
754          */
755         Runnable runnable;
756         while ((runnable = sslEngine.getDelegatedTask()) != null) {
757             // TODO : we may have to use a thread pool here to improve the
758             // performances
759             runnable.run();
760         }
761         return sslEngine.getHandshakeStatus();
762     }
763 
764     /**
765      * Creates a new MINA buffer that is a deep copy of the remaining bytes in
766      * the given buffer (between index buf.position() and buf.limit())
767      * 
768      * @param src
769      *            the buffer to copy
770      * @return the new buffer, ready to read from
771      */
772     /* no qualifier */static IoBuffer copy(ByteBuffer src) {
773         IoBuffer copy = IoBuffer.allocate(src.remaining());
774         copy.put(src);
775         copy.flip();
776         return copy;
777     }
778 
779     public String toString() {
780         StringBuilder sb = new StringBuilder();
781 
782         sb.append("SSLStatus <");
783 
784         if (handshakeComplete) {
785             sb.append("SSL established");
786         } else {
787             sb.append("Processing Handshake").append("; ");
788             sb.append("Status : ").append(handshakeStatus).append("; ");
789         }
790 
791         sb.append(", ");
792         sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
793         sb.append(">");
794         return sb.toString();
795     }
796 
797 }