FileDocCategorySizeDatePackage
ServerHandshakeImpl.javaAPI DocAndroid 1.5 API31727Wed May 06 22:41:06 BST 2009org.apache.harmony.xnet.provider.jsse

ServerHandshakeImpl

public class ServerHandshakeImpl extends HandshakeProtocol
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.

Fields Summary
private PrivateKey
privKey
Constructors Summary
public ServerHandshakeImpl(Object owner)
Creates Server Handshake Implementation

param
owner

        super(owner);
        status = NEED_UNWRAP;
    
Methods Summary
private org.apache.harmony.xnet.provider.jsse.SSLSessionImplfindSessionToResume(byte[] session_id)

        return (SSLSessionImpl)parameters.getServerSessionContext().getSession(session_id);
    
protected voidmakeFinished()
Creates and sends finished message

        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;
        }
    
voidprocessClientHello()
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.

        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;
                // BEGIN android-removed
                // if (this.socketOwner != null) {
                //     alias = ekm.chooseServerAlias(certType, null,
                //             this.socketOwner);
                // } else {
                // END android-removed
                    alias = ekm.chooseEngineServerAlias(certType, null,
                            this.engineOwner);
                // BEGIN android-removed
                // }
                // END android-removed
                if (alias != null) {
                    certs = ekm.getCertificateChain(alias);
                }
            } else {
                // BEGIN android-removed
                // alias = km.chooseServerAlias(certType, null, this.socketOwner);
                // if (alias != null) {
                // END android-removed
                    certs = km.getCertificateChain(alias);
                // BEGIN android-removed
                // }
                // END android-removed
            }

            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;
    
public voidreceiveChangeCipherSpec()
Proceses inbound ChangeCipherSpec message

    
        if (isResuming) {
            if (serverFinished == null) {
                unexpectedMessage();
            } else {
                changeCipherSpecReceived = true;
            }
        } else {
            if ((parameters.getNeedClientAuth() && clientCert == null)
                    || clientKeyExchange == null
                    || (clientCert != null && !clientKeyExchange.isEmpty() && certificateVerify == null)) {
                unexpectedMessage();
            } else {
                changeCipherSpecReceived = true;
            }
            if (serverHello.server_version[1] == 1) {
                computerReferenceVerifyDataTLS("client finished");
            } else {
                computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
            }
        }
    
private CipherSuiteselectSuite(CipherSuite[] client_suites)

        for (int i = 0; i < client_suites.length; i++) {
            if (!client_suites[i].supported) {
                continue;
            }
            // BEGIN android-changed
            for (int j = 0; j < parameters.getEnabledCipherSuitesMember().length; j++) {
                if (client_suites[i].equals(parameters.getEnabledCipherSuitesMember()[j])) {
                    return client_suites[i];
                }
            }
            // END android-changed
        }
        return null;
    
public voidstart()
Start session negotiation

        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;
    
public voidunwrap(byte[] bytes)
Proceses inbound handshake messages

param
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);
                    ds.init(serverCert.certs[0]);                 
                    byte[] md5_hash = null;
                    byte[] sha_hash = null;

                    if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT
                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA
                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
                        md5_hash = io_stream.getDigestMD5withoutLast();
                        sha_hash = io_stream.getDigestSHAwithoutLast();
                    } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
                            || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT) {
                        sha_hash = io_stream.getDigestSHAwithoutLast();
                    } 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());
                    // BEGIN android-added
                    session.context = parameters.getServerSessionContext();
                    // END android-added
                    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;
            }
        }
    
public voidunwrapSSLv2(byte[] bytes)
Processes SSLv2 Hello message

see TLS 1.0 spec., E.1. Version 2 client hello
param
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);
        }