ServerHandshakeImpl.java revision 7c935d4e4ca990334200cf5eb4fbcfac718c6b45
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.xnet.provider.jsse;
19
20import java.io.IOException;
21import java.math.BigInteger;
22import java.security.AccessController;
23import java.security.KeyFactory;
24import java.security.KeyPair;
25import java.security.KeyPairGenerator;
26import java.security.NoSuchAlgorithmException;
27import java.security.PrivateKey;
28import java.security.PrivilegedExceptionAction;
29import java.security.PublicKey;
30import java.security.cert.CertificateException;
31import java.security.cert.X509Certificate;
32import java.security.interfaces.RSAPublicKey;
33import java.util.Arrays;
34import javax.crypto.Cipher;
35import javax.crypto.KeyAgreement;
36import javax.crypto.interfaces.DHPublicKey;
37import javax.crypto.spec.DHParameterSpec;
38import javax.crypto.spec.DHPublicKeySpec;
39import javax.net.ssl.X509ExtendedKeyManager;
40import javax.net.ssl.X509KeyManager;
41import javax.net.ssl.X509TrustManager;
42
43/**
44 * Server side handshake protocol implementation.
45 * Handshake protocol operates on top of the Record Protocol.
46 * It responsible for negotiating a session.
47 *
48 * The implementation processes inbound client handshake messages,
49 * creates and sends respond messages. Outbound messages are supplied
50 * to Record Protocol. Detected errors are reported to the Alert protocol.
51 *
52 * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.
53 * Handshake protocol.</a>
54 *
55 */
56public class ServerHandshakeImpl extends HandshakeProtocol {
57
58    // private key used in key exchange
59    private PrivateKey privKey;
60
61    /**
62     * Creates Server Handshake Implementation
63     *
64     * @param owner
65     */
66    public ServerHandshakeImpl(Object owner) {
67        super(owner);
68        status = NEED_UNWRAP;
69    }
70
71    /**
72     * Start session negotiation
73     */
74    @Override
75    public void start() {
76        if (session == null) { // initial handshake
77            status = NEED_UNWRAP;
78            return; // wait client hello
79        }
80        if (clientHello != null && this.status != FINISHED) {
81            // current negotiation has not completed
82            return; // ignore
83        }
84
85        // renegotiation
86        sendHelloRequest();
87        status = NEED_UNWRAP;
88    }
89
90    /**
91     * Proceses inbound handshake messages
92     * @param bytes
93     */
94    @Override
95    public void unwrap(byte[] bytes) {
96
97        io_stream.append(bytes);
98        while (io_stream.available() > 0) {
99            int handshakeType;
100            int length;
101            io_stream.mark();
102            try {
103                handshakeType = io_stream.read();
104                length = io_stream.readUint24();
105                if (io_stream.available() < length) {
106                    io_stream.reset();
107                    return;
108                }
109
110                switch (handshakeType) {
111                case 1: // CLIENT_HELLO
112                    if (clientHello != null && this.status != FINISHED) {
113                            // Client hello has been received during handshake
114                            unexpectedMessage();
115                            return;
116                    }
117                    // if protocol planed to send Hello Request message
118                    // - cancel this demand.
119                    needSendHelloRequest = false;
120                    clientHello = new ClientHello(io_stream, length);
121                    if (nonBlocking) {
122                        delegatedTasks.add(new DelegatedTask(new Runnable() {
123                            public void run() {
124                                processClientHello();
125                            }
126                        }, this));
127                        return;
128                    }
129                    processClientHello();
130                    break;
131
132                case 11: //    CLIENT CERTIFICATE
133                    if (isResuming || certificateRequest == null
134                            || serverHelloDone == null || clientCert != null) {
135                        unexpectedMessage();
136                        return;
137                    }
138                    clientCert = new CertificateMessage(io_stream, length);
139                    if (clientCert.certs.length == 0) {
140                        if (parameters.getNeedClientAuth()) {
141                            fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
142                                       "HANDSHAKE FAILURE: no client certificate received");
143                        }
144                    } else {
145                        String authType = clientCert.getAuthType();
146                        try {
147                            parameters.getTrustManager().checkClientTrusted(
148                                    clientCert.certs, authType);
149                        } catch (CertificateException e) {
150                            fatalAlert(AlertProtocol.BAD_CERTIFICATE,
151                                       "Untrusted Client Certificate ", e);
152                        }
153                        session.peerCertificates = clientCert.certs;
154                    }
155                    break;
156
157                case 15: // CERTIFICATE_VERIFY
158                    if (isResuming
159                            || clientKeyExchange == null
160                            || clientCert == null
161                            || clientKeyExchange.isEmpty() //client certificate
162                                                           // contains fixed DH
163                                                           // parameters
164                            || certificateVerify != null
165                            || changeCipherSpecReceived) {
166                        unexpectedMessage();
167                        return;
168                    }
169                    certificateVerify = new CertificateVerify(io_stream, length);
170
171                    String authType = clientCert.getAuthType();
172                    DigitalSignature ds = new DigitalSignature(authType);
173                    ds.init(clientCert.certs[0]);
174                    byte[] md5_hash = null;
175                    byte[] sha_hash = null;
176
177                    if ("RSA".equals(authType)) {
178                        md5_hash = io_stream.getDigestMD5withoutLast();
179                        sha_hash = io_stream.getDigestSHAwithoutLast();
180                    } else if ("DSA".equals(authType)) {
181                        sha_hash = io_stream.getDigestSHAwithoutLast();
182                    // The Signature should be empty in case of anonymous signature algorithm:
183                    // } else if ("DH".equals(authType)) {
184                    }
185                    ds.setMD5(md5_hash);
186                    ds.setSHA(sha_hash);
187                    if (!ds.verifySignature(certificateVerify.signedHash)) {
188                        fatalAlert(AlertProtocol.DECRYPT_ERROR,
189                                   "DECRYPT ERROR: CERTIFICATE_VERIFY incorrect signature");
190                    }
191                    break;
192                case 16: // CLIENT_KEY_EXCHANGE
193                    if (isResuming
194                            || serverHelloDone == null
195                            || clientKeyExchange != null
196                            || (clientCert == null && parameters.getNeedClientAuth())) {
197                        unexpectedMessage();
198                        return;
199                    }
200                    if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA
201                            || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
202                        clientKeyExchange = new ClientKeyExchange(io_stream,
203                                length, serverHello.server_version[1] == 1,
204                                true);
205                        Cipher c = null;
206                        try {
207                            c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
208                            c.init(Cipher.DECRYPT_MODE, privKey);
209                            preMasterSecret = c.doFinal(clientKeyExchange.exchange_keys);
210                            // check preMasterSecret:
211                            if (preMasterSecret.length != 48
212                                    || preMasterSecret[0] != clientHello.client_version[0]
213                                    || preMasterSecret[1] != clientHello.client_version[1]) {
214                                // incorrect preMasterSecret
215                                // prevent an attack (see TLS 1.0 spec., 7.4.7.1.)
216                                preMasterSecret = new byte[48];
217                                parameters.getSecureRandom().nextBytes(
218                                        preMasterSecret);
219                            }
220                        } catch (Exception e) {
221                            fatalAlert(AlertProtocol.INTERNAL_ERROR,
222                                       "INTERNAL ERROR", e);
223                        }
224                    } else { // diffie hellman key exchange
225                        clientKeyExchange = new ClientKeyExchange(io_stream,
226                                length, serverHello.server_version[1] == 1,
227                                false);
228                        if (clientKeyExchange.isEmpty()) {
229                            // TODO check that client cert. DH params
230                            // matched server cert. DH params
231
232                            // client cert. contains fixed DH parameters
233                            preMasterSecret = ((DHPublicKey) clientCert.certs[0].getPublicKey()).getY().toByteArray();
234                        } else {
235                            try {
236                                KeyFactory kf = KeyFactory.getInstance("DH");
237                                KeyAgreement agreement = KeyAgreement.getInstance("DH");
238                                PublicKey clientPublic = kf.generatePublic(new DHPublicKeySpec(
239                                                new BigInteger(
240                                                        1,
241                                                        clientKeyExchange.exchange_keys),
242                                                serverKeyExchange.par1,
243                                                serverKeyExchange.par2));
244                                agreement.init(privKey);
245                                agreement.doPhase(clientPublic, true);
246                                preMasterSecret = agreement.generateSecret();
247                            } catch (Exception e) {
248                                fatalAlert(AlertProtocol.INTERNAL_ERROR,
249                                           "INTERNAL ERROR", e);
250                                return;
251                            }
252                        }
253                    }
254
255                    computerMasterSecret();
256                    break;
257
258                case 20: // FINISHED
259                    if (!isResuming && !changeCipherSpecReceived) {
260                        unexpectedMessage();
261                        return;
262                    }
263
264                    clientFinished = new Finished(io_stream, length);
265                    verifyFinished(clientFinished.getData());
266                    session.context = parameters.getServerSessionContext();
267                    parameters.getServerSessionContext().putSession(session);
268                    if (!isResuming) {
269                        sendChangeCipherSpec();
270                    } else {
271                        session.lastAccessedTime = System.currentTimeMillis();
272                        status = FINISHED;
273                    }
274                    break;
275                default:
276                    unexpectedMessage();
277                    return;
278                }
279            } catch (IOException e) {
280                // io stream dosn't contain complete handshake message
281                io_stream.reset();
282                return;
283            }
284        }
285    }
286    /**
287     * Processes SSLv2 Hello message
288     * @ see TLS 1.0 spec., E.1. Version 2 client hello
289     * @param bytes
290     */
291    @Override
292    public void unwrapSSLv2(byte[] bytes) {
293        io_stream.append(bytes);
294        io_stream.mark();
295        try {
296            clientHello = new ClientHello(io_stream);
297        } catch (IOException e) {
298            io_stream.reset();
299            return;
300        }
301        if (nonBlocking) {
302            delegatedTasks.add(new DelegatedTask(new Runnable() {
303                public void run() {
304                    processClientHello();
305                }
306            }, this));
307            return;
308        }
309        processClientHello();
310    }
311
312    /**
313     *
314     * Processes Client Hello message.
315     * Server responds to client hello message with server hello
316     * and (if necessary) server certificate, server key exchange,
317     * certificate request, and server hello done messages.
318     */
319    void processClientHello() {
320        CipherSuite cipher_suite;
321
322        // check that clientHello contains CompressionMethod.null
323        checkCompression: {
324            for (int i = 0; i < clientHello.compression_methods.length; i++) {
325                if (clientHello.compression_methods[i] == 0) {
326                    break checkCompression;
327                }
328            }
329            fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
330                       "HANDSHAKE FAILURE. Incorrect client hello message");
331        }
332
333        if (!ProtocolVersion.isSupported(clientHello.client_version)) {
334            fatalAlert(AlertProtocol.PROTOCOL_VERSION,
335                       "PROTOCOL VERSION. Unsupported client version "
336                       + clientHello.client_version[0]
337                       + clientHello.client_version[1]);
338        }
339
340        isResuming = false;
341        FIND: if (clientHello.session_id.length != 0) {
342            // client wishes to reuse session
343
344            SSLSessionImpl sessionToResume;
345            boolean reuseCurrent = false;
346
347            // reuse current session
348            if (session != null
349                    && Arrays.equals(session.id, clientHello.session_id)) {
350                if (session.isValid()) {
351                    isResuming = true;
352                    break FIND;
353                }
354                reuseCurrent = true;
355            }
356
357            // find session in cash
358            sessionToResume = findSessionToResume(clientHello.session_id);
359            if (sessionToResume == null || !sessionToResume.isValid()) {
360                if (!parameters.getEnableSessionCreation()) {
361                    if (reuseCurrent) {
362                        // we can continue current session
363                        sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
364                        status = NOT_HANDSHAKING;
365                        clearMessages();
366                        return;
367                    }
368                    // throw AlertException
369                    fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created");
370                }
371                session = null;
372            } else {
373                session = (SSLSessionImpl)sessionToResume.clone();
374                isResuming = true;
375            }
376        }
377
378        if (isResuming) {
379            cipher_suite = session.cipherSuite;
380            // clientHello.cipher_suites must include at least cipher_suite from the session
381            checkCipherSuite: {
382                for (int i = 0; i < clientHello.cipher_suites.length; i++) {
383                    if (cipher_suite.equals(clientHello.cipher_suites[i])) {
384                        break checkCipherSuite;
385                    }
386                }
387                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
388                           "HANDSHAKE FAILURE. Incorrect client hello message");
389            }
390        } else {
391            cipher_suite = selectSuite(clientHello.cipher_suites);
392            if (cipher_suite == null) {
393                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "HANDSHAKE FAILURE. NO COMMON SUITE");
394            }
395            if (!parameters.getEnableSessionCreation()) {
396                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
397                           "SSL Session may not be created");
398            }
399            session = new SSLSessionImpl(cipher_suite, parameters.getSecureRandom());
400            if (engineOwner != null) {
401                session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
402            } else {
403                session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort());
404            }
405        }
406
407        recordProtocol.setVersion(clientHello.client_version);
408        session.protocol = ProtocolVersion.getByVersion(clientHello.client_version);
409        session.clientRandom = clientHello.random;
410
411        // create server hello message
412        serverHello = new ServerHello(parameters.getSecureRandom(),
413                clientHello.client_version,
414                session.getId(), cipher_suite, (byte) 0); //CompressionMethod.null
415        session.serverRandom = serverHello.random;
416        send(serverHello);
417        if (isResuming) {
418            sendChangeCipherSpec();
419            return;
420        }
421
422        //    create and send server certificate message if needed
423        if (!cipher_suite.isAnonymous()) { // need to send server certificate
424            X509Certificate[] certs = null;
425            String certType = cipher_suite.getServerKeyType();
426            if (certType == null) {
427                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "NO CERT TYPE FOR " + cipher_suite.getName());
428            }
429            // obtain certificates from key manager
430            String alias = null;
431            X509KeyManager km = parameters.getKeyManager();
432            if (km instanceof X509ExtendedKeyManager) {
433                X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
434                if (this.socketOwner != null) {
435                    alias = ekm.chooseServerAlias(certType, null,
436                            this.socketOwner);
437                } else {
438                    alias = ekm.chooseEngineServerAlias(certType, null,
439                            this.engineOwner);
440                }
441                if (alias != null) {
442                    certs = ekm.getCertificateChain(alias);
443                }
444            } else {
445                alias = km.chooseServerAlias(certType, null, this.socketOwner);
446                if (alias != null) {
447                    certs = km.getCertificateChain(alias);
448                }
449            }
450
451            if (certs == null) {
452                fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "NO SERVER CERTIFICATE FOUND");
453                return;
454            }
455            session.localCertificates = certs;
456            serverCert = new CertificateMessage(certs);
457            privKey = km.getPrivateKey(alias);
458            send(serverCert);
459        }
460
461        // create and send server key exchange message if needed
462        RSAPublicKey rsakey = null;
463        DHPublicKeySpec dhkeySpec = null;
464        byte[] hash = null;
465        BigInteger p = null;
466        BigInteger g = null;
467
468        KeyPairGenerator kpg = null;
469
470        try {
471            if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
472                PublicKey pk = serverCert.certs[0].getPublicKey();
473                if (getRSAKeyLength(pk) > 512) {
474                    // key is longer than 512 bits
475                    kpg = KeyPairGenerator.getInstance("RSA");
476                    kpg.initialize(512);
477                }
478            } else if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS
479                    || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS_EXPORT
480                    || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA
481                    || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA_EXPORT
482                    || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon
483                    || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon_EXPORT) {
484                kpg = KeyPairGenerator.getInstance("DH");
485                p = new BigInteger(1, DHParameters.getPrime());
486                g = new BigInteger("2");
487                DHParameterSpec spec = new DHParameterSpec(p, g);
488                kpg.initialize(spec);
489            }
490        } catch (Exception e) {
491            fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
492        }
493
494        if (kpg != null) {
495            // need to send server key exchange message
496            DigitalSignature ds = new DigitalSignature(cipher_suite.authType);
497            KeyPair kp = null;
498            try {
499                kp = kpg.genKeyPair();
500                if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
501                    rsakey = (RSAPublicKey) kp.getPublic();
502                } else {
503                    DHPublicKey dhkey = (DHPublicKey) kp.getPublic();
504                    KeyFactory kf = KeyFactory.getInstance("DH");
505                    dhkeySpec = kf.getKeySpec(dhkey, DHPublicKeySpec.class);
506                }
507                if (!cipher_suite.isAnonymous()) { // calculate signed_params
508
509                    // init by private key which correspond to
510                    // server certificate
511                    ds.init(privKey);
512
513                    // use emphemeral key for key exchange
514                    privKey = kp.getPrivate();
515                    ds.update(clientHello.getRandom());
516                    ds.update(serverHello.getRandom());
517
518                    byte[] tmp;
519                    byte[] tmpLength = new byte[2];
520//FIXME 1_byte==0x00
521                    if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
522                        tmp = ServerKeyExchange.toUnsignedByteArray(rsakey.getModulus());
523                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
524                        tmpLength[1] = (byte) (tmp.length & 0xFF);
525                        ds.update(tmpLength);
526                        ds.update(tmp);
527                        tmp = ServerKeyExchange.toUnsignedByteArray(rsakey.getPublicExponent());
528                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
529                        tmpLength[1] = (byte) (tmp.length & 0xFF);
530                        ds.update(tmpLength);
531                        ds.update(tmp);
532                    } else {
533                        tmp = ServerKeyExchange.toUnsignedByteArray(dhkeySpec.getP());
534                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
535                        tmpLength[1] = (byte) (tmp.length & 0xFF);
536                        ds.update(tmpLength);
537                        ds.update(tmp);
538                        tmp = ServerKeyExchange.toUnsignedByteArray(dhkeySpec.getG());
539                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
540                        tmpLength[1] = (byte) (tmp.length & 0xFF);
541                        ds.update(tmpLength);
542                        ds.update(tmp);
543                        tmp = ServerKeyExchange.toUnsignedByteArray(dhkeySpec.getY());
544                        tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
545                        tmpLength[1] = (byte) (tmp.length & 0xFF);
546                        ds.update(tmpLength);
547                        ds.update(tmp);
548                    }
549                    hash = ds.sign();
550                } else {
551                    privKey = kp.getPrivate(); // use emphemeral key for key exchange
552                }
553            } catch (Exception e) {
554                fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
555            }
556
557            if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
558                serverKeyExchange = new ServerKeyExchange(rsakey.getModulus(),
559                        rsakey.getPublicExponent(), null, hash);
560            } else {
561                serverKeyExchange = new ServerKeyExchange(p,
562                        g, dhkeySpec.getY(), hash);
563            }
564            send(serverKeyExchange);
565        }
566
567        // CERTIFICATE_REQUEST
568        certRequest: if (parameters.getWantClientAuth()
569                || parameters.getNeedClientAuth()) {
570            X509Certificate[] accepted;
571            try {
572                X509TrustManager tm = parameters.getTrustManager();
573                accepted = tm.getAcceptedIssuers();
574            } catch (ClassCastException e) {
575                // don't send certificateRequest
576                break certRequest;
577            }
578            byte[] requestedClientCertTypes = { CipherSuite.TLS_CT_RSA_SIGN,
579                                                CipherSuite.TLS_CT_DSS_SIGN };
580            certificateRequest = new CertificateRequest(
581                    requestedClientCertTypes, accepted);
582            send(certificateRequest);
583        }
584
585        // SERVER_HELLO_DONE
586        serverHelloDone = new ServerHelloDone();
587        send(serverHelloDone);
588        status = NEED_UNWRAP;
589    }
590
591    /**
592     * Creates and sends finished message
593     */
594    @Override
595    protected void makeFinished() {
596        byte[] verify_data;
597        boolean isTLS = (serverHello.server_version[1] == 1); // TLS 1.0 protocol
598        if (isTLS) {
599            verify_data = new byte[12];
600            computerVerifyDataTLS("server finished", verify_data);
601        } else { // SSL 3.0 protocol (http://wp.netscape.com/eng/ssl3)
602            verify_data = new byte[36];
603            computerVerifyDataSSLv3(SSLv3Constants.server, verify_data);
604        }
605        serverFinished = new Finished(verify_data);
606        send(serverFinished);
607        if (isResuming) {
608            if (isTLS) {
609                computerReferenceVerifyDataTLS("client finished");
610            } else {
611                computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
612            }
613            status = NEED_UNWRAP;
614        } else {
615            session.lastAccessedTime = System.currentTimeMillis();
616            status = FINISHED;
617        }
618    }
619
620    // find sesssion in the session hash
621    private SSLSessionImpl findSessionToResume(byte[] session_id) {
622        return (SSLSessionImpl)parameters.getServerSessionContext().getSession(session_id);
623    }
624
625    // find appropriate cipher_suite in the client suites
626    private CipherSuite selectSuite(CipherSuite[] clientSuites) {
627        for (CipherSuite clientSuite : clientSuites) {
628            if (!clientSuite.supported) {
629                continue;
630            }
631            for (CipherSuite enabledCipherSuite : parameters.getEnabledCipherSuitesMember()) {
632                if (clientSuite.equals(enabledCipherSuite)) {
633                    return clientSuite;
634                }
635            }
636        }
637        return null;
638    }
639
640    /**
641     * Processes inbound ChangeCipherSpec message
642     */
643    @Override
644    public void receiveChangeCipherSpec() {
645        if (isResuming) {
646            if (serverFinished == null) {
647                unexpectedMessage();
648            } else {
649                changeCipherSpecReceived = true;
650            }
651        } else {
652            if ((parameters.getNeedClientAuth() && clientCert == null)
653                    || clientKeyExchange == null
654                    || (clientCert != null && clientCert.certs.length > 0
655                            && !clientKeyExchange.isEmpty()
656                            && certificateVerify == null)) {
657                unexpectedMessage();
658            } else {
659                changeCipherSpecReceived = true;
660            }
661            if (serverHello.server_version[1] == 1) {
662                computerReferenceVerifyDataTLS("client finished");
663            } else {
664                computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
665            }
666        }
667    }
668
669}
670