Home | History | Annotate | Download | only in conscrypt
      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 
     18 package org.conscrypt;
     19 
     20 import java.io.IOException;
     21 import java.security.Key;
     22 import java.security.KeyFactory;
     23 import java.security.KeyPair;
     24 import java.security.KeyPairGenerator;
     25 import java.security.NoSuchAlgorithmException;
     26 import java.security.PrivateKey;
     27 import java.security.PublicKey;
     28 import java.security.cert.CertificateException;
     29 import java.security.cert.X509Certificate;
     30 import java.util.Arrays;
     31 import javax.crypto.Cipher;
     32 import javax.crypto.KeyAgreement;
     33 import javax.crypto.interfaces.DHKey;
     34 import javax.crypto.interfaces.DHPublicKey;
     35 import javax.crypto.spec.DHParameterSpec;
     36 import javax.crypto.spec.DHPublicKeySpec;
     37 import javax.crypto.spec.SecretKeySpec;
     38 import javax.net.ssl.X509ExtendedKeyManager;
     39 import javax.net.ssl.X509KeyManager;
     40 import javax.net.ssl.X509TrustManager;
     41 import javax.security.auth.x500.X500Principal;
     42 
     43 /**
     44  * Client side handshake protocol implementation.
     45  * Handshake protocol operates on top of the Record Protocol.
     46  * It is responsible for session negotiating.
     47  *
     48  * The implementation processes inbound server 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. The
     53  * TLS Handshake Protocol</a>
     54  *
     55  */
     56 public class ClientHandshakeImpl extends HandshakeProtocol {
     57 
     58     /**
     59      * Creates Client Handshake Implementation
     60      *
     61      * @param owner
     62      */
     63     ClientHandshakeImpl(Object owner) {
     64         super(owner);
     65     }
     66 
     67     /**
     68      * Starts handshake
     69      *
     70      */
     71     @Override
     72     public void start() {
     73         if (session == null) { // initial handshake
     74             session = findSessionToResume();
     75         } else { // start session renegotiation
     76             if (clientHello != null && this.status != FINISHED) {
     77                 // current negotiation has not completed
     78                 return; // ignore
     79             }
     80             if (!session.isValid()) {
     81                 session = null;
     82             }
     83         }
     84         if (session != null) {
     85             isResuming = true;
     86         } else if (parameters.getEnableSessionCreation()){
     87             isResuming = false;
     88             session = new SSLSessionImpl(parameters.getSecureRandom());
     89             if (engineOwner != null) {
     90                 session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
     91             } else {
     92                 session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort());
     93             }
     94             session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols());
     95             recordProtocol.setVersion(session.protocol.version);
     96         } else {
     97             fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created ");
     98         }
     99         startSession();
    100     }
    101 
    102     /**
    103      * Starts renegotiation on a new session
    104      *
    105      */
    106     private void renegotiateNewSession() {
    107         if (parameters.getEnableSessionCreation()){
    108             isResuming = false;
    109             session = new SSLSessionImpl(parameters.getSecureRandom());
    110             if (engineOwner != null) {
    111                 session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
    112             } else {
    113                 session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort());
    114             }
    115             session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols());
    116             recordProtocol.setVersion(session.protocol.version);
    117             startSession();
    118         } else {
    119             status = NOT_HANDSHAKING;
    120             sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
    121         }
    122     }
    123 
    124     /*
    125      * Starts/resumes session
    126      */
    127     private void startSession() {
    128         CipherSuite[] cipher_suites;
    129         if (isResuming) {
    130             cipher_suites = new CipherSuite[] { session.cipherSuite };
    131         } else {
    132             cipher_suites = parameters.getEnabledCipherSuitesMember();
    133         }
    134         clientHello = new ClientHello(parameters.getSecureRandom(),
    135                 session.protocol.version, session.id, cipher_suites);
    136         session.clientRandom = clientHello.random;
    137         send(clientHello);
    138         status = NEED_UNWRAP;
    139     }
    140 
    141     /**
    142      * Processes inbound handshake messages
    143      * @param bytes
    144      */
    145     @Override
    146     public void unwrap(byte[] bytes) {
    147         if (this.delegatedTaskErr != null) {
    148             Exception e = this.delegatedTaskErr;
    149             this.delegatedTaskErr = null;
    150             this.fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "Error in delegated task", e);
    151         }
    152         int handshakeType;
    153         io_stream.append(bytes);
    154         while (io_stream.available() > 0) {
    155             io_stream.mark();
    156             int length;
    157             try {
    158                 handshakeType = io_stream.read();
    159                 length = io_stream.readUint24();
    160                 if (io_stream.available() < length) {
    161                     io_stream.reset();
    162                     return;
    163                 }
    164                 switch (handshakeType) {
    165                 case 0: // HELLO_REQUEST
    166                     // we don't need to take this message into account
    167                     // during FINISH message verification, so remove it
    168                     io_stream.removeFromMarkedPosition();
    169                     if (clientHello != null
    170                             && (clientFinished == null || serverFinished == null)) {
    171                         //currently negotiating - ignore
    172                         break;
    173                     }
    174                     // renegotiate
    175                     if (session.isValid()) {
    176                         session = (SSLSessionImpl) session.clone();
    177                         isResuming = true;
    178                         startSession();
    179                     } else {
    180                         // if SSLSession is invalidated (e.g. timeout limit is
    181                         // exceeded) connection can't resume the session.
    182                         renegotiateNewSession();
    183                     }
    184                     break;
    185                 case 2: // SERVER_HELLO
    186                     if (clientHello == null || serverHello != null) {
    187                         unexpectedMessage();
    188                         return;
    189                     }
    190                     serverHello = new ServerHello(io_stream, length);
    191 
    192                     //check protocol version
    193                     ProtocolVersion servProt = ProtocolVersion.getByVersion(serverHello.server_version);
    194                     String[] enabled = parameters.getEnabledProtocols();
    195                     find: {
    196                         for (int i = 0; i < enabled.length; i++) {
    197                             if (servProt.equals(ProtocolVersion.getByName(enabled[i]))) {
    198                                 break find;
    199                             }
    200                         }
    201                         fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
    202                                    "Bad server hello protocol version");
    203                     }
    204 
    205                     // check compression method
    206                     if (serverHello.compression_method != 0) {
    207                         fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
    208                                    "Bad server hello compression method");
    209                     }
    210 
    211                     //check cipher_suite
    212                     CipherSuite[] enabledSuites = parameters.getEnabledCipherSuitesMember();
    213                     find: {
    214                         for (int i = 0; i < enabledSuites.length; i++) {
    215                             if (serverHello.cipher_suite.equals(enabledSuites[i])) {
    216                                 break find;
    217                             }
    218                         }
    219                         fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
    220                                    "Bad server hello cipher suite");
    221                     }
    222 
    223                     if (isResuming) {
    224                         if (serverHello.session_id.length == 0) {
    225                             // server is not willing to establish the new connection
    226                             // using specified session
    227                             isResuming = false;
    228                         } else if (!Arrays.equals(serverHello.session_id, clientHello.session_id)) {
    229                             isResuming = false;
    230                         } else if (!session.protocol.equals(servProt)) {
    231                             fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
    232                                        "Bad server hello protocol version");
    233                         } else if (!session.cipherSuite.equals(serverHello.cipher_suite)) {
    234                             fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
    235                                        "Bad server hello cipher suite");
    236                         }
    237                         if (serverHello.server_version[1] == 1) {
    238                             computerReferenceVerifyDataTLS("server finished");
    239                         } else {
    240                             computerReferenceVerifyDataSSLv3(SSLv3Constants.server);
    241                         }
    242                     }
    243                     session.protocol = servProt;
    244                     recordProtocol.setVersion(session.protocol.version);
    245                     session.cipherSuite = serverHello.cipher_suite;
    246                     session.id = serverHello.session_id.clone();
    247                     session.serverRandom = serverHello.random;
    248                     break;
    249                 case 11: // CERTIFICATE
    250                     if (serverHello == null || serverKeyExchange != null
    251                             || serverCert != null || isResuming) {
    252                         unexpectedMessage();
    253                         return;
    254                     }
    255                     serverCert = new CertificateMessage(io_stream, length);
    256                     break;
    257                 case 12: // SERVER_KEY_EXCHANGE
    258                     if (serverHello == null || serverKeyExchange != null
    259                             || isResuming) {
    260                         unexpectedMessage();
    261                         return;
    262                     }
    263                     serverKeyExchange = new ServerKeyExchange(io_stream,
    264                             length, session.cipherSuite.keyExchange);
    265                     break;
    266                 case 13: // CERTIFICATE_REQUEST
    267                     if (serverCert == null || certificateRequest != null
    268                             || session.cipherSuite.isAnonymous() || isResuming) {
    269                         unexpectedMessage();
    270                         return;
    271                     }
    272                     certificateRequest = new CertificateRequest(io_stream, length);
    273                     break;
    274                 case 14: // SERVER_HELLO_DONE
    275                     if (serverHello == null || serverHelloDone != null || isResuming) {
    276                         unexpectedMessage();
    277                         return;
    278                     }
    279                     serverHelloDone = new ServerHelloDone(io_stream, length);
    280                     if (this.nonBlocking) {
    281                         delegatedTasks.add(new DelegatedTask(new Runnable() {
    282                             public void run() {
    283                                 processServerHelloDone();
    284                             }
    285                         }, this));
    286                         return;
    287                     }
    288                     processServerHelloDone();
    289                     break;
    290                 case 20: // FINISHED
    291                     if (!changeCipherSpecReceived) {
    292                         unexpectedMessage();
    293                         return;
    294                     }
    295                     serverFinished = new Finished(io_stream, length);
    296                     verifyFinished(serverFinished.getData());
    297                     session.lastAccessedTime = System.currentTimeMillis();
    298                     session.context = parameters.getClientSessionContext();
    299                     parameters.getClientSessionContext().putSession(session);
    300                     if (isResuming) {
    301                         sendChangeCipherSpec();
    302                     } else {
    303                         session.lastAccessedTime = System.currentTimeMillis();
    304                         status = FINISHED;
    305                     }
    306                     // XXX there is no cleanup work
    307                     break;
    308                 default:
    309                     unexpectedMessage();
    310                     return;
    311                 }
    312             } catch (IOException e) {
    313                 // io stream dosn't contain complete handshake message
    314                 io_stream.reset();
    315                 return;
    316             }
    317         }
    318 
    319     }
    320 
    321     /**
    322      * Processes SSLv2 Hello message.
    323      * SSLv2 client hello message message is an unexpected message
    324      * for client side of handshake protocol.
    325      * See TLS 1.0 spec., E.1. Version 2 client hello
    326      * @param bytes
    327      */
    328     @Override
    329     public void unwrapSSLv2(byte[] bytes) {
    330         unexpectedMessage();
    331     }
    332 
    333     /**
    334      * Creates and sends Finished message
    335      */
    336     @Override
    337     protected void makeFinished() {
    338         byte[] verify_data;
    339         if (serverHello.server_version[1] == 1) {
    340             verify_data = new byte[12];
    341             computerVerifyDataTLS("client finished", verify_data);
    342         } else {
    343             verify_data = new byte[36];
    344             computerVerifyDataSSLv3(SSLv3Constants.client, verify_data);
    345         }
    346         clientFinished = new Finished(verify_data);
    347         send(clientFinished);
    348         if (isResuming) {
    349             session.lastAccessedTime = System.currentTimeMillis();
    350             status = FINISHED;
    351         } else {
    352             if (serverHello.server_version[1] == 1) {
    353                 computerReferenceVerifyDataTLS("server finished");
    354             } else {
    355                 computerReferenceVerifyDataSSLv3(SSLv3Constants.server);
    356             }
    357             status = NEED_UNWRAP;
    358         }
    359     }
    360 
    361     /**
    362      * Processes ServerHelloDone: makes verification of the server messages; sends
    363      * client messages, computers masterSecret, sends ChangeCipherSpec
    364      */
    365     void processServerHelloDone() {
    366         PrivateKey clientKey = null;
    367 
    368         if (serverCert != null) {
    369             if (session.cipherSuite.isAnonymous()) {
    370                 unexpectedMessage();
    371                 return;
    372             }
    373             verifyServerCert();
    374         } else {
    375             if (!session.cipherSuite.isAnonymous()) {
    376                 unexpectedMessage();
    377                 return;
    378             }
    379         }
    380 
    381         // Client certificate
    382         if (certificateRequest != null) {
    383             X509Certificate[] certs = null;
    384             // obtain certificates from key manager
    385             String alias = null;
    386             String[] certTypes = certificateRequest.getTypesAsString();
    387             X500Principal[] issuers = certificateRequest.certificate_authorities;
    388             X509KeyManager km = parameters.getKeyManager();
    389             if (km instanceof X509ExtendedKeyManager) {
    390                 X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
    391                 if (this.socketOwner != null) {
    392                     alias = ekm.chooseClientAlias(certTypes, issuers, this.socketOwner);
    393                 } else {
    394                     alias = ekm.chooseEngineClientAlias(certTypes, issuers, this.engineOwner);
    395                 }
    396                 if (alias != null) {
    397                     certs = ekm.getCertificateChain(alias);
    398                 }
    399             } else {
    400                 alias = km.chooseClientAlias(certTypes, issuers, this.socketOwner);
    401                 if (alias != null) {
    402                     certs = km.getCertificateChain(alias);
    403                 }
    404             }
    405 
    406             session.localCertificates = certs;
    407             clientCert = new CertificateMessage(certs);
    408             clientKey = km.getPrivateKey(alias);
    409             send(clientCert);
    410         }
    411         // Client key exchange
    412         if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA
    413                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
    414             // RSA encrypted premaster secret message
    415             Cipher c;
    416             try {
    417                 c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    418                 if (serverKeyExchange != null) {
    419                     if (!session.cipherSuite.isAnonymous()) {
    420                         DigitalSignature ds = new DigitalSignature(serverCert.getAuthType());
    421                         ds.init(serverCert.certs[0]);
    422                         ds.update(clientHello.getRandom());
    423                         ds.update(serverHello.getRandom());
    424                         if (!serverKeyExchange.verifySignature(ds)) {
    425                             fatalAlert(AlertProtocol.DECRYPT_ERROR, "Cannot verify RSA params");
    426                             return;
    427                         }
    428                     }
    429                     c.init(Cipher.WRAP_MODE, serverKeyExchange
    430                             .getRSAPublicKey());
    431                 } else {
    432                     c.init(Cipher.WRAP_MODE, serverCert.certs[0]);
    433                 }
    434             } catch (Exception e) {
    435                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
    436                         "Unexpected exception", e);
    437                 return;
    438             }
    439             preMasterSecret = new byte[48];
    440             parameters.getSecureRandom().nextBytes(preMasterSecret);
    441             System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0, 2);
    442             try {
    443                 clientKeyExchange = new ClientKeyExchange(c
    444                         .wrap(new SecretKeySpec(preMasterSecret, "preMasterSecret")),
    445                         serverHello.server_version[1] == 1);
    446             } catch (Exception e) {
    447                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
    448                         "Unexpected exception", e);
    449                 return;
    450             }
    451         } else if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS
    452                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS_EXPORT
    453                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA
    454                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA_EXPORT
    455                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon
    456                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon_EXPORT) {
    457             /*
    458              * All other key exchanges should have had a DH key communicated via
    459              * ServerKeyExchange beforehand.
    460              */
    461             if (serverKeyExchange == null) {
    462                 fatalAlert(AlertProtocol.UNEXPECTED_MESSAGE, "Expected ServerKeyExchange");
    463                 return;
    464             }
    465             if (session.cipherSuite.isAnonymous() != serverKeyExchange.isAnonymous()) {
    466                 fatalAlert(AlertProtocol.DECRYPT_ERROR, "Wrong type in ServerKeyExchange");
    467                 return;
    468             }
    469             try {
    470                 if (!session.cipherSuite.isAnonymous()) {
    471                     DigitalSignature ds = new DigitalSignature(serverCert.getAuthType());
    472                     ds.init(serverCert.certs[0]);
    473                     ds.update(clientHello.getRandom());
    474                     ds.update(serverHello.getRandom());
    475                     if (!serverKeyExchange.verifySignature(ds)) {
    476                         fatalAlert(AlertProtocol.DECRYPT_ERROR, "Cannot verify DH params");
    477                         return;
    478                     }
    479                 }
    480                 KeyFactory kf = KeyFactory.getInstance("DH");
    481                 KeyAgreement agreement = KeyAgreement.getInstance("DH");
    482                 KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
    483                 PublicKey serverDhPublic = kf.generatePublic(new DHPublicKeySpec(
    484                         serverKeyExchange.par3, serverKeyExchange.par1,
    485                         serverKeyExchange.par2));
    486                 DHParameterSpec spec = new DHParameterSpec(serverKeyExchange.par1,
    487                         serverKeyExchange.par2);
    488                 kpg.initialize(spec);
    489                 KeyPair kp = kpg.generateKeyPair();
    490                 DHPublicKey pubDhKey = (DHPublicKey) kp.getPublic();
    491                 clientKeyExchange = new ClientKeyExchange(pubDhKey.getY());
    492                 PrivateKey privDhKey = kp.getPrivate();
    493                 agreement.init(privDhKey);
    494                 agreement.doPhase(serverDhPublic, true);
    495                 preMasterSecret = agreement.generateSecret();
    496             } catch (Exception e) {
    497                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
    498                         "Unexpected exception", e);
    499                 return;
    500             }
    501         } else {
    502             fatalAlert(AlertProtocol.DECRYPT_ERROR, "Unsupported handshake type");
    503             return;
    504         }
    505 
    506         if (clientKeyExchange != null) {
    507             send(clientKeyExchange);
    508         }
    509 
    510         computerMasterSecret();
    511 
    512         // send certificate verify for all certificates except those containing
    513         // fixed DH parameters
    514         if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) {
    515             // Certificate verify
    516             String authType = clientKey.getAlgorithm();
    517             DigitalSignature ds = new DigitalSignature(authType);
    518             ds.init(clientKey);
    519 
    520             if ("RSA".equals(authType)) {
    521                 ds.setMD5(io_stream.getDigestMD5());
    522                 ds.setSHA(io_stream.getDigestSHA());
    523             } else if ("DSA".equals(authType)) {
    524                 ds.setSHA(io_stream.getDigestSHA());
    525             // The Signature should be empty in case of anonymous signature algorithm:
    526             // } else if ("DH".equals(authType)) {
    527             }
    528             certificateVerify = new CertificateVerify(ds.sign());
    529             send(certificateVerify);
    530         }
    531 
    532         sendChangeCipherSpec();
    533     }
    534 
    535     /*
    536      * Verifies certificate path
    537      */
    538     private void verifyServerCert() {
    539         String authType = session.cipherSuite.getAuthType(serverKeyExchange != null);
    540         if (authType == null) {
    541             return;
    542         }
    543         String hostname = null;
    544         if (engineOwner != null) {
    545             hostname = engineOwner.getPeerHost();
    546         } else {
    547             // we don't want to do an inet address lookup here in case we're talking to a proxy
    548             hostname = socketOwner.getWrappedHostName();
    549         }
    550         try {
    551             X509TrustManager x509tm = parameters.getTrustManager();
    552             if (x509tm instanceof TrustManagerImpl) {
    553                 TrustManagerImpl tm = (TrustManagerImpl) x509tm;
    554                 tm.checkServerTrusted(serverCert.certs, authType, hostname);
    555             } else {
    556                 x509tm.checkServerTrusted(serverCert.certs, authType);
    557             }
    558         } catch (CertificateException e) {
    559             fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e);
    560             return;
    561         }
    562         session.peerCertificates = serverCert.certs;
    563     }
    564 
    565     /**
    566      * Processes ChangeCipherSpec message
    567      */
    568     @Override
    569     public void receiveChangeCipherSpec() {
    570         if (isResuming) {
    571             if (serverHello == null) {
    572                 unexpectedMessage();
    573             }
    574         } else if (clientFinished == null) {
    575             unexpectedMessage();
    576         }
    577         changeCipherSpecReceived = true;
    578     }
    579 
    580     // Find session to resume in client session context
    581     private SSLSessionImpl findSessionToResume() {
    582         String host = null;
    583         int port = -1;
    584         if (engineOwner != null) {
    585             host = engineOwner.getPeerHost();
    586             port = engineOwner.getPeerPort();
    587         } else {
    588             host = socketOwner.getPeerHostName();
    589             port = socketOwner.getPeerPort();
    590         }
    591         if (host == null || port == -1) {
    592             return null; // starts new session
    593         }
    594 
    595         ClientSessionContext context = parameters.getClientSessionContext();
    596         SSLSessionImpl session
    597                 = (SSLSessionImpl) context.getSession(host, port);
    598         if (session != null) {
    599             session = (SSLSessionImpl) session.clone();
    600         }
    601         return session;
    602     }
    603 
    604 }
    605