📄 sslhandler.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.nio.ByteBuffer;import java.util.Queue;import java.util.concurrent.ConcurrentLinkedQueue;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLEngine;import javax.net.ssl.SSLEngineResult;import javax.net.ssl.SSLException;import javax.net.ssl.SSLHandshakeException;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.filterchain.IoFilterEvent;import org.apache.mina.core.filterchain.IoFilter.NextFilter;import org.apache.mina.core.future.DefaultWriteFuture;import org.apache.mina.core.future.WriteFuture;import org.apache.mina.core.session.IoEventType;import org.apache.mina.core.session.IoSession;import org.apache.mina.core.write.DefaultWriteRequest;import org.apache.mina.core.write.WriteRequest;import org.apache.mina.util.CircularQueue;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * A helper class using the SSLEngine API to decrypt/encrypt data. * <p/> * Each connection has a SSLEngine that is used through the lifetime of the connection. * We allocate buffers for use as the outbound and inbound network buffers. * These buffers handle all of the intermediary data for the SSL connection. To make things easy, * we'll require outNetBuffer be completely flushed before trying to wrap any more data. * * @author The Apache MINA Project (dev@mina.apache.org) * @version $Rev: 713364 $, $Date: 2008-11-12 14:35:51 +0100 (Wed, 12 Nov 2008) $ */class SslHandler { private final Logger logger = LoggerFactory.getLogger(getClass()); private final SslFilter parent; private final SSLContext sslContext; private final IoSession session; private final Queue<IoFilterEvent> preHandshakeEventQueue = new CircularQueue<IoFilterEvent>(); private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>(); private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>(); private SSLEngine sslEngine; /** * Encrypted data from the net */ private IoBuffer inNetBuffer; /** * Encrypted data to be written to the net */ private IoBuffer outNetBuffer; /** * Applicaton cleartext data to be read by application */ private IoBuffer appBuffer; /** * Empty buffer used during initial handshake and close operations */ private final IoBuffer emptyBuffer = IoBuffer.allocate(0); private SSLEngineResult.HandshakeStatus handshakeStatus; private boolean initialHandshakeComplete; private boolean handshakeComplete; private boolean writingEncryptedData; /** * Constuctor. * * @param sslc * @throws SSLException */ public SslHandler(SslFilter parent, SSLContext sslContext, IoSession session) throws SSLException { this.parent = parent; this.session = session; this.sslContext = sslContext; init(); } /** * Initialize the SSL handshake. * * @throws SSLException */ public void init() throws SSLException { if (sslEngine != null) { // We already have a SSL engine created, no need to create a new one return; } InetSocketAddress peer = (InetSocketAddress) session .getAttribute(SslFilter.PEER_ADDRESS); // Create the SSL engine here if (peer == null) { sslEngine = sslContext.createSSLEngine(); } else { sslEngine = sslContext.createSSLEngine(peer.getHostName(), peer.getPort()); } // Initialize the engine in client mode if necessary sslEngine.setUseClientMode(parent.isUseClientMode()); // Initialize the different SslEngine modes if (parent.isWantClientAuth()) { sslEngine.setWantClientAuth(true); } if (parent.isNeedClientAuth()) { sslEngine.setNeedClientAuth(true); } if (parent.getEnabledCipherSuites() != null) { sslEngine.setEnabledCipherSuites(parent.getEnabledCipherSuites()); } if (parent.getEnabledProtocols() != null) { sslEngine.setEnabledProtocols(parent.getEnabledProtocols()); } // TODO : we may not need to call this method... sslEngine.beginHandshake(); handshakeStatus = sslEngine.getHandshakeStatus(); handshakeComplete = false; initialHandshakeComplete = false; writingEncryptedData = false; } /** * Release allocated buffers. */ public void destroy() { if (sslEngine == null) { return; } // Close inbound and flush all remaining data if available. try { sslEngine.closeInbound(); } catch (SSLException e) { logger.debug( "Unexpected exception from SSLEngine.closeInbound().", e); } if (outNetBuffer != null) { outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize()); } else { createOutNetBuffer(0); } try { do { outNetBuffer.clear(); } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0); } catch (SSLException e) { // Ignore. } finally { destroyOutNetBuffer(); } sslEngine.closeOutbound(); sslEngine = null; preHandshakeEventQueue.clear(); } private void destroyOutNetBuffer() { outNetBuffer.free(); outNetBuffer = null; } public SslFilter getParent() { return parent; } public IoSession getSession() { return session; } /** * Check we are writing encrypted data. */ public boolean isWritingEncryptedData() { return writingEncryptedData; } /** * Check if handshake is completed. */ public boolean isHandshakeComplete() { return handshakeComplete; } public boolean isInboundDone() { return sslEngine == null || sslEngine.isInboundDone(); } public boolean isOutboundDone() { return sslEngine == null || sslEngine.isOutboundDone(); } /** * Check if there is any need to complete handshake. */ public boolean needToCompleteHandshake() { return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone(); } public void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) { preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest)); } public void flushPreHandshakeEvents() throws SSLException { IoFilterEvent scheduledWrite; while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) { parent.filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter()); } } public void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) { filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest)); } public void scheduleMessageReceived(NextFilter nextFilter, Object message) { messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message)); } public void flushScheduledEvents() { // Fire events only when no lock is hold for this handler. if (Thread.holdsLock(this)) { return; } IoFilterEvent e; // We need synchronization here inevitably because filterWrite can be // called simultaneously and cause 'bad record MAC' integrity error. synchronized (this) { while ((e = filterWriteEventQueue.poll()) != null) { e.getNextFilter().filterWrite(session, (WriteRequest) e.getParameter()); } } while ((e = messageReceivedEventQueue.poll()) != null) { e.getNextFilter().messageReceived(session, e.getParameter()); } } /** * Call when data read from net. Will perform inial hanshake or decrypt provided * Buffer. * Decrytpted data reurned by getAppBuffer(), if any. * * @param buf buffer to decrypt * @param nextFilter Next filter in chain * @throws SSLException on errors */ public void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException { // append buf to inNetBuffer if (inNetBuffer == null) { inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true); } inNetBuffer.put(buf); if (!handshakeComplete) { handshake(nextFilter); } else { decrypt(nextFilter); } if (isInboundDone()) { // Rewind the MINA buffer if not all data is processed and inbound is finished. int inNetBufferPosition = inNetBuffer == null? 0 : inNetBuffer.position(); buf.position(buf.position() - inNetBufferPosition); inNetBuffer = null; } } /** * Get decrypted application data. * * @return buffer with data */ public IoBuffer fetchAppBuffer() { IoBuffer appBuffer = this.appBuffer.flip(); this.appBuffer = null; return appBuffer; } /** * Get encrypted data to be sent. * * @return buffer with data */ public IoBuffer fetchOutNetBuffer() { IoBuffer answer = outNetBuffer; if (answer == null) { return emptyBuffer; } outNetBuffer = null; return answer.shrink(); } /** * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer(). * * @param src data to encrypt * @throws SSLException on errors */ public void encrypt(ByteBuffer src) throws SSLException { if (!handshakeComplete) { throw new IllegalStateException(); } if (!src.hasRemaining()) { if (outNetBuffer == null) { outNetBuffer = emptyBuffer; } return; } createOutNetBuffer(src.remaining());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -