harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ge...@apache.org
Subject svn commit: r423275 [6/7] - in /incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org: ./ apache/ apache/harmony/ apache/harmony/security/ apache/harmony/security/provider/ apache/harmony/security/provider/jsse/
Date Tue, 18 Jul 2006 22:50:14 GMT
Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketImpl.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketImpl.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketImpl.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketImpl.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,819 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.security.provider.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * SSLSocket implementation.
+ * @see javax.net.ssl.SSLSocket class documentation for more information.
+ */
+public class SSLSocketImpl extends SSLSocket {
+
+    // indicates if handshake has been started
+    private boolean handshake_started = false;
+
+    // record protocol to be used
+    protected SSLRecordProtocol recordProtocol;
+    // handshake protocol to be used
+    private HandshakeProtocol handshakeProtocol;
+    // alert protocol to be used
+    private AlertProtocol alertProtocol;
+    // application data input stream, this stream is presented by
+    // ssl socket as an input stream. Additionaly this object is a
+    // place where application data will be stored by record protocol
+    private SSLSocketInputStream appDataIS;
+    // outcoming application data stream
+    private SSLSocketOutputStream appDataOS;
+    // active session object
+    private SSLSessionImpl session;
+
+    private boolean socket_was_closed = false;
+
+    // the sslParameters object incapsulates all the info
+    // about supported and enabled cipher suites and protocols,
+    // as well as the information about client/server mode of
+    // ssl socket, whether it require/want client authentication or not,
+    // and controls whether new SSL sessions may be established by this
+    // socket or not.
+    protected SSLParameters sslParameters;
+    // super's streams to be wrapped:
+    protected InputStream input;
+    protected OutputStream output;
+    // handshake complete listeners
+    private ArrayList listeners;
+    // logger
+    private Logger.Stream logger = Logger.getStream("socket");
+
+    // ----------------- Constructors and initializers --------------------
+
+    /**
+     * Constructor
+     * @param   sslParameters:  SSLParameters
+     * @see javax.net.ssl.SSLSocket#SSLSocket() method documentation
+     * for more information.
+     */
+    protected SSLSocketImpl(SSLParameters sslParameters) {
+        super();
+        this.sslParameters = sslParameters;
+        // init should be called after creation!
+    }
+
+    /**
+     * Constructor
+     * @param   host:   String
+     * @param   port:   int
+     * @param   sslParameters:  SSLParameters
+     * @throws  IOException
+     * @throws  UnknownHostException
+     * @see javax.net.ssl.SSLSocket#SSLSocket(String,int) 
+     * method documentation for more information.
+     */
+    protected SSLSocketImpl(String host, int port, SSLParameters sslParameters)
+            throws IOException, UnknownHostException {
+        super(host, port);
+        this.sslParameters = sslParameters;
+        init();
+    }
+
+    /**
+     * Constructor
+     * @param   host:   String
+     * @param   port:   int
+     * @param   localHost:  InetAddress
+     * @param   localPort:  int
+     * @param   sslParameters:  SSLParameters
+     * @throws  IOException
+     * @throws  UnknownHostException
+     * @see javax.net.ssl.SSLSocket#SSLSocket(String,int,InetAddress,int)
+     * method documentation for more information.
+     */
+    protected SSLSocketImpl(String host, int port,
+            InetAddress localHost, int localPort,
+            SSLParameters sslParameters) throws IOException,
+            UnknownHostException {
+        super(host, port, localHost, localPort);
+        this.sslParameters = sslParameters;
+        init();
+    }
+
+    /**
+     * Constructor
+     * @param   host:   InetAddress
+     * @param   port:   int
+     * @param   sslParameters:  SSLParameters
+     * @return
+     * @throws  IOException
+     * @see javax.net.ssl.SSLSocket#SSLSocket(InetAddress,int) 
+     * method documentation for more information.
+     */
+    protected SSLSocketImpl(InetAddress host, int port,
+            SSLParameters sslParameters) throws IOException {
+        super(host, port);
+        this.sslParameters = sslParameters;
+        init();
+    }
+
+    /**
+     * Constructor
+     * @param   address:    InetAddress
+     * @param   port:   int
+     * @param   localAddress:   InetAddress
+     * @param   localPort:  int
+     * @param   sslParameters:  SSLParameters
+     * @return
+     * @throws  IOException
+     * @see javax.net.ssl.SSLSocket#SSLSocket(InetAddress,int,InetAddress,int)
+     * method documentation for more information.
+     */
+    protected SSLSocketImpl(InetAddress address, int port,
+            InetAddress localAddress, int localPort,
+            SSLParameters sslParameters) throws IOException {
+        super(address, port, localAddress, localPort);
+        this.sslParameters = sslParameters;
+        init();
+    }
+
+    /**
+     * Initialize the SSL socket.
+     */
+    protected void init() throws IOException {
+        if (appDataIS != null) {
+            // already initialized
+            return;
+        }
+        initTransportLayer();
+        appDataIS = new SSLSocketInputStream(this);
+        appDataOS = new SSLSocketOutputStream(this);
+    }
+
+    /**
+     * Initialize the transport data streams.
+     */
+    protected void initTransportLayer() throws IOException {
+        input = super.getInputStream();
+        output = super.getOutputStream();
+    }
+
+    /**
+     * Closes the transport data streams.
+     */
+    protected void closeTransportLayer() throws IOException {
+        super.close();
+        if (input != null) {
+            input.close();
+            output.close();
+        }
+    }
+
+    // --------------- SSLParameters based methods ---------------------
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites()
+     * method documentation for more information
+     */
+    public String[] getSupportedCipherSuites() {
+        return CipherSuite.getSupportedCipherSuiteNames();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites()
+     * method documentation for more information
+     */
+    public String[] getEnabledCipherSuites() {
+        return sslParameters.getEnabledCipherSuites();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[])
+     * method documentation for more information
+     */
+    public void setEnabledCipherSuites(String[] suites) {
+        sslParameters.setEnabledCipherSuites(suites);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getSupportedProtocols()
+     * method documentation for more information
+     */
+    public String[] getSupportedProtocols() {
+        return (String[]) ProtocolVersion.supportedProtocols.clone();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getEnabledProtocols()
+     * method documentation for more information
+     */
+    public String[] getEnabledProtocols() {
+        return sslParameters.getEnabledProtocols();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#setEnabledProtocols(String[])
+     * method documentation for more information
+     */
+    public void setEnabledProtocols(String[] protocols) {
+        sslParameters.setEnabledProtocols(protocols);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean)
+     * method documentation for more information
+     */
+    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);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getUseClientMode()
+     * method documentation for more information
+     */
+    public boolean getUseClientMode() {
+        return sslParameters.getUseClientMode();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean)
+     * method documentation for more information
+     */
+    public void setNeedClientAuth(boolean need) {
+        sslParameters.setNeedClientAuth(need);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getNeedClientAuth()
+     * method documentation for more information
+     */
+    public boolean getNeedClientAuth() {
+        return sslParameters.getNeedClientAuth();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean)
+     * method documentation for more information
+     */
+    public void setWantClientAuth(boolean want) {
+        sslParameters.setWantClientAuth(want);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getWantClientAuth()
+     * method documentation for more information
+     */
+    public boolean getWantClientAuth() {
+        return sslParameters.getWantClientAuth();
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean)
+     * method documentation for more information
+     */
+    public void setEnableSessionCreation(boolean flag) {
+        sslParameters.setEnableSessionCreation(flag);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getEnableSessionCreation()
+     * method documentation for more information
+     */
+    public boolean getEnableSessionCreation() {
+        return sslParameters.getEnableSessionCreation();
+    }
+
+    // -----------------------------------------------------------------
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getSession()
+     * method documentation for more information
+     */
+    public SSLSession getSession() {
+        if (!handshake_started) {
+            try {
+                startHandshake();
+            } catch (IOException e) {
+                // return an invalid session with
+                // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+                return SSLSessionImpl.NULL_SESSION;
+            }
+        }
+        return session;
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(HandshakeCompletedListener)
+     * method documentation for more information
+     */
+    public void addHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Provided listener is null");
+        }
+        if (listeners == null) {
+            listeners = new ArrayList();
+        }
+        listeners.add(listener);
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(HandshakeCompletedListener)
+     * method documentation for more information
+     */
+    public void removeHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Provided listener is null");
+        }
+        if (listeners == null) {
+            throw new IllegalArgumentException(
+                    "Provided listener is not registered");
+        }
+        if (!listeners.remove(listener)) {
+            throw new IllegalArgumentException(
+                    "Provided listener is not registered");
+        }
+    }
+
+    /**
+     * Performs the handshake process over the SSL/TLS connection
+     * as described in rfc 2246, TLS v1 specification
+     * http://www.ietf.org/rfc/rfc2246.txt. If the initial handshake
+     * has been already done, this method initiates rehandshake.
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#startHandshake()
+     * method documentation for more information
+     */
+    public void startHandshake() throws IOException {
+        if (appDataIS == null) {
+            throw new IOException("Socket is not connected.");
+        }
+        if (socket_was_closed) {
+            throw new IOException("Socket has already been closed.");
+        }
+
+        if (!handshake_started) {
+            handshake_started = true;
+            if (sslParameters.getUseClientMode()) {
+                if (logger != null) {
+                    logger.println("SSLSocketImpl: CLIENT");
+                }
+                handshakeProtocol = new ClientHandshakeImpl(this);
+            } else {
+                if (logger != null) {
+                    logger.println("SSLSocketImpl: SERVER");
+                }
+                handshakeProtocol = new ServerHandshakeImpl(this);
+            }
+
+            alertProtocol = new AlertProtocol();
+            recordProtocol = new SSLRecordProtocol(handshakeProtocol,
+                    alertProtocol, new SSLStreamedInput(input),
+                    appDataIS.dataPoint);
+        }
+
+        if (logger != null) {
+            logger.println("SSLSocketImpl.startHandshake");
+        }
+
+        handshakeProtocol.start();
+
+        doHandshake();
+
+        if (logger != null) {
+            logger.println("SSLSocketImpl.startHandshake: END");
+        }
+    }
+
+
+    // ---------------- Socket's methods overridings -------------------
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getInputStream()
+     * method documentation for more information
+     */
+    public InputStream getInputStream() throws IOException {
+        if (socket_was_closed) {
+            throw new IOException("Socket has already been closed.");
+        }
+        return appDataIS;
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#getOutputStream()
+     * method documentation for more information
+     */
+    public OutputStream getOutputStream() throws IOException {
+        if (socket_was_closed) {
+            throw new IOException("Socket has already been closed.");
+        }
+        return appDataOS;
+    }
+
+    /**
+     * This method works according to the specification of implemented class.
+     * @see java.net.Socket#connect(SocketAddress)
+     * method documentation for more information
+     */
+    public void connect(SocketAddress endpoint) throws IOException {
+        super.connect(endpoint);
+        init();
+    }
+    
+    /**
+     * This method works according to the specification of implemented class.
+     * @see java.net.Socket#connect(SocketAddress,int)
+     * method documentation for more information
+     */
+    public void connect(SocketAddress endpoint, int timeout) 
+            throws IOException {
+        super.connect(endpoint, timeout);
+        init();
+    }
+    
+    /**
+     * This method works according to the specification of implemented class.
+     * @see javax.net.ssl.SSLSocket#close()
+     * method documentation for more information
+     */
+    public void close() throws IOException {
+        if (logger != null) {
+            logger.println("SSLSocket.close "+socket_was_closed);
+        }
+        if (!socket_was_closed) {
+            if (handshake_started) {
+                alertProtocol.alert(AlertProtocol.WARNING,
+                        AlertProtocol.CLOSE_NOTIFY);
+                try {
+                    output.write(alertProtocol.wrap());
+                } catch (IOException ex) { }
+                alertProtocol.setProcessed();
+            }
+            shutdown();
+            closeTransportLayer();
+            socket_was_closed = true;
+        }
+    }
+
+    /**
+     * This method is not supported for SSLSocket implementation.
+     */
+    public void sendUrgentData(int data) throws IOException {
+        throw new SocketException(
+                "Method sendUrgentData() is not supported.");
+    }
+
+    /**
+     * This method is not supported for SSLSocket implementation.
+     */
+    public void setOOBInline(boolean on) throws SocketException {
+        throw new SocketException(
+                "Methods sendUrgentData, setOOBInline are not supported.");
+    }
+
+    /**
+     * This method is not supported for SSLSocket implementation.
+     */
+    public void shutdownOutput() {
+        throw new UnsupportedOperationException(
+                "Method shutdownOutput() is not supported.");
+    }
+
+    /**
+     * This method is not supported for SSLSocket implementation.
+     */
+    public void shutdownInput() {
+        throw new UnsupportedOperationException(
+                "Method shutdownInput() is not supported.");
+    }
+
+    /**
+     * Returns the string representation of the object.
+     */
+    public String toString() {
+        return "[SSLSocketImpl]";
+    }
+
+    // -----------------------------------------------------------------
+
+    // Shutdownes the ssl socket and makes all cleanup work.
+    private void shutdown() {
+        if (handshake_started) {
+            alertProtocol.shutdown();
+            alertProtocol = null;
+            handshakeProtocol.shutdown();
+            handshakeProtocol = null;
+            recordProtocol.shutdown();
+            recordProtocol = null;
+        }
+        socket_was_closed = true;
+    }
+
+    /**
+     * This method is called by SSLSocketInputStream class
+     * when client application tryes to read application data from
+     * the stream, but there is no data in its underlying buffer.
+     * @throws  IOException
+     */
+    protected void needAppData() throws IOException {
+        if (!handshake_started) {
+            startHandshake();
+        }
+        int type;
+        if (logger != null) {
+            logger.println("SSLSocket.needAppData..");
+        }
+        try {
+            while(appDataIS.available() == 0) {
+                // read and unwrap the record contained in the transport
+                // input stream (SSLStreamedInput), pass it
+                // to appropriate client protocol (alert, handshake, or app)
+                // and retrieve the type of unwrapped data
+                switch (type = recordProtocol.unwrap()) {
+                    case ContentType.HANDSHAKE:
+                        if (!handshakeProtocol.getStatus().equals(
+                                SSLEngineResult.HandshakeStatus
+                                .NOT_HANDSHAKING)) {
+                            // handshake protocol got addressed to it message 
+                            // and did not ignore it, so it's a rehandshake
+                            doHandshake();
+                        }
+                        break;
+                    case ContentType.ALERT:
+                        processAlert();
+                        if (socket_was_closed) {
+                            return;
+                        }
+                        break;
+                    case ContentType.APPLICATION_DATA:
+                        if (logger != null) {
+                            logger.println(
+                                    "SSLSocket.needAppData: got the data");
+                        }
+                        break;
+                    default:
+                        // will throw exception
+                        reportFatalAlert(AlertProtocol.UNEXPECTED_MESSAGE,
+                                new SSLException("Unexpected message of type "
+                                    + type + " has been got"));
+                }
+                if (alertProtocol.hasAlert()) {
+                    // warning alert occured during wrap or unwrap
+                    // (note: fatal alert causes AlertException
+                    // to be thrown)
+                    output.write(alertProtocol.wrap());
+                    alertProtocol.setProcessed();
+                }
+                if (socket_was_closed) {
+                    appDataIS.setEnd();
+                    return;
+                }
+            }
+        } catch (AlertException e) {
+            // will throw exception
+            reportFatalAlert(e.getDescriptionCode(), e.getReason());
+        } catch (EndOfSourceException e) {
+            // end of socket's input stream has been reached
+            appDataIS.setEnd();
+        }
+        if (logger != null) {
+            logger.println("SSLSocket.needAppData: app data len: "
+                    + appDataIS.available());
+        }
+    }
+
+    /**
+     * This method is called by SSLSocketOutputStream when client application
+     * tryes to send the data over ssl protocol.
+     */
+    protected void writeAppData(byte[] data, int offset, int len)
+                                                    throws IOException {
+        if (!handshake_started) {
+            startHandshake();
+        }
+        if (logger != null) {
+            logger.println("SSLSocket.writeAppData: " +
+                    len + " " + SSLRecordProtocol.MAX_DATA_LENGTH);
+            //logger.println(new String(data, offset, len));
+        }
+        try {
+            if (len < SSLRecordProtocol.MAX_DATA_LENGTH) {
+                output.write(recordProtocol.wrap(ContentType.APPLICATION_DATA,
+                            data, offset, len));
+            } else {
+                while (len >= SSLRecordProtocol.MAX_DATA_LENGTH) {
+                    output.write(recordProtocol.wrap(
+                                ContentType.APPLICATION_DATA, data, offset,
+                                SSLRecordProtocol.MAX_DATA_LENGTH));
+                    offset += SSLRecordProtocol.MAX_DATA_LENGTH;
+                    len -= SSLRecordProtocol.MAX_DATA_LENGTH;
+                }
+                if (len > 0) {
+                    output.write(
+                        recordProtocol.wrap(ContentType.APPLICATION_DATA,
+                                            data, offset, len));
+                }
+            }
+        } catch (AlertException e) {
+            // will throw exception
+            reportFatalAlert(e.getDescriptionCode(), e.getReason());
+        }
+    }
+
+    /*
+     * Performs handshake proccess over this connection. The hanshake
+     * process is dirrected by the handshake status code provided by
+     * handshake protocol. If this status is NEED_WRAP, method retrieves
+     * handshake message from handshake protocol and sends it to another peer.
+     * If this status is NEED_UNWRAP, method receives and processes handshake
+     * message from another peer. Each of this stages (wrap/unwrap) change
+     * the state of handshake protocol and this process is performed 
+     * until handshake status is FINISHED. After handshake process is finnished
+     * handshake completed event are sent to the registered listeners.
+     * For more information about the handshake process see
+     * TLS v1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 7.3.
+     */
+    private void doHandshake() throws IOException {
+        SSLEngineResult.HandshakeStatus status;
+        int type;
+        try {
+            while (!(status = handshakeProtocol.getStatus()).equals(
+                        SSLEngineResult.HandshakeStatus.FINISHED)) {
+                if (logger != null) {
+                    String s = (status.equals(
+                                SSLEngineResult.HandshakeStatus.NEED_WRAP))
+                        ? "NEED_WRAP"
+                        : (status.equals(
+                                SSLEngineResult.HandshakeStatus.NEED_UNWRAP))
+                            ? "NEED_UNWRAP"
+                            : "STATUS: OTHER!";
+                    logger.println("SSLSocketImpl: HS status: "+s+" "+status);
+                }
+                if (status.equals(SSLEngineResult.HandshakeStatus.NEED_WRAP)) {
+                    output.write(handshakeProtocol.wrap());
+                } else if (status.equals(
+                            SSLEngineResult.HandshakeStatus.NEED_UNWRAP)) {
+                    // read and unwrap the record contained in the transport
+                    // input stream (SSLStreamedInput), pass it
+                    // to appropriate client protocol (alert, handshake, or app)
+                    // and retrieve the type of unwrapped data
+                    switch (type = recordProtocol.unwrap()) {
+                        case ContentType.HANDSHAKE:
+                        case ContentType.CHANGE_CIPHER_SPEC:
+                            break;
+                        case ContentType.APPLICATION_DATA:
+                            // So it's rehandshake and
+                            // if app data buffer will be overloaded
+                            // it will throw alert exception.
+                            // Probably we should count the number of
+                            // not handshaking data and make additional
+                            // constraints (do not expect buffer overflow).
+                            break;
+                        case ContentType.ALERT:
+                            processAlert();
+                            if (socket_was_closed) {
+                                return;
+                            }
+                            break;
+                        default:
+                            // will throw exception
+                            reportFatalAlert(AlertProtocol.UNEXPECTED_MESSAGE,
+                                    new SSLException(
+                                        "Unexpected message of type "
+                                        + type + " has been got"));
+                    }
+                } else {
+                    // will throw exception
+                    reportFatalAlert(AlertProtocol.INTERNAL_ERROR,
+                        new SSLException(
+                            "Handshake passed unexpected status: "+status));
+                }
+                if (alertProtocol.hasAlert()) {
+                    // warning alert uccured during wrap or unwrap
+                    // (note: fatal alert causes AlertException
+                    // to be thrown)
+                    output.write(alertProtocol.wrap());
+                    alertProtocol.setProcessed();
+                }
+            }
+        } catch (EndOfSourceException e) {
+            appDataIS.setEnd();
+            throw new IOException("Connection was closed");
+        } catch (AlertException e) {
+            // will throw exception
+            reportFatalAlert(e.getDescriptionCode(), e.getReason());
+        }
+
+        session = recordProtocol.getSession();
+        if (listeners != null) {
+            // notify the listeners
+            HandshakeCompletedEvent event =
+                new HandshakeCompletedEvent(this, session);
+            int size = listeners.size();
+            for (int i=0; i<size; i++) {
+                ((HandshakeCompletedListener)listeners.get(i))
+                    .handshakeCompleted(event);
+            }
+        }
+    }
+        
+    /*
+     * Process received alert message
+     */
+    private void processAlert() throws IOException {
+        if (!alertProtocol.hasAlert()) {
+            return;
+        }
+        if (alertProtocol.isFatalAlert()) {
+            alertProtocol.setProcessed();
+            String description = "Fatal alert received "
+                + alertProtocol.getAlertDescription();
+            shutdown();
+            throw new SSLException(description);
+        } else {
+            if (logger != null) {
+                logger.println("Warning alert received: "
+                    + alertProtocol.getAlertDescription());
+            }
+            switch(alertProtocol.getDescriptionCode()) {
+                case AlertProtocol.CLOSE_NOTIFY:
+                    alertProtocol.setProcessed();
+                    appDataIS.setEnd();
+                    close();
+                    return;
+                default:
+                    alertProtocol.setProcessed();
+                // TODO: process other warning messages
+            }
+        }
+    }
+    
+    /*
+     * Sends fatal alert message and throws exception
+     */
+    private void reportFatalAlert(byte description_code, 
+            SSLException reason) throws IOException {
+        alertProtocol.alert(AlertProtocol.FATAL, description_code);
+        try {
+            // the output stream can be closed
+            output.write(alertProtocol.wrap());
+        } catch (IOException ex) { }
+        alertProtocol.setProcessed();
+        shutdown();
+        throw reason;
+    }
+}
+

Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketInputStream.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketInputStream.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketInputStream.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketInputStream.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,195 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.security.provider.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.net.ssl.SSLException;
+
+/**
+ * This class provides input data stream functionality
+ * for SSLSocket. It accumulates the application data
+ * received by SSL protocol.
+ */
+public final class SSLSocketInputStream
+        extends InputStream {
+
+    // The size of the internal data buffer.
+    // It should not be less than maximum data chunk enclosed
+    // in one ssl packet.
+    private int size = SSLRecordProtocol.MAX_DATA_LENGTH;
+
+    // Internal buffer accumulating the received application data
+    private byte[] buffer = new byte[size];
+
+    // position of the next byte to read from the buffer
+    private int pos;
+
+    // position of the last byte to read + 1
+    private int end;
+
+    // the ssl socket owning the stream
+    private final SSLSocketImpl owner;
+
+    // the flag indicating that the end of the (owner's) input stream
+    // has been reached
+    private boolean end_reached = false;
+
+    /**
+     * Creates the application data input stream for specified socket.
+     * @param   owner the socket which will provide this input stream
+     * to client applications.
+     */
+    protected SSLSocketInputStream(SSLSocketImpl owner) {
+        this.owner = owner;
+    }
+
+    // The helper delivering the applicatin data from the record layer
+    protected Adapter dataPoint = new Adapter();
+
+    /**
+     * Tells to the stream that the end of the income data has
+     * been reached.
+     */
+    protected void setEnd() {
+        end_reached = true;
+    }
+
+    // ------------------ InputStream implementetion -------------------
+
+    /**
+     * Returns the number of bytes available for reading without blocking.
+     * @return the number of available bytes.
+     * @throws  IOException
+     */
+    public int available() throws IOException {
+        return end - pos;
+    }
+
+    /**
+     * Closes the stream
+     * @throws  IOException
+     */
+    public void close() throws IOException {
+        buffer = null;
+    }
+
+    /**
+     * Reads one byte. If there is no data in the underlying buffer,
+     * this operation can block until the data will be
+     * available.
+     * @return read value.
+     * @throws  IOException
+     */
+    public int read() throws IOException {
+        if (buffer == null) {
+            throw new IOException("Stream was closed.");
+        }
+        while (pos == end) {
+            if (end_reached) {
+                return -1;
+            }
+            // If there is no data in the buffer
+            // - will block untill the data will be provided by
+            // record layer
+            owner.needAppData();
+        }
+        return buffer[pos++] & 0xFF;
+    }
+
+    /**
+     * Method acts as described in spec for superclass.
+     * @see java.io.InputStream#read(byte[])
+     */
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    /**
+     * Method acts as described in spec for superclass.
+     * @see java.io.InputStream#read(byte[],int,int)
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        int read_b;
+        int i = 0;
+        do {
+            if ((read_b = read()) == -1) {
+                return (i == 0) ? -1 : i;
+            }
+            b[off+i] = (byte) read_b;
+            i++;
+        } while ((available() != 0) && (i<len));
+        return i;
+    }
+
+    /**
+     * Method acts as described in spec for superclass.
+     * @see java.io.InputStream#skip(long)
+     */
+    public long skip(long n) throws IOException {
+        long i = 0;
+        int av = available();
+        if (av < n) {
+            n = av;
+        }
+        while ((i < n) && (read() != -1)) {
+            i++;
+        }
+        return i;
+    }
+
+    // The helper class devivering the application data from the record layer
+    // to this input stream.
+    // It 'adapts' the InputStream interface to Appendable, which is used for
+    // transmition of income data from the record protocol to its clients.
+    private class Adapter implements Appendable {
+        /**
+         * Appends the data to the stream.
+         * This method could be implemented in the outer class
+         * itself, but it could be insecure.
+         */
+        public void append(byte[] src) {
+            int length = src.length;
+            if (size - (end - pos) < length) {
+                // If the size of the buffer is greater than or equals to
+                // SSLRecordProtocol.MAX_DATA_LENGTH this situation will
+                // happen iff:
+                // 1. the length of received data fragment is greater
+                // than allowed by the spec
+                // 2. it is rehandhaking stage and we have got several
+                // extra app data messages.
+                // In any case it is better to throw alert exception.
+                throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+                        new SSLException("Could not accept income app data."));
+            }
+            if (end + length > size) {
+                // move the content of the buffer to the beginnig
+                System.arraycopy(buffer, pos, buffer, 0, end-pos);
+                end -= pos;
+                pos = 0;
+            }
+            System.arraycopy(src, 0, buffer, end, length);
+            end = end + length;
+        }
+    }
+}
+

Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketOutputStream.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketOutputStream.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketOutputStream.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketOutputStream.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,56 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.security.provider.jsse;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This is a application data output stream used in SSLSocket
+ * implementation.
+ * The written bytes are encrypted, packed into the records,
+ * and then sent to the peer host.
+ */
+public class SSLSocketOutputStream extends OutputStream {
+
+    private SSLSocketImpl owner;
+
+    protected SSLSocketOutputStream(SSLSocketImpl owner) {
+        this.owner = owner;
+    }
+
+    private byte[] bytik = new byte[1];
+
+    public void write(int b) throws IOException {
+        bytik[0] = (byte) (b & 0xFF);
+        owner.writeAppData(bytik, 0, 1);
+    }
+
+    public void write(byte[] b) throws IOException {
+        owner.writeAppData(b, 0, b.length);
+    }
+
+    public void write(byte[] b, int off, int len) throws IOException {
+        owner.writeAppData(b, off, len);
+    }
+
+}

Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketWrapper.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketWrapper.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketWrapper.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLSocketWrapper.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,195 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.security.provider.jsse;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+
+/**
+ * This class wraps the SSL fuctionality over existing conneted socket.
+ */
+public class SSLSocketWrapper extends SSLSocketImpl {
+
+    private final Socket socket;
+    private final boolean autoClose;
+
+    protected SSLSocketWrapper(Socket socket, boolean autoClose, SSLParameters sslParameters) throws IOException {
+        super(sslParameters);
+        if (!socket.isConnected()) {
+            throw new SocketException("Socket is not connected.");
+        }
+        this.socket = socket;
+        this.autoClose = autoClose;
+        init();
+    }
+
+    protected void initTransportLayer() throws IOException {
+        input = socket.getInputStream();
+        output = socket.getOutputStream();
+    }
+
+    protected void closeTransportLayer() throws IOException {
+        if (autoClose && (input != null)) {
+            socket.close();
+            input.close();
+            output.close();
+        }
+    }
+
+    // ------------------- Wrapping method implementations ---------------
+
+    public void connect(SocketAddress sockaddr, int timeout)
+        throws IOException {
+        throw new IOException("Underlying socket is already connected.");
+    }
+
+    public void connect(SocketAddress sockaddr) throws IOException {
+        throw new IOException("Underlying socket is already connected.");
+    }
+
+    public void bind(SocketAddress sockaddr) throws IOException {
+        throw new IOException("Underlying socket is already connected.");
+    }
+
+    public SocketAddress getRemoteSocketAddress() {
+        return socket.getRemoteSocketAddress();
+    }
+
+    public SocketAddress getLocalSocketAddress() {
+        return socket.getLocalSocketAddress();
+    }
+
+    public InetAddress getLocalAddress() {
+        return socket.getLocalAddress();
+    }
+
+    public InetAddress getInetAddress() {
+        return socket.getInetAddress();
+    }
+
+    public String toString() {
+        return "SSL socket over " + socket.toString();
+    }
+
+    public void setSoLinger(boolean on, int linger) throws SocketException {
+        socket.setSoLinger(on, linger);
+    }
+
+    public void setTcpNoDelay(boolean on) throws SocketException {
+        socket.setTcpNoDelay(on);
+    }
+
+    public void setReuseAddress(boolean on) throws SocketException {
+        socket.setReuseAddress(on);
+    }
+
+    public void setKeepAlive(boolean on) throws SocketException {
+        socket.setKeepAlive(on);
+    }
+
+    public void setTrafficClass(int tos) throws SocketException {
+        socket.setTrafficClass(tos);
+    }
+
+    public void setSoTimeout(int to) throws SocketException {
+        socket.setSoTimeout(to);
+    }
+
+    public void setSendBufferSize(int size) throws SocketException {
+        socket.setSendBufferSize(size);
+    }
+
+    public void setReceiveBufferSize(int size) throws SocketException {
+        socket.setReceiveBufferSize(size);
+    }
+
+    public boolean getTcpNoDelay() throws SocketException {
+        return socket.getTcpNoDelay();
+    }
+
+    public boolean getReuseAddress() throws SocketException {
+        return socket.getReuseAddress();
+    }
+
+    public boolean getOOBInline() throws SocketException {
+        return socket.getOOBInline();
+    }
+
+    public boolean getKeepAlive() throws SocketException {
+        return socket.getKeepAlive();
+    }
+
+    public int getTrafficClass() throws SocketException {
+        return socket.getTrafficClass();
+    }
+
+    public int getSoTimeout() throws SocketException {
+        return socket.getSoTimeout();
+    }
+
+    public int getSoLinger() throws SocketException {
+        return socket.getSoLinger();
+    }
+
+    public int getSendBufferSize() throws SocketException {
+        return socket.getSendBufferSize();
+    }
+
+    public int getReceiveBufferSize() throws SocketException {
+        return socket.getReceiveBufferSize();
+    }
+
+    public boolean isConnected() {
+        return socket.isConnected();
+    }
+
+    public boolean isClosed() {
+        return socket.isClosed();
+    }
+
+    public boolean isBound() {
+        return socket.isBound();
+    }
+
+    public boolean isOutputShutdown() {
+        return socket.isOutputShutdown();
+    }
+
+    public boolean isInputShutdown() {
+        return socket.isInputShutdown();
+    }
+
+    public int getPort() {
+        return socket.getPort();
+    }
+
+    public int getLocalPort() {
+        return socket.getLocalPort();
+    }
+
+    // -------------------------------------------------------------------
+
+}
+

Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLStreamedInput.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLStreamedInput.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLStreamedInput.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLStreamedInput.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.security.provider.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class acts like a filtered input stream: it takes
+ * the bytes from another InputStream.
+ */
+public class SSLStreamedInput extends SSLInputStream {
+
+    private InputStream in;
+
+    public SSLStreamedInput(InputStream in) {
+        this.in = in;
+    }
+
+    public int available() throws IOException {
+        return in.available();
+    }
+
+    /**
+     * Read an opaque value from the stream.
+     * @return the value read from the underlying stream.
+     * @throws IOException if the data could not be read from
+     * the underlying stream
+     * @throws EndOfSourceException if the end of the underlying
+     * stream has been reached.
+     */
+    public int read() throws IOException {
+        int res = in.read();
+        if (res < 0) {
+            throw new EndOfSourceException();
+        }
+        return res;
+    }
+}
+

Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLv3Constants.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLv3Constants.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLv3Constants.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/SSLv3Constants.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.security.provider.jsse;
+
+/**
+ * 
+ * Contains SSL 3.0 constants 
+ * @see SSL 3.0 Spec. 
+ * http://wp.netscape.com/eng/ssl3
+ */
+public class SSLv3Constants {
+
+    /**
+     * Client is a sender. Used in hash calculating for finished message.
+     * @see SSL 3.0 Spec., 5.6.9 Finished
+     */
+    static final byte[] client = new byte[] { 0x43, 0x4C, 0x4E, 0x54 };
+
+    /**
+     * Server is a sender. Used in hash calculating for finished message.
+     * @see SSL 3.0 Spec., 5.6.9 Finished
+     */
+    static final byte[] server = new byte[] { 0x53, 0x52, 0x56, 0x52 };
+
+    /**
+     * pad_1 for MD5
+     * @see SSL 3.0 Spec., 5.2.3.1 Null or standard stream cipher
+     */
+    static final byte[] MD5pad1 = new byte[] { 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 };
+
+    /**
+     * pad_1 for SHA
+     * @see SSL 3.0 Spec., 5.2.3.1 Null or standard stream cipher
+     */
+    static final byte[] SHApad1 = new byte[] { 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+            0x36, 0x36, 0x36 };
+
+    /**
+     * pad_2 for MD5
+     * @see SSL 3.0 Spec., 5.2.3.1 Null or standard stream cipher
+     */
+    static final byte[] MD5pad2 = new byte[] { 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C };
+
+    /**
+     * pad_2 for SHA
+     * @see SSL 3.0 Spec., 5.2.3.1 Null or standard stream cipher
+     */
+    static final byte[] SHApad2 = new byte[] { 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+            0x5C, 0x5C, 0x5C };
+}
\ No newline at end of file

Added: incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/ServerHandshakeImpl.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/ServerHandshakeImpl.java?rev=423275&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/ServerHandshakeImpl.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/x-net/src/main/java/org/apache/harmony/security/provider/jsse/ServerHandshakeImpl.java Tue Jul 18 15:50:12 2006
@@ -0,0 +1,723 @@
+/*
+ *  Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  Licensed 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.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.security.provider.jsse;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.AccessController;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PrivilegedExceptionAction;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Server side handshake protocol implementation.
+ * Handshake protocol operates on top of the Record Protocol.
+ * It responsible for negotiating a session.
+ * 
+ * The implementation proceses inbound client handshake messages,
+ * creates and sends respond messages. Outbound messages are supplied 
+ * to Record Protocol. Detected errors are reported to the Alert protocol.
+ * 
+ * @see TLS 1.0 spec., 7.4. Handshake protocol.
+ * (http://www.ietf.org/rfc/rfc2246.txt)
+ *  
+ */
+public class ServerHandshakeImpl extends HandshakeProtocol {
+
+    // private key used in key exchange
+    private PrivateKey privKey;
+
+    /**
+     * Creates Server Handshake Implementation
+     * 
+     * @param owner
+     */
+    public ServerHandshakeImpl(Object owner) {
+        super(owner);
+        status = NEED_UNWRAP;
+    }
+
+    /**
+     * Start session negotiation
+     * @param session
+     */
+    public void start() {
+        if (session == null) { // initial handshake
+            status = NEED_UNWRAP;
+            return; // wait client hello
+        }
+        if (clientHello != null && this.status != FINISHED) {
+            // current negotiation has not completed
+            return; // ignore
+        }
+        
+        // renegotiation
+        sendHelloRequest();
+        status = NEED_UNWRAP;
+    }
+
+    /**
+     * Proceses inbound handshake messages
+     * @param bytes
+     */
+    public void unwrap(byte[] bytes) {
+
+        io_stream.append(bytes);
+        while (io_stream.available() > 0) {
+            int handshakeType;
+            int length;
+            io_stream.mark();
+            try {
+                handshakeType = io_stream.read();
+                length = io_stream.readUint24();
+                if (io_stream.available() < length) {
+                    io_stream.reset();
+                    return;
+                }
+
+                switch (handshakeType) {
+                case 1: // CLIENT_HELLO
+                    if (clientHello != null && this.status != FINISHED) {
+                            // Client hello has been received during handshake
+                            unexpectedMessage();
+                            return;
+                    }
+                    // if protocol planed to send Hello Request message
+                    // - cancel this demand.
+                    needSendHelloRequest = false;
+                    clientHello = new ClientHello(io_stream, length);
+                    if (nonBlocking) {
+                        delegatedTasks.add(new DelegatedTask(
+                                new PrivilegedExceptionAction(){ 
+                            public Object run() throws Exception {
+                                processClientHello();
+                                return null;
+                                }
+                            },
+                            this,
+                            AccessController.getContext()));
+                        return;
+                    }
+                    processClientHello();
+                    break;
+
+                case 11: //    CLIENT CERTIFICATE
+                    if (isResuming || certificateRequest == null
+                            || serverHelloDone == null || clientCert != null) {
+                        unexpectedMessage();
+                        return;
+                    }
+                    clientCert = new CertificateMessage(io_stream, length);
+                    if (clientCert.certs.length == 0) {
+                        if (parameters.getNeedClientAuth()) {
+                            fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+                                    "HANDSHAKE FAILURE: no client certificate recived");
+                        }
+                    } else {
+                        String authType = clientCert.certs[0].getPublicKey()
+                                .getAlgorithm();
+                        try {
+                            parameters.getTrustManager().checkClientTrusted(
+                                    clientCert.certs, authType);
+                        } catch (CertificateException e) {
+                            fatalAlert(AlertProtocol.BAD_CERTIFICATE,
+                                    "Untrusted Client Certificate ", e);
+                        }
+                        session.peerCertificates = clientCert.certs;
+                    }
+                    break;
+
+                case 15: // CERTIFICATE_VERIFY
+                    if (isResuming
+                            || clientKeyExchange == null
+                            || clientCert == null
+                            || clientKeyExchange.isEmpty() //client certificate
+                                                           // contains fixed DH
+                                                           // parameters
+                            || certificateVerify != null
+                            || changeCipherSpecReceived) {
+                        unexpectedMessage();
+                        return;
+                    }
+                    certificateVerify = new CertificateVerify(io_stream, length);
+
+                    DigitalSignature ds = new DigitalSignature(session.cipherSuite.keyExchange);
+                    byte[] md5_hash = null;
+                    byte[] sha_hash = null;
+                    PublicKey pk = serverCert.certs[0].getPublicKey();
+                    if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+                        int l;
+                        try {
+                            l = getRSAKeyLength(pk);
+                        } catch (Exception e) {
+                            fatalAlert(AlertProtocol.INTERNAL_ERROR,
+                                    "INTERNAL ERROR", e);
+                            return;
+                        }
+                        if (l > 512) { // key is longer than 512 bits
+                            md5_hash = io_stream.getDigestMD5();
+                            sha_hash = io_stream.getDigestSHA();
+                        }
+                    } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
+                        md5_hash = io_stream.getDigestMD5();
+                        sha_hash = io_stream.getDigestSHA();
+                    } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT) {
+                        sha_hash = io_stream.getDigestSHA();
+                    } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon
+                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+                    }
+                    ds.setMD5(md5_hash);
+                    ds.setSHA(sha_hash);
+                    if (!ds.verifySignature(certificateVerify.signedHash)) {
+                        fatalAlert(AlertProtocol.DECRYPT_ERROR,
+                                "DECRYPT ERROR: CERTIFICATE_VERIFY incorrect signature");
+                    }
+                    break;
+                case 16: // CLIENT_KEY_EXCHANGE
+                    if (isResuming
+                            || serverHelloDone == null
+                            || clientKeyExchange != null
+                            || (clientCert == null && parameters
+                                    .getNeedClientAuth())) {
+                        unexpectedMessage();
+                        return;
+                    }
+                    if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA
+                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+                        clientKeyExchange = new ClientKeyExchange(io_stream,
+                                length, serverHello.server_version[1] == 1,
+                                true);
+                        Cipher c = null;
+                        try {
+                            c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+                            c.init(Cipher.DECRYPT_MODE, privKey);
+                            preMasterSecret = c
+                                    .doFinal(clientKeyExchange.exchange_keys);
+                            // check preMasterSecret:
+                            if (preMasterSecret.length != 48
+                                    || preMasterSecret[0] != clientHello.client_version[0]
+                                    || preMasterSecret[1] != clientHello.client_version[1]) {
+                                // incorrect preMasterSecret
+                                // prevent an attack (see TLS 1.0 spec., 7.4.7.1.)
+                                preMasterSecret = new byte[48];
+                                parameters.getSecureRandom().nextBytes(
+                                        preMasterSecret);
+                            }
+                        } catch (Exception e) {
+                            fatalAlert(AlertProtocol.INTERNAL_ERROR,
+                                    "INTERNAL ERROR", e);
+                        }
+                    } else { // diffie hellman key exchange
+                        clientKeyExchange = new ClientKeyExchange(io_stream,
+                                length, serverHello.server_version[1] == 1,
+                                false);
+                        if (clientKeyExchange.isEmpty()) {
+                            // TODO check that client cert. DH params
+                            // matched server cert. DH params
+
+                            // client cert. contains fixed DH parameters 
+                            preMasterSecret = ((DHPublicKey) clientCert.certs[0]
+                                    .getPublicKey()).getY().toByteArray();
+                        } else {
+                            PublicKey clientPublic;
+                            KeyAgreement agreement;
+                            try {
+                                KeyFactory kf = null;
+                                try {
+                                    kf = KeyFactory.getInstance("DH");
+                                } catch (NoSuchAlgorithmException ee) {
+                                    kf = KeyFactory
+                                            .getInstance("DiffieHellman");
+                                }
+                                try {
+                                    agreement = KeyAgreement.getInstance("DH");
+                                } catch (NoSuchAlgorithmException ee) {
+                                    agreement = KeyAgreement
+                                            .getInstance("DiffieHellman");
+                                }
+                                clientPublic = kf
+                                        .generatePublic(new DHPublicKeySpec(
+                                                new BigInteger(
+                                                        1,
+                                                        clientKeyExchange.exchange_keys),
+                                                serverKeyExchange.par1,
+                                                serverKeyExchange.par2));
+                                agreement.init(privKey);
+                                agreement.doPhase(clientPublic, true);
+                                preMasterSecret = agreement.generateSecret();
+                            } catch (Exception e) {
+                                fatalAlert(AlertProtocol.INTERNAL_ERROR,
+                                        "INTERNAL ERROR", e);
+                                return;
+                            }
+                        }
+                    }
+
+                    computerMasterSecret();
+                    break;
+
+                case 20: // FINISHED
+                    if (!isResuming && !changeCipherSpecReceived) {
+                        unexpectedMessage();
+                        return;
+                    }
+
+                    clientFinished = new Finished(io_stream, length);
+                    verifyFinished(clientFinished.getData());
+                    parameters.getServerSessionContext().putSession(session);
+                    if (!isResuming) {
+                        sendChangeCipherSpec();
+                    } else {
+                        session.lastAccessedTime = System.currentTimeMillis();
+                        status = FINISHED;
+                    }
+                    break;
+                default:
+                    unexpectedMessage();
+                    return;
+                }
+            } catch (IOException e) {
+                // io stream dosn't contain complete handshake message
+                io_stream.reset();
+                return;
+            }
+        }
+    }
+    /**
+     * Processes SSLv2 Hello message
+     * @ see TLS 1.0 spec., E.1. Version 2 client hello
+     * @param bytes
+     */
+    public void unwrapSSLv2(byte[] bytes) {
+        try {
+            io_stream.append(bytes);
+            io_stream.mark();
+            try {
+                clientHello = new ClientHello(io_stream);
+            } catch (IOException e) {
+                io_stream.reset();
+                return;
+            }
+            if (nonBlocking) {
+                delegatedTasks.add(new DelegatedTask(
+                        new PrivilegedExceptionAction(){ 
+                    public Object run() throws Exception {
+                        processClientHello();
+                        return null;
+                        }
+                    },
+                    this,
+                    AccessController.getContext()));
+                return;
+            }
+            processClientHello();
+        } catch (Exception e) {
+            fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+        }
+    }
+
+    /**
+     * 
+     * Processes Client Hello message. 
+     * Server responds to client hello message with server hello 
+     * and (if necessary) server certificate, server key exchange,
+     * certificate request, and server hello done messages.
+     */
+    void processClientHello() {
+        CipherSuite cipher_suite;
+
+        // check that clientHello contains CompressionMethod.null
+        checkCompression: {
+            for (int i = 0; i < clientHello.compression_methods.length; i++) {
+                if (clientHello.compression_methods[i] == 0) {
+                    break checkCompression;
+                }
+            }
+            fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+                    "HANDSHAKE FAILURE. Incorrect client hello message");
+        }
+        
+        if (!ProtocolVersion.isSupported(clientHello.client_version)) {
+            fatalAlert(AlertProtocol.PROTOCOL_VERSION, 
+                    "PROTOCOL VERSION. Unsupported client version "
+                    + clientHello.client_version[0]
+                    + clientHello.client_version[1]);        
+        }
+
+        isResuming = false;
+        FIND: if (clientHello.session_id.length != 0) {
+            // client wishes to reuse session
+
+            SSLSessionImpl sessionToResume;
+            boolean reuseCurrent = false;
+
+            // reuse current session
+            if (session != null
+                    && Arrays.equals(session.id, clientHello.session_id)) {
+                if (session.isValid()) {
+                    isResuming = true;
+                    break FIND;
+                }
+                reuseCurrent = true;
+            }
+            
+            // find session in cash
+            sessionToResume = findSessionToResume(clientHello.session_id);
+            if (sessionToResume == null || !sessionToResume.isValid()) {
+                if (!parameters.getEnableSessionCreation()) {
+                    if (reuseCurrent) {
+                        // we can continue current session
+                        sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
+                        status = NOT_HANDSHAKING;
+                        clearMessages();
+                        return;
+                    } else {
+                        fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+                                "SSL Session may not be created");
+                    }
+                }
+                session = null;
+            } else {
+                session = (SSLSessionImpl)sessionToResume.clone();
+                isResuming = true;
+            }
+        }
+
+        if (isResuming) {
+            cipher_suite = session.cipherSuite;
+            // clientHello.cipher_suites must include at least cipher_suite from the session
+            checkCipherSuite: {
+                for (int i = 0; i < clientHello.cipher_suites.length; i++) {
+                    if (cipher_suite.equals(clientHello.cipher_suites[i])) {
+                        break checkCipherSuite;
+                    }
+                }
+                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+                        "HANDSHAKE FAILURE. Incorrect client hello message");
+            }
+        } else {
+            cipher_suite = selectSuite(clientHello.cipher_suites);
+            if (cipher_suite == null) {
+                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "HANDSHAKE FAILURE. NO COMMON SUITE");
+            }
+            if (!parameters.getEnableSessionCreation()) {
+                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+                        "SSL Session may not be created");
+            }
+            session = new SSLSessionImpl(cipher_suite, parameters
+                    .getSecureRandom());
+        }
+
+        recordProtocol.setVersion(clientHello.client_version);
+        session.protocol = ProtocolVersion.getByVersion(clientHello.client_version);
+        session.clientRandom = clientHello.random;
+        
+        // create server hello message
+        serverHello = new ServerHello(parameters.getSecureRandom(), 
+                clientHello.client_version,
+                session.getId(), cipher_suite, (byte) 0); //CompressionMethod.null
+        session.serverRandom = serverHello.random;
+        send(serverHello);
+        if (isResuming) {
+            sendChangeCipherSpec();
+            return;
+        }
+        
+        //    create and send server certificate message if needed
+        if (!cipher_suite.isAnonymous()) { // need to send server certificate
+            X509Certificate[] certs = null;
+            String certType = null;
+            if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
+                certType = "RSA";
+            } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT) {
+                certType = "DSA";
+            } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_DSS) {
+                certType = "DH_DSA";
+            } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_RSA) {
+                certType = "DH_RSA";
+            }
+            // obtain certificates from key manager
+            String alias = null;
+            X509KeyManager km = parameters.getKeyManager();
+            if (km instanceof X509ExtendedKeyManager) {
+                X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
+                if (this.socketOwner != null) {
+                    alias = ekm.chooseServerAlias(certType, null,
+                            this.socketOwner);
+                } else {
+                    alias = ekm.chooseEngineServerAlias(certType, null,
+                            this.engineOwner);
+                }
+                if (alias != null) {
+                    certs = ekm.getCertificateChain(alias);
+                }
+            } else {
+                alias = km.chooseServerAlias(certType, null, this.socketOwner);
+                if (alias != null) {
+                    certs = km.getCertificateChain(alias);
+                }
+            }
+
+            if (certs == null) {
+                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "NO SERVER CERTIFICATE FOUND");
+                return;
+            }
+            session.localCertificates = certs;
+            serverCert = new CertificateMessage(certs);
+            privKey = parameters.getKeyManager().getPrivateKey(alias);
+            send(serverCert);
+        }
+
+        // create and send server key exchange message if needed
+        RSAPublicKey rsakey = null;
+        DHPublicKeySpec dhkeySpec = null;
+        byte[] hash = null;
+        BigInteger p = null;
+        BigInteger g = null;
+
+        KeyPairGenerator kpg = null;
+
+        try {
+            if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+                PublicKey pk = serverCert.certs[0].getPublicKey();                
+                if (getRSAKeyLength(pk) > 512) {
+                    // key is longer than 512 bits
+                    kpg = KeyPairGenerator.getInstance("RSA");
+                    kpg.initialize(512);
+                }
+            } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_anon
+                    || cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+                try {
+                    kpg = KeyPairGenerator.getInstance("DH");
+                } catch (NoSuchAlgorithmException ee) {
+                    kpg = KeyPairGenerator.getInstance("DiffieHellman");
+                }
+                p = new BigInteger(1, DHParameters.getPrime());
+                g = new BigInteger("2");
+                DHParameterSpec spec = new DHParameterSpec(p, g);
+                kpg.initialize(spec);
+            }
+        } catch (Exception e) {
+            fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+        }
+
+        if (kpg != null) {
+            // need to send server key exchange message
+            DigitalSignature ds = new DigitalSignature(cipher_suite.keyExchange);
+            KeyPair kp = null;
+            try {
+                kp = kpg.genKeyPair();
+                if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+                    rsakey = (RSAPublicKey) kp.getPublic();
+                } else {
+                    DHPublicKey dhkey = (DHPublicKey) kp.getPublic();
+                    KeyFactory kf = null;
+                    try {
+                        kf = KeyFactory.getInstance("DH");
+                    } catch (NoSuchAlgorithmException e) {
+                            kf = KeyFactory.getInstance("DiffieHellman");
+                    }
+                    dhkeySpec = (DHPublicKeySpec) kf.getKeySpec(dhkey,
+                            DHPublicKeySpec.class);
+                }
+                if (!cipher_suite.isAnonymous()) { // calculate signed_params
+            
+                    // init by private key which correspond to
+                    // server certificate
+                    ds.init(privKey);
+                    
+                    // use emphemeral key for key exchange
+                    privKey = kp.getPrivate();
+                    ds.update(clientHello.getRandom());
+                    ds.update(serverHello.getRandom());
+
+                    byte[] tmp;
+                    byte[] tmpLength = new byte[2];
+//FIXME 1_byte==0x00                    
+                    if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+                        tmp = rsakey.getModulus().toByteArray();
+                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+                        tmpLength[1] = (byte) (tmp.length & 0xFF);
+                        ds.update(tmpLength);
+                        ds.update(tmp);
+                        tmp = rsakey.getPublicExponent().toByteArray();
+                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+                        tmpLength[1] = (byte) (tmp.length & 0xFF);
+                        ds.update(tmp);
+                    } else {
+                        tmp = dhkeySpec.getP().toByteArray();
+                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+                        tmpLength[1] = (byte) (tmp.length & 0xFF);
+                        ds.update(tmp);
+                        tmp = dhkeySpec.getG().toByteArray();
+                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+                        tmpLength[1] = (byte) (tmp.length & 0xFF);
+                        ds.update(tmp);
+                        tmp = dhkeySpec.getY().toByteArray();
+                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+                        tmpLength[1] = (byte) (tmp.length & 0xFF);
+                        ds.update(tmp);
+                    }
+                    hash = ds.sign();
+                } else {
+                    privKey = kp.getPrivate(); // use emphemeral key for key exchange
+                }
+            } catch (Exception e) {
+                fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+            }
+
+            if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+                serverKeyExchange = new ServerKeyExchange(rsakey.getModulus(),
+                        rsakey.getPublicExponent(), null, hash);
+            } else {
+                serverKeyExchange = new ServerKeyExchange(p,
+                        g, dhkeySpec.getY(), hash);
+            }
+            send(serverKeyExchange);
+        }
+
+        // CERTIFICATE_REQUEST
+        certRequest: if (parameters.getWantClientAuth()
+                || parameters.getNeedClientAuth()) {
+            X509Certificate[] accepted;
+            try {
+                X509TrustManager tm = parameters.getTrustManager();
+                accepted = tm.getAcceptedIssuers();
+            } catch (ClassCastException e) {
+                // don't send certificateRequest
+                break certRequest;
+            }
+            byte[] requestedClientCertTypes = {1, 2}; // rsa sign, dsa sign
+            certificateRequest = new CertificateRequest(
+                    requestedClientCertTypes, accepted);
+            send(certificateRequest);
+        }
+
+        // SERVER_HELLO_DONE
+        serverHelloDone = new ServerHelloDone();
+        send(serverHelloDone);
+        status = NEED_UNWRAP;
+    }
+    
+    /**
+     * Creates and sends finished message
+     */
+    protected void makeFinished() {
+        byte[] verify_data;
+        boolean isTLS = (serverHello.server_version[1] == 1); // TLS 1.0 protocol
+        if (isTLS) {
+            verify_data = new byte[12];
+            computerVerifyDataTLS("server finished", verify_data);
+        } else { // SSL 3.0 protocol (http://wp.netscape.com/eng/ssl3)
+            verify_data = new byte[36];
+            computerVerifyDataSSLv3(SSLv3Constants.server, verify_data);
+        }
+        serverFinished = new Finished(verify_data);
+        send(serverFinished);
+        if (isResuming) {
+            if (isTLS) {
+                computerReferenceVerifyDataTLS("client finished");
+            } else {
+                computerReferenceVerifyDataSSLv3(SSLv3Constants.client);                
+            }
+            status = NEED_UNWRAP;
+        } else {
+            session.lastAccessedTime = System.currentTimeMillis();
+            status = FINISHED;
+        }
+    }
+
+    // find sesssion in the session hash
+    private SSLSessionImpl findSessionToResume(byte[] session_id) {
+        return (SSLSessionImpl)parameters.getServerSessionContext().getSession(session_id);
+    }
+    
+    // find appropriate cipher_suite in the client suites
+    private CipherSuite selectSuite(CipherSuite[] client_suites) {
+        for (int i = 0; i < client_suites.length; i++) {
+            if (!client_suites[i].supported) {
+                continue;
+            }
+            for (int j = 0; j < parameters.enabledCipherSuites.length; j++) {
+                if (client_suites[i].equals(parameters.enabledCipherSuites[j])) {
+                    return client_suites[i];
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Proceses inbound ChangeCipherSpec message
+     */
+    public void receiveChangeCipherSpec() {    
+        if (isResuming) {
+            if (serverFinished == null) {
+                unexpectedMessage();
+            } else {
+                changeCipherSpecReceived = true;
+            }
+        } else {
+            if ((parameters.getNeedClientAuth() && clientCert == null)
+                    || clientKeyExchange == null
+                    || (clientKeyExchange.isEmpty() && certificateVerify == null)) {
+                unexpectedMessage();
+            } else {
+                changeCipherSpecReceived = true;
+            }
+            if (serverHello.server_version[1] == 1) {
+                computerReferenceVerifyDataTLS("client finished");
+            } else {
+                computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
+            }
+        }
+    }
+
+}



Mime
View raw message