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