| /* |
| * 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.harmony.xnet.provider.jsse; |
| |
| import java.nio.ByteBuffer; |
| import java.nio.ReadOnlyBufferException; |
| |
| import javax.net.ssl.SSLEngine; |
| import javax.net.ssl.SSLEngineResult; |
| import javax.net.ssl.SSLEngineResult.HandshakeStatus; |
| import javax.net.ssl.SSLException; |
| import javax.net.ssl.SSLSession; |
| |
| import org.apache.harmony.nio.AddressUtil; |
| |
| /** |
| * Implementation of SSLEngine. |
| * @see javax.net.ssl.SSLEngine class documentation for more information. |
| */ |
| public class SSLEngineImpl extends SSLEngine { |
| |
| // indicates if peer mode was set |
| private boolean peer_mode_was_set = false; |
| // indicates if handshake has been started |
| private boolean handshake_started = false; |
| // indicates if inbound operations finished |
| private boolean isInboundDone = false; |
| // indicates if outbound operations finished |
| private boolean isOutboundDone = false; |
| // indicates if engine was closed (it means that |
| // all the works on it are done, except (probably) some finalizing work) |
| private boolean engine_was_closed = false; |
| // indicates if engine was shut down (it means that |
| // all cleaning work had been done and the engine is not operable) |
| private boolean engine_was_shutdown = false; |
| // indicates if close_notify alert had been sent to another peer |
| private boolean close_notify_was_sent = false; |
| // indicates if close_notify alert had been received from another peer |
| private boolean close_notify_was_received = false; |
| private boolean need_alert_wrap = false; |
| |
| // active session object |
| private SSLSessionImpl session; |
| |
| // peer configuration parameters |
| protected SSLParameters sslParameters; |
| |
| // logger |
| private Logger.Stream logger = Logger.getStream("engine"); |
| |
| // Pointer to the SSL struct |
| private long SSL; |
| // Pointer to the custom struct used for this engine |
| private long SSLEngineAddress; |
| |
| private HandshakeStatus handshakeStatus; |
| |
| static { |
| initImpl(); |
| } |
| |
| private static native void initImpl(); |
| private static native long initSSL(long context); |
| private static native long initSSLEngine(long context); |
| private static native HandshakeStatus connectImpl(long ssl); |
| private static native HandshakeStatus acceptImpl(long ssl); |
| private static native SSLEngineResult wrapImpl(long ssl, long sslEngineAddress, |
| long src_address, int src_len, long dst_address, int dst_len); |
| private static native SSLEngineResult unwrapImpl(long ssl, long sslEngineAddress, |
| long src_address, int src_len, long dst_address, int dst_len); |
| |
| /** |
| * Ctor |
| * @param sslParameters: SSLParameters |
| */ |
| protected SSLEngineImpl(SSLParameters sslParameters) { |
| super(); |
| this.sslParameters = sslParameters; |
| SSL = initSSL(sslParameters.getSSLContextAddress()); |
| SSLEngineAddress = initSSLEngine(SSL); |
| } |
| |
| /** |
| * Ctor |
| * @param host: String |
| * @param port: int |
| * @param sslParameters: SSLParameters |
| */ |
| protected SSLEngineImpl(String host, int port, SSLParameters sslParameters) { |
| super(host, port); |
| this.sslParameters = sslParameters; |
| SSL = initSSL(sslParameters.getSSLContextAddress()); |
| SSLEngineAddress = initSSLEngine(SSL); |
| } |
| |
| /** |
| * Starts the handshake. |
| * @throws SSLException |
| * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation |
| * for more information |
| */ |
| @Override |
| public void beginHandshake() throws SSLException { |
| if (engine_was_closed) { |
| throw new SSLException("Engine has already been closed."); |
| } |
| if (!peer_mode_was_set) { |
| throw new IllegalStateException("Client/Server mode was not set"); |
| } |
| |
| if (!getEnableSessionCreation()) { |
| handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; |
| throw new SSLException("New session creation is disabled"); |
| } |
| // TODO: need to repeat connect/accept if status was waiting on wrap/unwrap previously? |
| if (!handshake_started) { |
| handshake_started = true; |
| |
| SSLSessionContextImpl sessionContext; |
| if (sslParameters.getUseClientMode()) { |
| if (logger != null) { |
| logger.println("SSLEngineImpl: CLIENT connecting"); |
| } |
| |
| handshakeStatus = connectImpl(SSL); |
| sessionContext = sslParameters.getClientSessionContext(); |
| } else { |
| if (logger != null) { |
| logger.println("SSLEngineImpl: SERVER accepting connection"); |
| } |
| handshakeStatus = acceptImpl(SSL); |
| sessionContext = sslParameters.getServerSessionContext(); |
| } |
| session = new SSLSessionImpl(sslParameters, SSL); |
| sessionContext.putSession(session); |
| } |
| } |
| |
| private static native void shutdownImpl(long SSL); |
| private static native void closeInboundImpl(long SSLEngineAddress); |
| |
| /** |
| * Closes inbound operations of this engine |
| * @throws SSLException |
| * @see javax.net.ssl.SSLEngine#closeInbound() method documentation |
| * for more information |
| */ |
| @Override |
| public void closeInbound() throws SSLException { |
| if (logger != null) { |
| logger.println("closeInbound() "+isInboundDone); |
| } |
| if (isInboundDone) { |
| return; |
| } |
| isInboundDone = true; |
| engine_was_closed = true; |
| if (handshake_started) { |
| if (!close_notify_was_received) { |
| if (session != null) { |
| session.invalidate(); |
| } |
| if (!close_notify_was_sent) { |
| shutdownImpl(SSL); |
| close_notify_was_sent = true; |
| } |
| need_alert_wrap = true; |
| throw new SSLException("Inbound is closed before close_notify alert has been received."); |
| } |
| closeInboundImpl(SSLEngineAddress); |
| } else { |
| // engine is closing before initial handshake has been made |
| shutdown(); |
| } |
| } |
| |
| /** |
| * Closes outbound operations of this engine |
| * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation |
| * for more information |
| */ |
| @Override |
| public void closeOutbound() { |
| if (logger != null) { |
| logger.println("closeOutbound() "+isOutboundDone); |
| } |
| if (isOutboundDone) { |
| return; |
| } |
| isOutboundDone = true; |
| engine_was_closed = true; |
| if (handshake_started) { |
| if (!close_notify_was_sent) { |
| if (!close_notify_was_sent) { |
| shutdownImpl(SSL); |
| close_notify_was_sent = true; |
| } |
| need_alert_wrap = true; |
| } |
| } else { |
| // engine is closing before initial handshake has been made |
| shutdown(); |
| } |
| } |
| |
| /** |
| * Returns handshake's delegated tasks to be run |
| * @return the delegated task to be executed. |
| * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation |
| * for more information |
| */ |
| @Override |
| public Runnable getDelegatedTask() { |
| return null; |
| //return handshakeProtocol.getTask(); |
| } |
| |
| /** |
| * Returns names of supported cipher suites. |
| * @return array of strings containing the names of supported cipher suites |
| * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method |
| * documentation for more information |
| */ |
| @Override |
| public String[] getSupportedCipherSuites() { |
| return sslParameters.getSupportedCipherSuites(); |
| } |
| |
| // --------------- SSLParameters based methods --------------------- |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method |
| * documentation for more information |
| */ |
| @Override |
| public String[] getEnabledCipherSuites() { |
| return sslParameters.getEnabledCipherSuites(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String) method |
| * documentation for more information |
| */ |
| @Override |
| public void setEnabledCipherSuites(String[] suites) { |
| sslParameters.setEnabledCipherSuites(SSL, suites); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method |
| * documentation for more information |
| */ |
| @Override |
| public String[] getSupportedProtocols() { |
| return sslParameters.getSupportedProtocols(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method |
| * documentation for more information |
| */ |
| @Override |
| public String[] getEnabledProtocols() { |
| return sslParameters.getEnabledProtocols(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String) method |
| * documentation for more information |
| */ |
| @Override |
| public void setEnabledProtocols(String[] protocols) { |
| sslParameters.setEnabledProtocols(SSL, protocols); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method |
| * documentation for more information |
| */ |
| @Override |
| public void setUseClientMode(boolean mode) { |
| if (handshake_started) { |
| throw new IllegalArgumentException( |
| "Could not change the mode after the initial handshake has begun."); |
| } |
| sslParameters.setUseClientMode(mode); |
| peer_mode_was_set = true; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getUseClientMode() method |
| * documentation for more information |
| */ |
| @Override |
| public boolean getUseClientMode() { |
| return sslParameters.getUseClientMode(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method |
| * documentation for more information |
| */ |
| @Override |
| public void setNeedClientAuth(boolean need) { |
| sslParameters.setNeedClientAuth(SSL, need); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method |
| * documentation for more information |
| */ |
| @Override |
| public boolean getNeedClientAuth() { |
| return sslParameters.getNeedClientAuth(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method |
| * documentation for more information |
| */ |
| @Override |
| public void setWantClientAuth(boolean want) { |
| sslParameters.setWantClientAuth(SSL, want); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getWantClientAuth() method |
| * documentation for more information |
| */ |
| @Override |
| public boolean getWantClientAuth() { |
| return sslParameters.getWantClientAuth(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method |
| * documentation for more information |
| */ |
| @Override |
| public void setEnableSessionCreation(boolean flag) { |
| sslParameters.setEnableSessionCreation(flag); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method |
| * documentation for more information |
| */ |
| @Override |
| public boolean getEnableSessionCreation() { |
| return sslParameters.getEnableSessionCreation(); |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method |
| * documentation for more information |
| */ |
| @Override |
| public HandshakeStatus getHandshakeStatus() { |
| if (!handshake_started || engine_was_shutdown) { |
| // initial handshake has not been started yet |
| return HandshakeStatus.NOT_HANDSHAKING; |
| } |
| if (need_alert_wrap) { |
| return HandshakeStatus.NEED_WRAP; |
| } |
| if (close_notify_was_sent && !close_notify_was_received) { |
| // waiting for "close_notify" response |
| return HandshakeStatus.NEED_UNWRAP; |
| } |
| return handshakeStatus; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#getSession() method |
| * documentation for more information |
| */ |
| @Override |
| public SSLSession getSession() { |
| if (session != null) { |
| return session; |
| } |
| return SSLSessionImpl.NULL_SESSION; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#isInboundDone() method |
| * documentation for more information |
| */ |
| @Override |
| public boolean isInboundDone() { |
| return isInboundDone || engine_was_closed; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLEngine#isOutboundDone() method |
| * documentation for more information |
| */ |
| @Override |
| public boolean isOutboundDone() { |
| return isOutboundDone; |
| } |
| |
| /** |
| * Decodes one complete SSL/TLS record provided in the source buffer. |
| * If decoded record contained application data, this data will |
| * be placed in the destination buffers. |
| * For more information about TLS record fragmentation see |
| * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2. |
| * @param src source buffer containing SSL/TLS record. |
| * @param dsts destination buffers to place received application data. |
| * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int) |
| * method documentation for more information |
| */ |
| @Override |
| public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, |
| int offset, int length) throws SSLException { |
| if (engine_was_shutdown) { |
| return new SSLEngineResult(SSLEngineResult.Status.CLOSED, |
| HandshakeStatus.NOT_HANDSHAKING, 0, 0); |
| } |
| if ((src == null) || (dsts == null)) { |
| throw new IllegalStateException( |
| "Some of the input parameters are null"); |
| } |
| |
| if (!handshake_started) { |
| beginHandshake(); |
| } |
| |
| // get direct addresses to the buffers |
| // only use the first buffer at the moment |
| ByteBuffer src_temp_buffer = null, dst_temp_buffer = null; |
| long src_address, dst_address; |
| int src_length = src.remaining(); |
| int dst_length = dsts[0].remaining(); |
| if (src.isDirect()) { |
| src_address = AddressUtil.getDirectBufferAddress(src) + src.position(); |
| } else { |
| // create a temporary buffer and copy the contents |
| src_temp_buffer = ByteBuffer.allocateDirect(src_length); |
| src_temp_buffer.put(src.array(), src.position(), src_length); |
| src_temp_buffer.rewind(); |
| src_address = AddressUtil.getDirectBufferAddress(src_temp_buffer); |
| } |
| if (dsts[0].isDirect()) { |
| dst_address = AddressUtil.getDirectBufferAddress(dsts[0]) + dsts[0].position(); |
| } else { |
| dst_temp_buffer = ByteBuffer.allocateDirect(dst_length); |
| dst_address = AddressUtil.getDirectBufferAddress(dst_temp_buffer); |
| } |
| |
| SSLEngineResult result = unwrapImpl(SSL, SSLEngineAddress, src_address, src_length, dst_address, dst_length); |
| |
| // update the buffers contents and positions |
| src.position(src.position() + result.bytesConsumed()); |
| |
| if (dst_temp_buffer == null) { |
| dsts[0].position(dsts[0].position() + result.bytesConsumed()); |
| } else { |
| // if a temporary buffer was used, copy buffer contents |
| int position = dsts[0].position(); |
| dsts[0].put(dst_temp_buffer); |
| // adjust position as not all bytes may have been written |
| dsts[0].position(position + result.bytesProduced()); |
| } |
| |
| // update handshake status |
| handshakeStatus = result.getHandshakeStatus(); |
| |
| if (handshakeStatus == HandshakeStatus.FINISHED) { |
| // If we've just completed the handshake, refresh the data in the SSLSession |
| session.refreshSessionData(null, sslParameters, SSL); |
| handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Encodes the application data into SSL/TLS record. If handshake status |
| * of the engine differs from NOT_HANDSHAKING the operation can work |
| * without consuming of the source data. |
| * For more information about TLS record fragmentation see |
| * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2. |
| * @param srcs the source buffers with application data to be encoded |
| * into SSL/TLS record. |
| * @param offset the offset in the destination buffers array pointing to |
| * the first buffer with the source data. |
| * @param len specifies the maximum number of buffers to be procesed. |
| * @param dst the destination buffer where encoded data will be placed. |
| * @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method |
| * documentation for more information |
| */ |
| @Override |
| public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, |
| int len, ByteBuffer dst) throws SSLException { |
| if (engine_was_shutdown) { |
| return new SSLEngineResult(SSLEngineResult.Status.CLOSED, |
| HandshakeStatus.NOT_HANDSHAKING, 0, 0); |
| } |
| if ((srcs == null) || (dst == null)) { |
| throw new IllegalStateException( |
| "Some of the input parameters are null"); |
| } |
| if (dst.isReadOnly()) { |
| throw new ReadOnlyBufferException(); |
| } |
| |
| if (!handshake_started) { |
| beginHandshake(); |
| } |
| |
| if (need_alert_wrap) { |
| need_alert_wrap = false; |
| } |
| |
| // get direct addresses to the buffers |
| // only use the first buffer at the moment |
| ByteBuffer src_temp_buffer = null, dst_temp_buffer = null; |
| long src_address, dst_address; |
| int src_length = srcs[0].remaining(); |
| int dst_length = dst.remaining(); |
| if (srcs[0].isDirect()) { |
| src_address = AddressUtil.getDirectBufferAddress(srcs[0]) + srcs[0].position(); |
| } else { |
| // create a temporary buffer and copy the contents |
| src_temp_buffer = ByteBuffer.allocateDirect(src_length); |
| src_temp_buffer.put(srcs[0].array(), srcs[0].position(), src_length); |
| src_temp_buffer.rewind(); |
| src_address = AddressUtil.getDirectBufferAddress(src_temp_buffer); |
| } |
| if (dst.isDirect()) { |
| dst_address = AddressUtil.getDirectBufferAddress(dst) + dst.position(); |
| } else { |
| dst_temp_buffer = ByteBuffer.allocateDirect(dst_length); |
| dst_address = AddressUtil.getDirectBufferAddress(dst_temp_buffer); |
| } |
| |
| SSLEngineResult result = wrapImpl(SSL, SSLEngineAddress, src_address, src_length, dst_address, dst_length); |
| |
| // update the buffers contents and positions |
| srcs[0].position(srcs[0].position() + result.bytesConsumed()); |
| |
| if (dst_temp_buffer == null) { |
| dst.position(dst.position() + result.bytesConsumed()); |
| } else { |
| // if a temporary buffer was used, copy buffer contents |
| int position = dst.position(); |
| dst.put(dst_temp_buffer); |
| // adjust position as not all bytes may have been written |
| dst.position(position + result.bytesProduced()); |
| } |
| |
| // update handshake status |
| handshakeStatus = result.getHandshakeStatus(); |
| |
| if (handshakeStatus == HandshakeStatus.FINISHED) { |
| // If we've just completed the handshake, refresh the data in |
| // the SSLSession and set the handshake status to NOT_HANDSHAKING |
| session.refreshSessionData(null, sslParameters, SSL); |
| handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; |
| } |
| |
| return result; |
| } |
| |
| // Shutdown the engine and makes all cleanup work. |
| private void shutdown() { |
| engine_was_closed = true; |
| engine_was_shutdown = true; |
| isOutboundDone = true; |
| isInboundDone = true; |
| } |
| |
| |
| private SSLEngineResult.Status getEngineStatus() { |
| return (engine_was_closed) |
| ? SSLEngineResult.Status.CLOSED |
| : SSLEngineResult.Status.OK; |
| } |
| } |
| |