001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.filter.ssl; 021 022import java.net.InetSocketAddress; 023import java.util.ArrayList; 024import java.util.List; 025 026import javax.net.ssl.SSLContext; 027import javax.net.ssl.SSLEngine; 028import javax.net.ssl.SSLException; 029import javax.net.ssl.SSLHandshakeException; 030import javax.net.ssl.SSLSession; 031 032import org.apache.mina.core.buffer.IoBuffer; 033import org.apache.mina.core.filterchain.IoFilter; 034import org.apache.mina.core.filterchain.IoFilterAdapter; 035import org.apache.mina.core.filterchain.IoFilterChain; 036import org.apache.mina.core.future.DefaultWriteFuture; 037import org.apache.mina.core.future.IoFuture; 038import org.apache.mina.core.future.IoFutureListener; 039import org.apache.mina.core.future.WriteFuture; 040import org.apache.mina.core.service.IoAcceptor; 041import org.apache.mina.core.service.IoHandler; 042import org.apache.mina.core.session.AttributeKey; 043import org.apache.mina.core.session.IoSession; 044import org.apache.mina.core.write.WriteRequest; 045import org.apache.mina.core.write.WriteRequestWrapper; 046import org.apache.mina.core.write.WriteToClosedSessionException; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050/** 051 * An SSL filter that encrypts and decrypts the data exchanged in the session. 052 * Adding this filter triggers SSL handshake procedure immediately by sending 053 * a SSL 'hello' message, so you don't need to call 054 * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS 055 * (see below). If you don't want the handshake procedure to start 056 * immediately, please specify {@code false} as {@code autoStart} parameter in 057 * the constructor. 058 * <p> 059 * This filter uses an {@link SSLEngine} which was introduced in Java 5, so 060 * Java version 5 or above is mandatory to use this filter. And please note that 061 * this filter only works for TCP/IP connections. 062 * 063 * <h2>Implementing StartTLS</h2> 064 * <p> 065 * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS: 066 * <pre> 067 * public void messageReceived(IoSession session, Object message) { 068 * if (message instanceof MyStartTLSRequest) { 069 * // Insert SSLFilter to get ready for handshaking 070 * session.getFilterChain().addFirst(sslFilter); 071 * 072 * // Disable encryption temporarily. 073 * // This attribute will be removed by SSLFilter 074 * // inside the Session.write() call below. 075 * session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE); 076 * 077 * // Write StartTLSResponse which won't be encrypted. 078 * session.write(new MyStartTLSResponse(OK)); 079 * 080 * // Now DISABLE_ENCRYPTION_ONCE attribute is cleared. 081 * assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null; 082 * } 083 * } 084 * </pre> 085 * 086 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 087 * @org.apache.xbean.XBean 088 */ 089public class SslFilter extends IoFilterAdapter { 090 /** The logger */ 091 private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class); 092 093 /** 094 * A session attribute key that stores underlying {@link SSLSession} 095 * for each session. 096 */ 097 public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session"); 098 099 /** 100 * A session attribute key that makes next one write request bypass 101 * this filter (not encrypting the data). This is a marker attribute, 102 * which means that you can put whatever as its value. ({@link Boolean#TRUE} 103 * is preferred.) The attribute is automatically removed from the session 104 * attribute map as soon as {@link IoSession#write(Object)} is invoked, 105 * and therefore should be put again if you want to make more messages 106 * bypass this filter. This is especially useful when you implement 107 * StartTLS. 108 */ 109 public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce"); 110 111 /** 112 * A session attribute key that makes this filter to emit a 113 * {@link IoHandler#messageReceived(IoSession, Object)} event with a 114 * special message ({@link #SESSION_SECURED} or {@link #SESSION_UNSECURED}). 115 * This is a marker attribute, which means that you can put whatever as its 116 * value. ({@link Boolean#TRUE} is preferred.) By default, this filter 117 * doesn't emit any events related with SSL session flow control. 118 */ 119 public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification"); 120 121 /** 122 * A session attribute key that should be set to an {@link InetSocketAddress}. 123 * Setting this attribute causes 124 * {@link SSLContext#createSSLEngine(String, int)} to be called passing the 125 * hostname and port of the {@link InetSocketAddress} to get an 126 * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()} 127 * will be called. 128 * <br> 129 * Using this feature {@link SSLSession} objects may be cached and reused 130 * when in client mode. 131 * 132 * @see SSLContext#createSSLEngine(String, int) 133 */ 134 public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress"); 135 136 /** 137 * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)} 138 * event when the session is secured and its {@link #USE_NOTIFICATION} 139 * attribute is set. 140 */ 141 public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage("SESSION_SECURED"); 142 143 /** 144 * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)} 145 * event when the session is not secure anymore and its {@link #USE_NOTIFICATION} 146 * attribute is set. 147 */ 148 public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage("SESSION_UNSECURED"); 149 150 /** An attribute containing the next filter */ 151 private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter"); 152 153 private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler"); 154 155 /** The SslContext used */ 156 /* No qualifier */final SSLContext sslContext; 157 158 /** A flag used to tell the filter to start the handshake immediately */ 159 private final boolean autoStart; 160 161 /** A flag used to determinate if the handshake should start immediately */ 162 private static final boolean START_HANDSHAKE = true; 163 164 private boolean client; 165 166 private boolean needClientAuth; 167 168 private boolean wantClientAuth; 169 170 private String[] enabledCipherSuites; 171 172 private String[] enabledProtocols; 173 174 /** 175 * Creates a new SSL filter using the specified {@link SSLContext}. 176 * The handshake will start immediately. 177 * 178 * @param sslContext The SSLContext to use 179 */ 180 public SslFilter(SSLContext sslContext) { 181 this(sslContext, START_HANDSHAKE); 182 } 183 184 /** 185 * Creates a new SSL filter using the specified {@link SSLContext}. 186 * If the <tt>autostart</tt> flag is set to <tt>true</tt>, the 187 * handshake will start immediately. 188 * 189 * @param sslContext The SSLContext to use 190 * @param autoStart The flag used to tell the filter to start the handshake immediately 191 */ 192 public SslFilter(SSLContext sslContext, boolean autoStart) { 193 if (sslContext == null) { 194 throw new IllegalArgumentException("sslContext"); 195 } 196 197 this.sslContext = sslContext; 198 this.autoStart = autoStart; 199 } 200 201 /** 202 * Returns the underlying {@link SSLSession} for the specified session. 203 * 204 * @param session The current session 205 * @return <tt>null</tt> if no {@link SSLSession} is initialized yet. 206 */ 207 public SSLSession getSslSession(IoSession session) { 208 return (SSLSession) session.getAttribute(SSL_SESSION); 209 } 210 211 /** 212 * (Re)starts SSL session for the specified <tt>session</tt> if not started yet. 213 * Please note that SSL session is automatically started by default, and therefore 214 * you don't need to call this method unless you've used TLS closure. 215 * 216 * @param session The session that will be switched to SSL mode 217 * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started. 218 * @throws SSLException if failed to start the SSL session 219 */ 220 public boolean startSsl(IoSession session) throws SSLException { 221 SslHandler sslHandler = getSslSessionHandler(session); 222 boolean started; 223 224 try { 225 synchronized (sslHandler) { 226 if (sslHandler.isOutboundDone()) { 227 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER); 228 sslHandler.destroy(); 229 sslHandler.init(); 230 sslHandler.handshake(nextFilter); 231 started = true; 232 } else { 233 started = false; 234 } 235 } 236 237 sslHandler.flushScheduledEvents(); 238 } catch (SSLException se) { 239 sslHandler.release(); 240 throw se; 241 } 242 243 return started; 244 } 245 246 /** 247 * An extended toString() method for sessions. If the SSL handshake 248 * is not yet completed, we will print (ssl) in small caps. Once it's 249 * completed, we will use SSL capitalized. 250 */ 251 /* no qualifier */String getSessionInfo(IoSession session) { 252 StringBuilder sb = new StringBuilder(); 253 254 if (session.getService() instanceof IoAcceptor) { 255 sb.append("Session Server"); 256 257 } else { 258 sb.append("Session Client"); 259 } 260 261 sb.append('[').append(session.getId()).append(']'); 262 263 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 264 265 if (sslHandler == null) { 266 sb.append("(no sslEngine)"); 267 } else if (isSslStarted(session)) { 268 if (sslHandler.isHandshakeComplete()) { 269 sb.append("(SSL)"); 270 } else { 271 sb.append("(ssl...)"); 272 } 273 } 274 275 return sb.toString(); 276 } 277 278 /** 279 * @return <tt>true</tt> if and only if the specified <tt>session</tt> is 280 * encrypted/decrypted over SSL/TLS currently. This method will start 281 * to return <tt>false</tt> after TLS <tt>close_notify</tt> message 282 * is sent and any messages written after then is not going to get encrypted. 283 * 284 * @param session the session we want to check 285 */ 286 public boolean isSslStarted(IoSession session) { 287 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 288 289 if (sslHandler == null) { 290 return false; 291 } 292 293 synchronized (sslHandler) { 294 return !sslHandler.isOutboundDone(); 295 } 296 } 297 298 /** 299 * Stops the SSL session by sending TLS <tt>close_notify</tt> message to 300 * initiate TLS closure. 301 * 302 * @param session the {@link IoSession} to initiate TLS closure 303 * @return The Future for the initiated closure 304 * @throws SSLException if failed to initiate TLS closure 305 */ 306 public WriteFuture stopSsl(IoSession session) throws SSLException { 307 SslHandler sslHandler = getSslSessionHandler(session); 308 NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER); 309 WriteFuture future; 310 311 try { 312 synchronized (sslHandler) { 313 future = initiateClosure(nextFilter, session); 314 } 315 316 sslHandler.flushScheduledEvents(); 317 } catch (SSLException se) { 318 sslHandler.release(); 319 throw se; 320 } 321 322 return future; 323 } 324 325 /** 326 * @return <tt>true</tt> if the engine is set to use client mode 327 * when handshaking. 328 */ 329 public boolean isUseClientMode() { 330 return client; 331 } 332 333 /** 334 * Configures the engine to use client (or server) mode when handshaking. 335 * 336 * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt> when in server mode 337 */ 338 public void setUseClientMode(boolean clientMode) { 339 this.client = clientMode; 340 } 341 342 /** 343 * @return <tt>true</tt> if the engine will <em>require</em> client authentication. 344 * This option is only useful to engines in the server mode. 345 */ 346 public boolean isNeedClientAuth() { 347 return needClientAuth; 348 } 349 350 /** 351 * Configures the engine to <em>require</em> client authentication. 352 * This option is only useful for engines in the server mode. 353 * 354 * @param needClientAuth A flag set when we need to authenticate the client 355 */ 356 public void setNeedClientAuth(boolean needClientAuth) { 357 this.needClientAuth = needClientAuth; 358 } 359 360 /** 361 * @return <tt>true</tt> if the engine will <em>request</em> client authentication. 362 * This option is only useful to engines in the server mode. 363 */ 364 public boolean isWantClientAuth() { 365 return wantClientAuth; 366 } 367 368 /** 369 * Configures the engine to <em>request</em> client authentication. 370 * This option is only useful for engines in the server mode. 371 * 372 * @param wantClientAuth A flag set when we want to check the client authentication 373 */ 374 public void setWantClientAuth(boolean wantClientAuth) { 375 this.wantClientAuth = wantClientAuth; 376 } 377 378 /** 379 * @return the list of cipher suites to be enabled when {@link SSLEngine} 380 * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.' 381 */ 382 public String[] getEnabledCipherSuites() { 383 return enabledCipherSuites; 384 } 385 386 /** 387 * Sets the list of cipher suites to be enabled when {@link SSLEngine} 388 * is initialized. 389 * 390 * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.' 391 */ 392 public void setEnabledCipherSuites(String[] cipherSuites) { 393 this.enabledCipherSuites = cipherSuites; 394 } 395 396 /** 397 * @return the list of protocols to be enabled when {@link SSLEngine} 398 * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.' 399 */ 400 public String[] getEnabledProtocols() { 401 return enabledProtocols; 402 } 403 404 /** 405 * Sets the list of protocols to be enabled when {@link SSLEngine} 406 * is initialized. 407 * 408 * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.' 409 */ 410 public void setEnabledProtocols(String[] protocols) { 411 this.enabledProtocols = protocols; 412 } 413 414 /** 415 * Executed just before the filter is added into the chain, we do : 416 * <ul> 417 * <li>check that we don't have a SSL filter already present 418 * <li>we update the next filter 419 * <li>we create the SSL handler helper class 420 * <li>and we store it into the session's Attributes 421 * </ul> 422 */ 423 @Override 424 public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException { 425 // Check that we don't have a SSL filter already present in the chain 426 if (parent.contains(SslFilter.class)) { 427 String msg = "Only one SSL filter is permitted in a chain."; 428 LOGGER.error(msg); 429 throw new IllegalStateException(msg); 430 } 431 432 LOGGER.debug("Adding the SSL Filter {} to the chain", name); 433 434 IoSession session = parent.getSession(); 435 session.setAttribute(NEXT_FILTER, nextFilter); 436 437 // Create a SSL handler and start handshake. 438 SslHandler sslHandler = new SslHandler(this, session); 439 440 // Adding the supported ciphers in the SSLHandler 441 if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) { 442 enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites(); 443 } 444 445 sslHandler.init(); 446 447 session.setAttribute(SSL_HANDLER, sslHandler); 448 } 449 450 @Override 451 public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException { 452 if (autoStart == START_HANDSHAKE) { 453 initiateHandshake(nextFilter, parent.getSession()); 454 } 455 } 456 457 @Override 458 public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException { 459 IoSession session = parent.getSession(); 460 stopSsl(session); 461 session.removeAttribute(NEXT_FILTER); 462 session.removeAttribute(SSL_HANDLER); 463 } 464 465 // IoFilter impl. 466 @Override 467 public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException { 468 SslHandler sslHandler = getSslSessionHandler(session); 469 470 try { 471 synchronized (sslHandler) { 472 // release resources 473 sslHandler.destroy(); 474 } 475 } finally { 476 // notify closed session 477 nextFilter.sessionClosed(session); 478 } 479 } 480 481 @Override 482 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException { 483 if (LOGGER.isDebugEnabled()) { 484 LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message); 485 } 486 487 SslHandler sslHandler = getSslSessionHandler(session); 488 489 synchronized (sslHandler) { 490 if (!isSslStarted(session) && sslHandler.isInboundDone()) { 491 // The SSL session must be established first before we 492 // can push data to the application. Store the incoming 493 // data into a queue for a later processing 494 sslHandler.scheduleMessageReceived(nextFilter, message); 495 } else { 496 IoBuffer buf = (IoBuffer) message; 497 498 try { 499 // forward read encrypted data to SSL handler 500 sslHandler.messageReceived(nextFilter, buf.buf()); 501 502 // Handle data to be forwarded to application or written to net 503 handleSslData(nextFilter, sslHandler); 504 505 if (sslHandler.isInboundDone()) { 506 if (sslHandler.isOutboundDone()) { 507 sslHandler.destroy(); 508 } else { 509 initiateClosure(nextFilter, session); 510 } 511 512 if (buf.hasRemaining()) { 513 // Forward the data received after closure. 514 sslHandler.scheduleMessageReceived(nextFilter, buf); 515 } 516 } 517 } catch (SSLException ssle) { 518 if (!sslHandler.isHandshakeComplete()) { 519 SSLException newSsle = new SSLHandshakeException("SSL handshake failed."); 520 newSsle.initCause(ssle); 521 ssle = newSsle; 522 } else { 523 // Free the SSL Handler buffers 524 sslHandler.release(); 525 } 526 527 throw ssle; 528 } 529 } 530 } 531 532 sslHandler.flushScheduledEvents(); 533 } 534 535 @Override 536 public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) { 537 if (writeRequest instanceof EncryptedWriteRequest) { 538 EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest; 539 nextFilter.messageSent(session, wrappedRequest.getParentRequest()); 540 } else { 541 // ignore extra buffers used for handshaking 542 } 543 } 544 545 @Override 546 public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception { 547 548 if (cause instanceof WriteToClosedSessionException) { 549 // Filter out SSL close notify, which is likely to fail to flush 550 // due to disconnection. 551 WriteToClosedSessionException e = (WriteToClosedSessionException) cause; 552 List<WriteRequest> failedRequests = e.getRequests(); 553 boolean containsCloseNotify = false; 554 555 for (WriteRequest r : failedRequests) { 556 if (isCloseNotify(r.getMessage())) { 557 containsCloseNotify = true; 558 break; 559 } 560 } 561 562 if (containsCloseNotify) { 563 if (failedRequests.size() == 1) { 564 // close notify is the only failed request; bail out. 565 return; 566 } 567 568 List<WriteRequest> newFailedRequests = new ArrayList<WriteRequest>(failedRequests.size() - 1); 569 570 for (WriteRequest r : failedRequests) { 571 if (!isCloseNotify(r.getMessage())) { 572 newFailedRequests.add(r); 573 } 574 } 575 576 if (newFailedRequests.isEmpty()) { 577 // the failedRequests were full with close notify; bail out. 578 return; 579 } 580 581 cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause()); 582 } 583 } 584 585 nextFilter.exceptionCaught(session, cause); 586 } 587 588 private boolean isCloseNotify(Object message) { 589 if (!(message instanceof IoBuffer)) { 590 return false; 591 } 592 593 IoBuffer buf = (IoBuffer) message; 594 int offset = buf.position(); 595 596 return (buf.get(offset + 0) == 0x15) /* Alert */ 597 && (buf.get(offset + 1) == 0x03) /* TLS/SSL */ 598 && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */ 599 || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */ 600 || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */ 601 || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */ 602 && (buf.get(offset + 3) == 0x00); /* close_notify */ 603 } 604 605 @Override 606 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException { 607 if (LOGGER.isDebugEnabled()) { 608 LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest); 609 } 610 611 boolean needsFlush = true; 612 SslHandler sslHandler = getSslSessionHandler(session); 613 614 try { 615 synchronized (sslHandler) { 616 if (!isSslStarted(session)) { 617 sslHandler.scheduleFilterWrite(nextFilter, writeRequest); 618 } 619 // Don't encrypt the data if encryption is disabled. 620 else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) { 621 // Remove the marker attribute because it is temporary. 622 session.removeAttribute(DISABLE_ENCRYPTION_ONCE); 623 sslHandler.scheduleFilterWrite(nextFilter, writeRequest); 624 } else { 625 // Otherwise, encrypt the buffer. 626 IoBuffer buf = (IoBuffer) writeRequest.getMessage(); 627 628 if (sslHandler.isWritingEncryptedData()) { 629 // data already encrypted; simply return buffer 630 sslHandler.scheduleFilterWrite(nextFilter, writeRequest); 631 } else if (sslHandler.isHandshakeComplete()) { 632 // SSL encrypt 633 buf.mark(); 634 sslHandler.encrypt(buf.buf()); 635 IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer(); 636 sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest, 637 encryptedBuffer)); 638 } else { 639 if (session.isConnected()) { 640 // Handshake not complete yet. 641 sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest); 642 } 643 644 needsFlush = false; 645 } 646 } 647 } 648 649 if (needsFlush) { 650 sslHandler.flushScheduledEvents(); 651 } 652 } catch (SSLException se) { 653 sslHandler.release(); 654 throw se; 655 } 656 } 657 658 @Override 659 public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException { 660 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 661 662 if (sslHandler == null) { 663 // The connection might already have closed, or 664 // SSL might have not started yet. 665 nextFilter.filterClose(session); 666 return; 667 } 668 669 WriteFuture future = null; 670 671 try { 672 synchronized (sslHandler) { 673 if (isSslStarted(session)) { 674 future = initiateClosure(nextFilter, session); 675 future.addListener(new IoFutureListener<IoFuture>() { 676 public void operationComplete(IoFuture future) { 677 nextFilter.filterClose(session); 678 } 679 }); 680 } 681 } 682 683 sslHandler.flushScheduledEvents(); 684 } catch (SSLException se) { 685 sslHandler.release(); 686 throw se; 687 } finally { 688 if (future == null) { 689 nextFilter.filterClose(session); 690 } 691 } 692 } 693 694 /** 695 * Initiate the SSL handshake. This can be invoked if you have set the 'autoStart' to 696 * false when creating the SslFilter instance. 697 * 698 * @param session The session for which the SSL handshake should be done 699 * @throws SSLException If the handshake failed 700 */ 701 public void initiateHandshake(IoSession session) throws SSLException { 702 IoFilterChain filterChain = session.getFilterChain(); 703 704 if (filterChain == null) { 705 throw new SSLException("No filter chain"); 706 } 707 708 IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class); 709 710 if (nextFilter == null) { 711 throw new SSLException("No SSL next filter in the chain"); 712 } 713 714 initiateHandshake(nextFilter, session); 715 } 716 717 private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException { 718 LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session)); 719 SslHandler sslHandler = getSslSessionHandler(session); 720 721 try { 722 synchronized (sslHandler) { 723 sslHandler.handshake(nextFilter); 724 } 725 726 sslHandler.flushScheduledEvents(); 727 } catch (SSLException se) { 728 sslHandler.release(); 729 throw se; 730 } 731 } 732 733 private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException { 734 SslHandler sslHandler = getSslSessionHandler(session); 735 WriteFuture future = null; 736 737 // if already shut down 738 try { 739 if (!sslHandler.closeOutbound()) { 740 return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException( 741 "SSL session is shut down already.")); 742 } 743 744 // there might be data to write out here? 745 future = sslHandler.writeNetBuffer(nextFilter); 746 747 if (future == null) { 748 future = DefaultWriteFuture.newWrittenFuture(session); 749 } 750 751 if (sslHandler.isInboundDone()) { 752 sslHandler.destroy(); 753 } 754 755 if (session.containsAttribute(USE_NOTIFICATION)) { 756 sslHandler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED); 757 } 758 } catch (SSLException se) { 759 sslHandler.release(); 760 throw se; 761 } 762 763 return future; 764 } 765 766 // Utilities 767 private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException { 768 if (LOGGER.isDebugEnabled()) { 769 LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession())); 770 } 771 772 // Flush any buffered write requests occurred before handshaking. 773 if (sslHandler.isHandshakeComplete()) { 774 sslHandler.flushPreHandshakeEvents(); 775 } 776 777 // Write encrypted data to be written (if any) 778 sslHandler.writeNetBuffer(nextFilter); 779 780 // handle app. data read (if any) 781 handleAppDataRead(nextFilter, sslHandler); 782 } 783 784 private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) { 785 // forward read app data 786 IoBuffer readBuffer = sslHandler.fetchAppBuffer(); 787 788 if (readBuffer.hasRemaining()) { 789 sslHandler.scheduleMessageReceived(nextFilter, readBuffer); 790 } 791 } 792 793 private SslHandler getSslSessionHandler(IoSession session) { 794 SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER); 795 796 if (sslHandler == null) { 797 throw new IllegalStateException(); 798 } 799 800 if (sslHandler.getSslFilter() != this) { 801 throw new IllegalArgumentException("Not managed by this filter."); 802 } 803 804 return sslHandler; 805 } 806 807 /** 808 * A message that is sent from {@link SslFilter} when the connection became 809 * secure or is not secure anymore. 810 * 811 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 812 */ 813 public static class SslFilterMessage { 814 private final String name; 815 816 private SslFilterMessage(String name) { 817 this.name = name; 818 } 819 820 @Override 821 public String toString() { 822 return name; 823 } 824 } 825 826 private static class EncryptedWriteRequest extends WriteRequestWrapper { 827 private final IoBuffer encryptedMessage; 828 829 private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) { 830 super(writeRequest); 831 this.encryptedMessage = encryptedMessage; 832 } 833 834 @Override 835 public Object getMessage() { 836 return encryptedMessage; 837 } 838 } 839}