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             if (engineOwner != null) {
     90                 session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
     91             } else {
     92                 session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort());
     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.getInetAddress().getHostName(), socketOwner.getPort());
    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                     c.init(Cipher.ENCRYPT_MODE, serverKeyExchange
    420                             .getRSAPublicKey());
    421                 } else {
    422                     c.init(Cipher.ENCRYPT_MODE, serverCert.certs[0]);
    423                 }
    424             } catch (Exception e) {
    425                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
    426                         "Unexpected exception", e);
    427                 return;
    428             }
    429             preMasterSecret = new byte[48];
    430             parameters.getSecureRandom().nextBytes(preMasterSecret);
    431             System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0, 2);
    432             try {
    433                 clientKeyExchange = new ClientKeyExchange(c
    434                         .doFinal(preMasterSecret),
    435                         serverHello.server_version[1] == 1);
    436             } catch (Exception e) {
    437                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
    438                         "Unexpected exception", e);
    439                 return;
    440             }
    441         } else {
    442             try {
    443                 KeyFactory kf = KeyFactory.getInstance("DH");
    444                 KeyAgreement agreement = KeyAgreement.getInstance("DH");
    445                 KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
    446                 PublicKey serverPublic;
    447                 DHParameterSpec spec;
    448                 if (serverKeyExchange != null) {
    449                     serverPublic = kf.generatePublic(new DHPublicKeySpec(
    450                             serverKeyExchange.par3, serverKeyExchange.par1,
    451                             serverKeyExchange.par2));
    452                     spec = new DHParameterSpec(serverKeyExchange.par1,
    453                             serverKeyExchange.par2);
    454                 } else {
    455                     serverPublic = serverCert.certs[0].getPublicKey();
    456                     spec = ((DHPublicKey) serverPublic).getParams();
    457                 }
    458                 kpg.initialize(spec);
    459 
    460                 KeyPair kp = kpg.generateKeyPair();
    461                 Key key = kp.getPublic();
    462                 if (clientCert != null
    463                         && serverCert != null
    464                         && (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA
    465                                 || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS)) {
    466                     PublicKey client_pk = clientCert.certs[0].getPublicKey();
    467                     PublicKey server_pk = serverCert.certs[0].getPublicKey();
    468                     if (client_pk instanceof DHKey
    469                             && server_pk instanceof DHKey) {
    470                         if (((DHKey) client_pk).getParams().getG().equals(
    471                                 ((DHKey) server_pk).getParams().getG())
    472                                 && ((DHKey) client_pk).getParams().getP()
    473                                     .equals(((DHKey) server_pk).getParams().getG())) {
    474                             // client cert message DH public key parameters
    475                             // matched those specified by the
    476                             //   server in its certificate,
    477                             clientKeyExchange = new ClientKeyExchange(); // empty
    478                         }
    479                     }
    480                 } else {
    481                     clientKeyExchange = new ClientKeyExchange(
    482                             ((DHPublicKey) key).getY());
    483                 }
    484                 key = kp.getPrivate();
    485                 agreement.init(key);
    486                 agreement.doPhase(serverPublic, true);
    487                 preMasterSecret = agreement.generateSecret();
    488             } catch (Exception e) {
    489                 fatalAlert(AlertProtocol.INTERNAL_ERROR,
    490                         "Unexpected exception", e);
    491                 return;
    492             }
    493         }
    494         if (clientKeyExchange != null) {
    495             send(clientKeyExchange);
    496         }
    497 
    498         computerMasterSecret();
    499 
    500         // send certificate verify for all certificates except those containing
    501         // fixed DH parameters
    502         if (clientCert != null && !clientKeyExchange.isEmpty()) {
    503             // Certificate verify
    504             String authType = clientKey.getAlgorithm();
    505             DigitalSignature ds = new DigitalSignature(authType);
    506             ds.init(clientKey);
    507 
    508             if ("RSA".equals(authType)) {
    509                 ds.setMD5(io_stream.getDigestMD5());
    510                 ds.setSHA(io_stream.getDigestSHA());
    511             } else if ("DSA".equals(authType)) {
    512                 ds.setSHA(io_stream.getDigestSHA());
    513             // The Signature should be empty in case of anonymous signature algorithm:
    514             // } else if ("DH".equals(authType)) {
    515             }
    516             certificateVerify = new CertificateVerify(ds.sign());
    517             send(certificateVerify);
    518         }
    519 
    520         sendChangeCipherSpec();
    521     }
    522 
    523     /*
    524      * Verifies certificate path
    525      */
    526     private void verifyServerCert() {
    527         String authType = session.cipherSuite.getAuthType(serverKeyExchange != null);
    528         if (authType == null) {
    529             return;
    530         }
    531         try {
    532             parameters.getTrustManager().checkServerTrusted(serverCert.certs, authType);
    533         } catch (CertificateException e) {
    534             fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e);
    535             return;
    536         }
    537         session.peerCertificates = serverCert.certs;
    538     }
    539 
    540     /**
    541      * Processes ChangeCipherSpec message
    542      */
    543     @Override
    544     public void receiveChangeCipherSpec() {
    545         if (isResuming) {
    546             if (serverHello == null) {
    547                 unexpectedMessage();
    548             }
    549         } else if (clientFinished == null) {
    550             unexpectedMessage();
    551         }
    552         changeCipherSpecReceived = true;
    553     }
    554 
    555     // Find session to resume in client session context
    556     private SSLSessionImpl findSessionToResume() {
    557         String host = null;
    558         int port = -1;
    559         if (engineOwner != null) {
    560             host = engineOwner.getPeerHost();
    561             port = engineOwner.getPeerPort();
    562         } else {
    563             host = socketOwner.getInetAddress().getHostName();
    564             port = socketOwner.getPort();
    565         }
    566         if (host == null || port == -1) {
    567             return null; // starts new session
    568         }
    569 
    570         ClientSessionContext context = parameters.getClientSessionContext();
    571         SSLSessionImpl session
    572                 = (SSLSessionImpl) context.getSession(host, port);
    573         if (session != null) {
    574             session = (SSLSessionImpl) session.clone();
    575         }
    576         return session;
    577     }
    578 
    579 }
    580