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