| | |
| | | import java.util.Set; |
| | | |
| | | import javax.net.ssl.*; |
| | | import javax.net.ssl.SSLEngineResult.HandshakeStatus; |
| | | |
| | | import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg; |
| | | import org.opends.server.api.ClientConnection; |
| | |
| | | /** |
| | | * A class that provides a TLS byte channel implementation. |
| | | */ |
| | | public class TLSByteChannel implements ByteChannel, ConnectionSecurityProvider |
| | | public final class TLSByteChannel implements ConnectionSecurityProvider |
| | | { |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | /** |
| | | * Private implementation. |
| | | */ |
| | | private final class ByteChannelImpl implements ByteChannel |
| | | { |
| | | |
| | | private final ByteChannel socketChannel; |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void close() throws IOException |
| | | { |
| | | synchronized (readLock) |
| | | { |
| | | synchronized (writeLock) |
| | | { |
| | | final boolean isInitiator = !sslEngine.isInboundDone(); |
| | | |
| | | private final SSLEngine sslEngine; |
| | | try |
| | | { |
| | | if (!sslEngine.isOutboundDone()) |
| | | { |
| | | sslEngine.closeOutbound(); |
| | | while (doWrapAndSend(EMPTY_BUFFER) > 0) |
| | | { |
| | | // Write out any remaining SSL close notifications. |
| | | } |
| | | } |
| | | } |
| | | catch (final ClosedChannelException e) |
| | | { |
| | | // Ignore this so that close is idempotent. |
| | | } |
| | | finally |
| | | { |
| | | try |
| | | { |
| | | sslEngine.closeInbound(); |
| | | } |
| | | catch (final SSLException e) |
| | | { |
| | | // Not yet received peer's close notification. Ignore this if we |
| | | // are the initiator. |
| | | if (!isInitiator) |
| | | { |
| | | throw e; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | channel.close(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // read copy to buffer |
| | | private final ByteBuffer appData; |
| | | |
| | | // read encrypted |
| | | private final ByteBuffer appNetData; |
| | | |
| | | // Write encrypted |
| | | private final ByteBuffer netData; |
| | | private final ByteBuffer tempData; |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isOpen() |
| | | { |
| | | return !sslEngine.isOutboundDone() || !sslEngine.isInboundDone(); |
| | | } |
| | | |
| | | private final int sslBufferSize; |
| | | private final int appBufSize; |
| | | |
| | | private boolean reading = false; |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int read(final ByteBuffer unwrappedData) throws IOException |
| | | { |
| | | synchronized (readLock) |
| | | { |
| | | // Repeat until there is some unwrapped data available or all available |
| | | // data has been read from the underlying socket. |
| | | if (!recvUnwrappedBuffer.hasRemaining()) |
| | | { |
| | | final int read = doRecvAndUnwrap(); |
| | | if (read <= 0) |
| | | { |
| | | // No data read or end of stream. |
| | | return read; |
| | | } |
| | | } |
| | | |
| | | // Copy available data. |
| | | final int startPos = unwrappedData.position(); |
| | | if (recvUnwrappedBuffer.remaining() > unwrappedData.remaining()) |
| | | { |
| | | // Unwrapped data does not fit in client buffer so copy one by at a |
| | | // time: it's annoying that there is no easy way to do this with |
| | | // ByteBuffers. |
| | | while (unwrappedData.hasRemaining()) |
| | | { |
| | | unwrappedData.put(recvUnwrappedBuffer.get()); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // Unwrapped data fits client buffer so block copy. |
| | | unwrappedData.put(recvUnwrappedBuffer); |
| | | } |
| | | return unwrappedData.position() - startPos; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int write(final ByteBuffer unwrappedData) throws IOException |
| | | { |
| | | // This method will block until the entire message is sent. |
| | | final int bytesWritten = unwrappedData.remaining(); |
| | | |
| | | // Synchronized in order to prevent interleaving and reordering. |
| | | synchronized (writeLock) |
| | | { |
| | | // Repeat until the entire input data is written. |
| | | while (unwrappedData.hasRemaining()) |
| | | { |
| | | // Wrap and send the data. |
| | | doWrapAndSend(unwrappedData); |
| | | |
| | | // Perform handshake if needed. |
| | | if (isHandshaking(sslEngine.getHandshakeStatus())) |
| | | { |
| | | doHandshake(false /* isReading */); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return bytesWritten; |
| | | } |
| | | |
| | | |
| | | |
| | | private void doHandshake(final boolean isReading) throws IOException |
| | | { |
| | | // This lock is probably unnecessary since tasks can be run in parallel, |
| | | // but it adds no additional overhead so there's little harm in having |
| | | // it. |
| | | synchronized (handshakeLock) |
| | | { |
| | | while (true) |
| | | { |
| | | switch (sslEngine.getHandshakeStatus()) |
| | | { |
| | | case NEED_TASK: |
| | | Runnable runnable; |
| | | while ((runnable = sslEngine.getDelegatedTask()) != null) |
| | | { |
| | | runnable.run(); |
| | | } |
| | | break; |
| | | case NEED_UNWRAP: |
| | | // Block for writes, but be non-blocking for reads. |
| | | if (isReading) |
| | | { |
| | | // Let doRecvAndUnwrap() deal with this. |
| | | return; |
| | | } |
| | | |
| | | // Need to do an unwrap (read) while writing. |
| | | if (doRecvAndUnwrap() < 0) |
| | | { |
| | | throw new ClosedChannelException(); |
| | | } |
| | | break; |
| | | case NEED_WRAP: |
| | | doWrapAndSend(EMPTY_BUFFER); |
| | | break; |
| | | default: // NOT_HANDSHAKING, FINISHED. |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | // Attempt to read and unwrap the next SSL packet. |
| | | private int doRecvAndUnwrap() throws IOException |
| | | { |
| | | // Synchronize SSL unwrap with channel reads. |
| | | synchronized (unwrapLock) |
| | | { |
| | | // Repeat if there is underflow or overflow. |
| | | boolean needRead = true; |
| | | while (true) |
| | | { |
| | | // Read wrapped data if needed. |
| | | if (needRead) |
| | | { |
| | | recvWrappedBuffer.compact(); // Prepare for append. |
| | | final int read = channel.read(recvWrappedBuffer); |
| | | recvWrappedBuffer.flip(); // Restore for read. |
| | | if (read < 0) |
| | | { |
| | | // Peer abort? |
| | | sslEngine.closeInbound(); |
| | | return -1; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | needRead = true; |
| | | } |
| | | |
| | | // Unwrap. |
| | | recvUnwrappedBuffer.compact(); // Prepare for append. |
| | | final SSLEngineResult result = sslEngine.unwrap(recvWrappedBuffer, |
| | | recvUnwrappedBuffer); |
| | | recvUnwrappedBuffer.flip(); // Restore for read. |
| | | |
| | | switch (result.getStatus()) |
| | | { |
| | | case BUFFER_OVERFLOW: |
| | | // The unwrapped buffer is not big enough: resize and repeat. |
| | | final int newAppSize = sslEngine.getSession() |
| | | .getApplicationBufferSize(); |
| | | final ByteBuffer newRecvUnwrappedBuffer = ByteBuffer |
| | | .allocate(recvUnwrappedBuffer.limit() + newAppSize); |
| | | newRecvUnwrappedBuffer.put(recvUnwrappedBuffer); |
| | | newRecvUnwrappedBuffer.flip(); |
| | | recvUnwrappedBuffer = newRecvUnwrappedBuffer; |
| | | needRead = false; |
| | | break; // Retry unwrap. |
| | | case BUFFER_UNDERFLOW: |
| | | // Not enough data was read. This either means that the inbound |
| | | // buffer was too small, or not enough data is available. |
| | | final int newPktSize = sslEngine.getSession().getPacketBufferSize(); |
| | | if (newPktSize > recvWrappedBuffer.capacity()) |
| | | { |
| | | // Buffer needs resizing. |
| | | final ByteBuffer newRecvWrappedBuffer = ByteBuffer |
| | | .allocate(newPktSize); |
| | | newRecvWrappedBuffer.put(recvWrappedBuffer); |
| | | newRecvWrappedBuffer.flip(); |
| | | recvWrappedBuffer = newRecvWrappedBuffer; |
| | | break; |
| | | } |
| | | else |
| | | { |
| | | // Not enough data is available to read a complete SSL packet. |
| | | return 0; |
| | | } |
| | | case CLOSED: |
| | | // Peer sent SSL close notification. |
| | | sslEngine.closeInbound(); |
| | | return -1; |
| | | default: // OK |
| | | if (recvUnwrappedBuffer.hasRemaining()) |
| | | { |
| | | // Some application data was read so return it. |
| | | return recvUnwrappedBuffer.remaining(); |
| | | } |
| | | else if (isHandshaking(result.getHandshakeStatus())) |
| | | { |
| | | // No application data was read, but if we are handshaking then |
| | | // try to continue. |
| | | if (result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP |
| | | && !recvWrappedBuffer.hasRemaining()) |
| | | { |
| | | // Not enough data is available to continue handshake. |
| | | return 0; |
| | | } |
| | | else |
| | | { |
| | | // Continue handshake. |
| | | doHandshake(true /* isReading */); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // No data available and not handshaking. |
| | | return 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | // Attempt to wrap and send the next SSL packet. |
| | | private int doWrapAndSend(final ByteBuffer unwrappedData) |
| | | throws IOException |
| | | { |
| | | // Synchronize SSL wrap with channel writes. |
| | | synchronized (wrapLock) |
| | | { |
| | | // Repeat while there is overflow. |
| | | while (true) |
| | | { |
| | | final SSLEngineResult result = sslEngine.wrap(unwrappedData, |
| | | sendWrappedBuffer); |
| | | switch (result.getStatus()) |
| | | { |
| | | case BUFFER_OVERFLOW: |
| | | // The wrapped buffer is not big enough: resize and repeat. |
| | | final int newSize = sslEngine.getSession().getPacketBufferSize(); |
| | | final ByteBuffer newSendWrappedBuffer = ByteBuffer |
| | | .allocate(sendWrappedBuffer.position() + newSize); |
| | | sendWrappedBuffer.flip(); |
| | | newSendWrappedBuffer.put(sendWrappedBuffer); |
| | | sendWrappedBuffer = newSendWrappedBuffer; |
| | | break; // Retry. |
| | | case BUFFER_UNDERFLOW: |
| | | // This should not happen for sends. |
| | | throw new SSLException("Got unexpected underflow while wrapping"); |
| | | case CLOSED: |
| | | throw new ClosedChannelException(); |
| | | default: // OK |
| | | // Write the SSL packet: our IO stack will block until all the |
| | | // data is written. |
| | | sendWrappedBuffer.flip(); |
| | | while (sendWrappedBuffer.hasRemaining()) |
| | | { |
| | | channel.write(sendWrappedBuffer); |
| | | } |
| | | final int written = sendWrappedBuffer.position(); |
| | | sendWrappedBuffer.clear(); |
| | | return written; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | private boolean isHandshaking(final HandshakeStatus status) |
| | | { |
| | | return status != HandshakeStatus.NOT_HANDSHAKING; |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | // Map of cipher phrases to effective key size (bits). Taken from the |
| | | // following RFCs: 5289, 4346, 3268,4132 and 4162. |
| | | private static final Map<String, Integer> cipherMap; |
| | | private static final Map<String, Integer> CIPHER_MAP; |
| | | static |
| | | { |
| | | cipherMap = new LinkedHashMap<String, Integer>(); |
| | | cipherMap.put("_WITH_AES_256_CBC_", new Integer(256)); |
| | | cipherMap.put("_WITH_CAMELLIA_256_CBC_", new Integer(256)); |
| | | cipherMap.put("_WITH_AES_256_GCM_", new Integer(256)); |
| | | cipherMap.put("_WITH_3DES_EDE_CBC_", new Integer(112)); |
| | | cipherMap.put("_WITH_AES_128_GCM_", new Integer(128)); |
| | | cipherMap.put("_WITH_SEED_CBC_", new Integer(128)); |
| | | cipherMap.put("_WITH_CAMELLIA_128_CBC_", new Integer(128)); |
| | | cipherMap.put("_WITH_AES_128_CBC_", new Integer(128)); |
| | | cipherMap.put("_WITH_IDEA_CBC_", new Integer(128)); |
| | | cipherMap.put("_WITH_DES_CBC_", new Integer(56)); |
| | | cipherMap.put("_WITH_RC2_CBC_40_", new Integer(40)); |
| | | cipherMap.put("_WITH_RC4_40_", new Integer(40)); |
| | | cipherMap.put("_WITH_DES40_CBC_", new Integer(40)); |
| | | cipherMap.put("_WITH_NULL_", new Integer(0)); |
| | | }; |
| | | CIPHER_MAP = new LinkedHashMap<String, Integer>(); |
| | | CIPHER_MAP.put("_WITH_AES_256_CBC_", new Integer(256)); |
| | | CIPHER_MAP.put("_WITH_CAMELLIA_256_CBC_", new Integer(256)); |
| | | CIPHER_MAP.put("_WITH_AES_256_GCM_", new Integer(256)); |
| | | CIPHER_MAP.put("_WITH_3DES_EDE_CBC_", new Integer(112)); |
| | | CIPHER_MAP.put("_WITH_AES_128_GCM_", new Integer(128)); |
| | | CIPHER_MAP.put("_WITH_SEED_CBC_", new Integer(128)); |
| | | CIPHER_MAP.put("_WITH_CAMELLIA_128_CBC_", new Integer(128)); |
| | | CIPHER_MAP.put("_WITH_AES_128_CBC_", new Integer(128)); |
| | | CIPHER_MAP.put("_WITH_IDEA_CBC_", new Integer(128)); |
| | | CIPHER_MAP.put("_WITH_DES_CBC_", new Integer(56)); |
| | | CIPHER_MAP.put("_WITH_RC2_CBC_40_", new Integer(40)); |
| | | CIPHER_MAP.put("_WITH_RC4_40_", new Integer(40)); |
| | | CIPHER_MAP.put("_WITH_DES40_CBC_", new Integer(40)); |
| | | CIPHER_MAP.put("_WITH_NULL_", new Integer(0)); |
| | | } |
| | | |
| | | private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | |
| | | |
| | |
| | | |
| | | |
| | | |
| | | private final ByteChannelImpl pimpl = new ByteChannelImpl(); |
| | | private final ByteChannel channel; |
| | | private final SSLEngine sslEngine; |
| | | |
| | | private ByteBuffer recvWrappedBuffer; |
| | | private ByteBuffer recvUnwrappedBuffer; |
| | | private ByteBuffer sendWrappedBuffer; |
| | | |
| | | private final Object handshakeLock = new Object(); |
| | | private final Object unwrapLock = new Object(); |
| | | private final Object wrapLock = new Object(); |
| | | private final Object readLock = new Object(); |
| | | private final Object writeLock = new Object(); |
| | | |
| | | |
| | | |
| | | private TLSByteChannel(final LDAPConnectionHandlerCfg config, |
| | | final ClientConnection c, final ByteChannel socketChannel, |
| | | final ClientConnection c, final ByteChannel channel, |
| | | final SSLContext sslContext) |
| | | { |
| | | |
| | | this.socketChannel = socketChannel; |
| | | this.channel = channel; |
| | | |
| | | // getHostName could potentially be very expensive and could block |
| | | // the connection handler for several minutes. (See issue 4229) |
| | |
| | | // avoid blocking new connections. Just remove for now to prevent |
| | | // potential DoS attacks. SSL sessions will not be reused and some |
| | | // cipher suites (such as Kerberos) will not work. |
| | | |
| | | // String hostName = socketChannel.socket().getInetAddress().getHostName(); |
| | | // int port = socketChannel.socket().getPort(); |
| | | // sslEngine = sslContext.createSSLEngine(hostName, port); |
| | | |
| | | sslEngine = sslContext.createSSLEngine(); |
| | | sslEngine.setUseClientMode(false); |
| | | |
| | | final Set<String> protocols = config.getSSLProtocol(); |
| | | if (!protocols.isEmpty()) |
| | | { |
| | |
| | | break; |
| | | } |
| | | |
| | | final SSLSession sslSession = sslEngine.getSession(); |
| | | sslBufferSize = sslSession.getPacketBufferSize(); |
| | | appBufSize = sslSession.getApplicationBufferSize(); |
| | | // Allocate read/write buffers. |
| | | final SSLSession session = sslEngine.getSession(); |
| | | final int wrappedBufferSize = session.getPacketBufferSize(); |
| | | final int unwrappedBufferSize = session.getApplicationBufferSize(); |
| | | |
| | | appNetData = ByteBuffer.allocate(sslBufferSize); |
| | | netData = ByteBuffer.allocate(sslBufferSize); |
| | | sendWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize); |
| | | recvWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize); |
| | | recvUnwrappedBuffer = ByteBuffer.allocate(unwrappedBufferSize); |
| | | |
| | | appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize()); |
| | | tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize()); |
| | | // Initially nothing has been received. |
| | | recvWrappedBuffer.flip(); |
| | | recvUnwrappedBuffer.flip(); |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized void close() throws IOException |
| | | public ByteChannel getChannel() |
| | | { |
| | | sslEngine.closeInbound(); |
| | | sslEngine.closeOutbound(); |
| | | final SSLEngineResult.HandshakeStatus hsStatus = sslEngine |
| | | .getHandshakeStatus(); |
| | | if (hsStatus != SSLEngineResult.HandshakeStatus.FINISHED |
| | | && hsStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) |
| | | { |
| | | doHandshakeWrite(hsStatus); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int getAppBufSize() |
| | | { |
| | | return appBufSize; |
| | | return pimpl; |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | public int getSSF() |
| | | { |
| | | int cipherKeySSF = 0; |
| | | final String cipherString = sslEngine.getSession().getCipherSuite(); |
| | | for (final Map.Entry<String, Integer> mapEntry : cipherMap.entrySet()) |
| | | for (final Map.Entry<String, Integer> mapEntry : CIPHER_MAP.entrySet()) |
| | | { |
| | | if (cipherString.indexOf(mapEntry.getKey()) >= 0) |
| | | { |
| | | cipherKeySSF = mapEntry.getValue().intValue(); |
| | | break; |
| | | return mapEntry.getValue().intValue(); |
| | | } |
| | | } |
| | | return cipherKeySSF; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isOpen() |
| | | { |
| | | if (sslEngine.isInboundDone() || sslEngine.isOutboundDone()) |
| | | { |
| | | return false; |
| | | } |
| | | return true; |
| | | return 0; |
| | | } |
| | | |
| | | |
| | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized int read(final ByteBuffer clearBuffer) throws IOException |
| | | { |
| | | SSLEngineResult.HandshakeStatus hsStatus; |
| | | if (!reading) |
| | | { |
| | | appNetData.clear(); |
| | | } |
| | | else |
| | | { |
| | | reading = false; |
| | | } |
| | | |
| | | if (!socketChannel.isOpen()) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | if (sslEngine.isInboundDone()) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | do |
| | | { |
| | | final int wrappedBytes = socketChannel.read(appNetData); |
| | | appNetData.flip(); |
| | | |
| | | if (wrappedBytes == -1) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | |
| | | if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) |
| | | { |
| | | doHandshakeRead(hsStatus); |
| | | } |
| | | |
| | | if (wrappedBytes == 0) |
| | | { |
| | | return 0; |
| | | } |
| | | |
| | | while (appNetData.hasRemaining()) |
| | | { |
| | | appData.clear(); |
| | | final SSLEngineResult res = sslEngine.unwrap(appNetData, appData); |
| | | appData.flip(); |
| | | |
| | | if (res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) |
| | | { |
| | | appNetData.compact(); |
| | | reading = true; |
| | | break; |
| | | } |
| | | else if (res.getStatus() != SSLEngineResult.Status.OK) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) |
| | | { |
| | | doHandshakeOp(hsStatus); |
| | | } |
| | | clearBuffer.put(appData); |
| | | } |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | } |
| | | while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP); |
| | | |
| | | return clearBuffer.position(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ByteChannel wrapChannel(final ByteChannel channel) |
| | | { |
| | | return this; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized int write(final ByteBuffer clearData) throws IOException |
| | | { |
| | | if (!socketChannel.isOpen() || sslEngine.isOutboundDone()) |
| | | { |
| | | throw new ClosedChannelException(); |
| | | } |
| | | |
| | | final int originalPosition = clearData.position(); |
| | | final int originalLimit = clearData.limit(); |
| | | final int length = originalLimit - originalPosition; |
| | | |
| | | if (length > sslBufferSize) |
| | | { |
| | | int pos = originalPosition; |
| | | int lim = originalPosition + sslBufferSize; |
| | | while (pos < originalLimit) |
| | | { |
| | | clearData.position(pos); |
| | | clearData.limit(lim); |
| | | writeInternal(clearData); |
| | | pos = lim; |
| | | lim = Math.min(originalLimit, pos + sslBufferSize); |
| | | } |
| | | return length; |
| | | } |
| | | else |
| | | { |
| | | return writeInternal(clearData); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | private void doHandshakeOp(SSLEngineResult.HandshakeStatus hsStatus) |
| | | throws IOException |
| | | { |
| | | SSLEngineResult res; |
| | | switch (hsStatus) |
| | | { |
| | | case NEED_TASK: |
| | | hsStatus = doTasks(); |
| | | break; |
| | | case NEED_WRAP: |
| | | tempData.clear(); |
| | | netData.clear(); |
| | | res = sslEngine.wrap(tempData, netData); |
| | | hsStatus = res.getHandshakeStatus(); |
| | | netData.flip(); |
| | | while (netData.hasRemaining()) |
| | | { |
| | | socketChannel.write(netData); |
| | | } |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | return; |
| | | default: |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | private void doHandshakeRead(SSLEngineResult.HandshakeStatus hsStatus) |
| | | throws IOException |
| | | { |
| | | do |
| | | { |
| | | doHandshakeOp(hsStatus); |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | } |
| | | while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK); |
| | | } |
| | | |
| | | |
| | | |
| | | private void doHandshakeUnwrap() throws IOException |
| | | { |
| | | netData.clear(); |
| | | tempData.clear(); |
| | | final int bytesRead = socketChannel.read(netData); |
| | | |
| | | if (bytesRead <= 0) |
| | | { |
| | | throw new ClosedChannelException(); |
| | | } |
| | | else |
| | | { |
| | | sslEngine.unwrap(netData, tempData); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | private void doHandshakeWrite(SSLEngineResult.HandshakeStatus hsStatus) |
| | | throws IOException |
| | | { |
| | | do |
| | | { |
| | | if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) |
| | | { |
| | | doHandshakeUnwrap(); |
| | | } |
| | | else |
| | | { |
| | | doHandshakeOp(hsStatus); |
| | | } |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | } |
| | | while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP); |
| | | } |
| | | |
| | | |
| | | |
| | | private SSLEngineResult.HandshakeStatus doTasks() |
| | | { |
| | | Runnable task; |
| | | while ((task = sslEngine.getDelegatedTask()) != null) |
| | | { |
| | | task.run(); |
| | | } |
| | | return sslEngine.getHandshakeStatus(); |
| | | } |
| | | |
| | | |
| | | |
| | | private int writeInternal(final ByteBuffer clearData) throws IOException |
| | | { |
| | | int totBytesSent = 0; |
| | | SSLEngineResult.HandshakeStatus hsStatus; |
| | | hsStatus = sslEngine.getHandshakeStatus(); |
| | | |
| | | if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) |
| | | { |
| | | doHandshakeWrite(hsStatus); |
| | | } |
| | | |
| | | while (clearData.hasRemaining()) |
| | | { |
| | | netData.clear(); |
| | | final SSLEngineResult res = sslEngine.wrap(clearData, netData); |
| | | netData.flip(); |
| | | if (netData.remaining() == 0) |
| | | { |
| | | // wrap didn't produce any data from our clear buffer. |
| | | // Throw exception to prevent looping. |
| | | throw new SSLException("SSLEngine.wrap produced 0 bytes"); |
| | | } |
| | | |
| | | if (res.getStatus() != SSLEngineResult.Status.OK) |
| | | { |
| | | throw new ClosedChannelException(); |
| | | } |
| | | |
| | | if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP |
| | | || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) |
| | | { |
| | | doHandshakeWrite(hsStatus); |
| | | } |
| | | totBytesSent += socketChannel.write(netData); |
| | | } |
| | | return totBytesSent; |
| | | } |
| | | } |