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