📄 sslfilter.java
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */package org.apache.mina.filter.ssl;import java.net.InetSocketAddress;import java.util.ArrayList;import java.util.List;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLEngine;import javax.net.ssl.SSLException;import javax.net.ssl.SSLHandshakeException;import javax.net.ssl.SSLSession;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.filterchain.IoFilterAdapter;import org.apache.mina.core.filterchain.IoFilterChain;import org.apache.mina.core.future.DefaultWriteFuture;import org.apache.mina.core.future.IoFuture;import org.apache.mina.core.future.IoFutureListener;import org.apache.mina.core.future.WriteFuture;import org.apache.mina.core.service.IoHandler;import org.apache.mina.core.session.AttributeKey;import org.apache.mina.core.session.IoSession;import org.apache.mina.core.write.WriteRequest;import org.apache.mina.core.write.WriteRequestWrapper;import org.apache.mina.core.write.WriteToClosedSessionException;/** * An SSL filter that encrypts and decrypts the data exchanged in the session. * Adding this filter triggers SSL handshake procedure immediately by sending * a SSL 'hello' message, so you don't need to call * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS * (see below). If you don't want the handshake procedure to start * immediately, please specify {@code false} as {@code autoStart} parameter in * the constructor. * <p> * This filter uses an {@link SSLEngine} which was introduced in Java 5, so * Java version 5 or above is mandatory to use this filter. And please note that * this filter only works for TCP/IP connections. * <p> * * <h2>Implementing StartTLS</h2> * <p> * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS: * <pre> * public void messageReceived(IoSession session, Object message) { * if (message instanceof MyStartTLSRequest) { * // Insert SSLFilter to get ready for handshaking * session.getFilterChain().addFirst(sslFilter); * * // Disable encryption temporarilly. * // This attribute will be removed by SSLFilter * // inside the Session.write() call below. * session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE); * * // Write StartTLSResponse which won't be encrypted. * session.write(new MyStartTLSResponse(OK)); * * // Now DISABLE_ENCRYPTION_ONCE attribute is cleared. * assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null; * } * } * </pre> * * @author The Apache MINA Project (dev@mina.apache.org) * @version $Rev: 713366 $, $Date: 2008-11-12 14:39:23 +0100 (Wed, 12 Nov 2008) $ * @org.apache.xbean.XBean */public class SslFilter extends IoFilterAdapter { /** * A session attribute key that stores underlying {@link SSLSession} * for each session. */ public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session"); /** * A session attribute key that makes next one write request bypass * this filter (not encrypting the data). This is a marker attribute, * which means that you can put whatever as its value. ({@link Boolean#TRUE} * is preferred.) The attribute is automatically removed from the session * attribute map as soon as {@link IoSession#write(Object)} is invoked, * and therefore should be put again if you want to make more messages * bypass this filter. This is especially useful when you implement * StartTLS. */ public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce"); /** * A session attribute key that makes this filter to emit a * {@link IoHandler#messageReceived(IoSession, Object)} event with a * special message ({@link #SESSION_SECURED} or {@link #SESSION_UNSECURED}). * This is a marker attribute, which means that you can put whatever as its * value. ({@link Boolean#TRUE} is preferred.) By default, this filter * doesn't emit any events related with SSL session flow control. */ public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification"); /** * A session attribute key that should be set to an {@link InetSocketAddress}. * Setting this attribute causes * {@link SSLContext#createSSLEngine(String, int)} to be called passing the * hostname and port of the {@link InetSocketAddress} to get an * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()} * will be called.<br/> * Using this feature {@link SSLSession} objects may be cached and reused * when in client mode. * * @see SSLContext#createSSLEngine(String, int) */ public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress"); /** * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)} * event when the session is secured and its {@link #USE_NOTIFICATION} * attribute is set. */ public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage( "SESSION_SECURED"); /** * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)} * event when the session is not secure anymore and its {@link #USE_NOTIFICATION} * attribute is set. */ public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage( "SESSION_UNSECURED"); private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter"); private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler"); /** The SslContext used */ private final SSLContext sslContext; /** A flag used to tell the filter to start the handshake immediately */ private final boolean autoStart; /** A flag used to determinate if the handshake should start immediately */ private static final boolean START_HANDSHAKE = true; private boolean client; private boolean needClientAuth; private boolean wantClientAuth; private String[] enabledCipherSuites; private String[] enabledProtocols; /** * Creates a new SSL filter using the specified {@link SSLContext}. * The handshake will start immediately. */ public SslFilter(SSLContext sslContext) { this(sslContext, START_HANDSHAKE); } /** * Creates a new SSL filter using the specified {@link SSLContext}. * If the <code>autostart</code> flag is set to <code>true</code>, the * handshake will start immediately. */ public SslFilter(SSLContext sslContext, boolean autoStart) { if (sslContext == null) { throw new NullPointerException("sslContext"); } this.sslContext = sslContext; this.autoStart = autoStart; } /** * Returns the underlying {@link SSLSession} for the specified session. * * @return <tt>null</tt> if no {@link SSLSession} is initialized yet. */ public SSLSession getSslSession(IoSession session) { return (SSLSession) session.getAttribute(SSL_SESSION); } /** * (Re)starts SSL session for the specified <tt>session</tt> if not started yet. * Please note that SSL session is automatically started by default, and therefore * you don't need to call this method unless you've used TLS closure. * * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started. * @throws SSLException if failed to start the SSL session */ public boolean startSsl(IoSession session) throws SSLException { SslHandler handler = getSslSessionHandler(session); boolean started; synchronized (handler) { if (handler.isOutboundDone()) { NextFilter nextFilter = (NextFilter) session .getAttribute(NEXT_FILTER); handler.destroy(); handler.init(); handler.handshake(nextFilter); started = true; } else { started = false; } } handler.flushScheduledEvents(); return started; } /** * Returns <tt>true</tt> if and only if the specified <tt>session</tt> is * encrypted/decrypted over SSL/TLS currently. This method will start * to retun <tt>false</tt> after TLS <tt>close_notify</tt> message * is sent and any messages written after then is not goinf to get encrypted. */ public boolean isSslStarted(IoSession session) { SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER); if (handler == null) { return false; } synchronized (handler) { return !handler.isOutboundDone(); } } /** * Stops the SSL session by sending TLS <tt>close_notify</tt> message to * initiate TLS closure. * * @param session the {@link IoSession} to initiate TLS closure * @throws SSLException if failed to initiate TLS closure * @throws IllegalArgumentException if this filter is not managing the specified session */ public WriteFuture stopSsl(IoSession session) throws SSLException { SslHandler handler = getSslSessionHandler(session); NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER); WriteFuture future; synchronized (handler) { future = initiateClosure(nextFilter, session); } handler.flushScheduledEvents(); return future; } /** * Returns <tt>true</tt> if the engine is set to use client mode * when handshaking. */ public boolean isUseClientMode() { return client; } /** * Configures the engine to use client (or server) mode when handshaking. */ public void setUseClientMode(boolean clientMode) { this.client = clientMode; } /** * Returns <tt>true</tt> if the engine will <em>require</em> client authentication. * This option is only useful to engines in the server mode. */ public boolean isNeedClientAuth() { return needClientAuth; } /** * Configures the engine to <em>require</em> client authentication. * This option is only useful for engines in the server mode. */ public void setNeedClientAuth(boolean needClientAuth) { this.needClientAuth = needClientAuth; } /** * Returns <tt>true</tt> if the engine will <em>request</em> client authentication. * This option is only useful to engines in the server mode. */ public boolean isWantClientAuth() { return wantClientAuth; } /** * Configures the engine to <em>request</em> client authentication. * This option is only useful for engines in the server mode. */ public void setWantClientAuth(boolean wantClientAuth) { this.wantClientAuth = wantClientAuth; } /** * Returns the list of cipher suites to be enabled when {@link SSLEngine} * is initialized. * * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.' */ public String[] getEnabledCipherSuites() { return enabledCipherSuites; } /** * Sets the list of cipher suites to be enabled when {@link SSLEngine} * is initialized. * * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.' */ public void setEnabledCipherSuites(String[] cipherSuites) { this.enabledCipherSuites = cipherSuites; } /** * Returns the list of protocols to be enabled when {@link SSLEngine} * is initialized. * * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.' */ public String[] getEnabledProtocols() { return enabledProtocols; } /** * Sets the list of protocols to be enabled when {@link SSLEngine} * is initialized. * * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.' */ public void setEnabledProtocols(String[] protocols) { this.enabledProtocols = protocols; } @Override public void onPreAdd(IoFilterChain parent, String name,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -