1678 lines · 65.9 KB · Java
RawDownload
/* * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */  package sun.security.ssl; import java.io.*;import java.util.*;import java.security.*;import java.security.cert.*;import java.security.interfaces.*;import java.security.spec.ECParameterSpec; import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; import javax.security.auth.Subject; import sun.security.ssl.HandshakeMessage.*;import sun.security.ssl.CipherSuite.*;import sun.security.ssl.SignatureAndHashAlgorithm.*;import static sun.security.ssl.CipherSuite.*;import static sun.security.ssl.CipherSuite.KeyExchange.*; /** * ServerHandshaker does the protocol handshaking from the point * of view of a server.  It is driven asychronously by handshake messages * as delivered by the parent Handshaker class, and also uses * common functionality (e.g. key generation) that is provided there. * * @author David Brownell */final class ServerHandshaker extends Handshaker {     // is the server going to require the client to authenticate?    private byte                doClientAuth;     // our authentication info    private X509Certificate[]   certs;    private PrivateKey          privateKey;     private SecretKey[]       kerberosKeys;     // flag to check for clientCertificateVerify message    private boolean             needClientVerify = false;     /*     * For exportable ciphersuites using non-exportable key sizes, we use     * ephemeral RSA keys. We could also do anonymous RSA in the same way     * but there are no such ciphersuites currently defined.     */    private PrivateKey          tempPrivateKey;    private PublicKey           tempPublicKey;     /*     * For anonymous and ephemeral Diffie-Hellman key exchange, we use     * ephemeral Diffie-Hellman keys.     */    private DHCrypt dh;     // Helper for ECDH based key exchanges    private ECDHCrypt ecdh;     // version request by the client in its ClientHello    // we remember it for the RSA premaster secret version check    private ProtocolVersion clientRequestedVersion;     private SupportedEllipticCurvesExtension supportedCurves;     // the preferable signature algorithm used by ServerKeyExchange message    SignatureAndHashAlgorithm preferableSignatureAlgorithm;     /*     * Constructor ... use the keys found in the auth context.     */    ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,            ProtocolList enabledProtocols, byte clientAuth,            ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,            boolean secureRenegotiation,            byte[] clientVerifyData, byte[] serverVerifyData) {         super(socket, context, enabledProtocols,                (clientAuth != SSLEngineImpl.clauth_none), false,                activeProtocolVersion, isInitialHandshake, secureRenegotiation,                clientVerifyData, serverVerifyData);        doClientAuth = clientAuth;    }     /*     * Constructor ... use the keys found in the auth context.     */    ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,            ProtocolList enabledProtocols, byte clientAuth,            ProtocolVersion activeProtocolVersion,            boolean isInitialHandshake, boolean secureRenegotiation,            byte[] clientVerifyData, byte[] serverVerifyData) {         super(engine, context, enabledProtocols,                (clientAuth != SSLEngineImpl.clauth_none), false,                activeProtocolVersion, isInitialHandshake, secureRenegotiation,                clientVerifyData, serverVerifyData);        doClientAuth = clientAuth;    }     /*     * As long as handshaking has not started, we can change     * whether client authentication is required.  Otherwise,     * we will need to wait for the next handshake.     */    void setClientAuth(byte clientAuth) {        doClientAuth = clientAuth;    }     /*     * This routine handles all the server side handshake messages, one at     * a time.  Given the message type (and in some cases the pending cipher     * spec) it parses the type-specific message.  Then it calls a function     * that handles that specific message.     *     * It updates the state machine as each message is processed, and writes     * responses as needed using the connection in the constructor.     */    void processMessage(byte type, int message_len)            throws IOException {        //        // In SSLv3 and TLS, messages follow strictly increasing        // numerical order _except_ for one annoying special case.        //        if ((state > type)                && (state != HandshakeMessage.ht_client_key_exchange                    && type != HandshakeMessage.ht_certificate_verify)) {            throw new SSLProtocolException(                    "Handshake message sequence violation, state = " + state                    + ", type = " + type);        }         switch (type) {            case HandshakeMessage.ht_client_hello:                ClientHello ch = new ClientHello(input, message_len);                /*                 * send it off for processing.                 */                this.clientHello(ch);                break;             case HandshakeMessage.ht_certificate:                if (doClientAuth == SSLEngineImpl.clauth_none) {                    fatalSE(Alerts.alert_unexpected_message,                                "client sent unsolicited cert chain");                    // NOTREACHED                }                this.clientCertificate(new CertificateMsg(input));                break;             case HandshakeMessage.ht_client_key_exchange:                SecretKey preMasterSecret;                switch (keyExchange) {                case K_RSA:                case K_RSA_EXPORT:                    /*                     * The client's pre-master secret is decrypted using                     * either the server's normal private RSA key, or the                     * temporary one used for non-export or signing-only                     * certificates/keys.                     */                    RSAClientKeyExchange pms = new RSAClientKeyExchange(                            protocolVersion, clientRequestedVersion,                            sslContext.getSecureRandom(), input,                            message_len, privateKey);                    preMasterSecret = this.clientKeyExchange(pms);                    break;                case K_KRB5:                case K_KRB5_EXPORT:                    preMasterSecret = this.clientKeyExchange(                        new KerberosClientKeyExchange(protocolVersion,                            clientRequestedVersion,                            sslContext.getSecureRandom(),                            input,                            kerberosKeys));                    break;                case K_DHE_RSA:                case K_DHE_DSS:                case K_DH_ANON:                    /*                     * The pre-master secret is derived using the normal                     * Diffie-Hellman calculation.   Note that the main                     * protocol difference in these five flavors is in how                     * the ServerKeyExchange message was constructed!                     */                    preMasterSecret = this.clientKeyExchange(                            new DHClientKeyExchange(input));                    break;                case K_ECDH_RSA:                case K_ECDH_ECDSA:                case K_ECDHE_RSA:                case K_ECDHE_ECDSA:                case K_ECDH_ANON:                    preMasterSecret = this.clientKeyExchange                                            (new ECDHClientKeyExchange(input));                    break;                default:                    throw new SSLProtocolException                        ("Unrecognized key exchange: " + keyExchange);                }                 //                // All keys are calculated from the premaster secret                // and the exchanged nonces in the same way.                //                calculateKeys(preMasterSecret, clientRequestedVersion);                break;             case HandshakeMessage.ht_certificate_verify:                this.clientCertificateVerify(new CertificateVerify(input,                            localSupportedSignAlgs, protocolVersion));                break;             case HandshakeMessage.ht_finished:                this.clientFinished(                    new Finished(protocolVersion, input, cipherSuite));                break;             default:                throw new SSLProtocolException(                        "Illegal server handshake msg, " + type);        }         //        // Move the state machine forward except for that annoying        // special case.  This means that clients could send extra        // cert verify messages; not a problem so long as all of        // them actually check out.        //        if (state < type && type != HandshakeMessage.ht_certificate_verify) {            state = type;        }    }      /*     * ClientHello presents the server with a bunch of options, to which the     * server replies with a ServerHello listing the ones which this session     * will use.  If needed, it also writes its Certificate plus in some cases     * a ServerKeyExchange message.  It may also write a CertificateRequest,     * to elicit a client certificate.     *     * All these messages are terminated by a ServerHelloDone message.  In     * most cases, all this can be sent in a single Record.     */    private void clientHello(ClientHello mesg) throws IOException {        if (debug != null && Debug.isOn("handshake")) {            mesg.print(System.out);        }         // Does the message include security renegotiation indication?        boolean renegotiationIndicated = false;         // check the TLS_EMPTY_RENEGOTIATION_INFO_SCSV        CipherSuiteList cipherSuites = mesg.getCipherSuites();        if (cipherSuites.contains(CipherSuite.C_SCSV)) {            renegotiationIndicated = true;            if (isInitialHandshake) {                secureRenegotiation = true;            } else {                // abort the handshake with a fatal handshake_failure alert                if (secureRenegotiation) {                    fatalSE(Alerts.alert_handshake_failure,                        "The SCSV is present in a secure renegotiation");                } else {                    fatalSE(Alerts.alert_handshake_failure,                        "The SCSV is present in a insecure renegotiation");                }            }        }         // check the "renegotiation_info" extension        RenegotiationInfoExtension clientHelloRI = (RenegotiationInfoExtension)                    mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO);        if (clientHelloRI != null) {            renegotiationIndicated = true;            if (isInitialHandshake) {                // verify the length of the "renegotiated_connection" field                if (!clientHelloRI.isEmpty()) {                    // abort the handshake with a fatal handshake_failure alert                    fatalSE(Alerts.alert_handshake_failure,                        "The renegotiation_info field is not empty");                }                 secureRenegotiation = true;            } else {                if (!secureRenegotiation) {                    // unexpected RI extension for insecure renegotiation,                    // abort the handshake with a fatal handshake_failure alert                    fatalSE(Alerts.alert_handshake_failure,                        "The renegotiation_info is present in a insecure " +                        "renegotiation");                }                 // verify the client_verify_data value                if (!Arrays.equals(clientVerifyData,                                clientHelloRI.getRenegotiatedConnection())) {                    fatalSE(Alerts.alert_handshake_failure,                        "Incorrect verify data in ClientHello " +                        "renegotiation_info message");                }            }        } else if (!isInitialHandshake && secureRenegotiation) {           // if the connection's "secure_renegotiation" flag is set to TRUE           // and the "renegotiation_info" extension is not present, abort           // the handshake.            fatalSE(Alerts.alert_handshake_failure,                        "Inconsistent secure renegotiation indication");        }         // if there is no security renegotiation indication or the previous        // handshake is insecure.        if (!renegotiationIndicated || !secureRenegotiation) {            if (isInitialHandshake) {                if (!allowLegacyHelloMessages) {                    // abort the handshake with a fatal handshake_failure alert                    fatalSE(Alerts.alert_handshake_failure,                        "Failed to negotiate the use of secure renegotiation");                }                 // continue with legacy ClientHello                if (debug != null && Debug.isOn("handshake")) {                    System.out.println("Warning: No renegotiation " +                        "indication in ClientHello, allow legacy ClientHello");                }            } else if (!allowUnsafeRenegotiation) {                // abort the handshake                if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {                    // response with a no_renegotiation warning,                    warningSE(Alerts.alert_no_renegotiation);                     // invalidate the handshake so that the caller can                    // dispose this object.                    invalidated = true;                     // If there is still unread block in the handshake                    // input stream, it would be truncated with the disposal                    // and the next handshake message will become incomplete.                    //                    // However, according to SSL/TLS specifications, no more                    // handshake message could immediately follow ClientHello                    // or HelloRequest. But in case of any improper messages,                    // we'd better check to ensure there is no remaining bytes                    // in the handshake input stream.                    if (input.available() > 0) {                        fatalSE(Alerts.alert_unexpected_message,                            "ClientHello followed by an unexpected  " +                            "handshake message");                    }                     return;                } else {                    // For SSLv3, send the handshake_failure fatal error.                    // Note that SSLv3 does not define a no_renegotiation                    // alert like TLSv1. However we cannot ignore the message                    // simply, otherwise the other side was waiting for a                    // response that would never come.                    fatalSE(Alerts.alert_handshake_failure,                        "Renegotiation is not allowed");                }            } else {   // !isInitialHandshake && allowUnsafeRenegotiation                // continue with unsafe renegotiation.                if (debug != null && Debug.isOn("handshake")) {                    System.out.println(                            "Warning: continue with insecure renegotiation");                }            }        }         /*         * Always make sure this entire record has been digested before we         * start emitting output, to ensure correct digesting order.         */        input.digestNow();         /*         * FIRST, construct the ServerHello using the options and priorities         * from the ClientHello.  Update the (pending) cipher spec as we do         * so, and save the client's version to protect against rollback         * attacks.         *         * There are a bunch of minor tasks here, and one major one: deciding         * if the short or the full handshake sequence will be used.         */        ServerHello m1 = new ServerHello();         clientRequestedVersion = mesg.protocolVersion;         // select a proper protocol version.        ProtocolVersion selectedVersion =               selectProtocolVersion(clientRequestedVersion);        if (selectedVersion == null ||                selectedVersion.v == ProtocolVersion.SSL20Hello.v) {            fatalSE(Alerts.alert_handshake_failure,                "Client requested protocol " + clientRequestedVersion +                " not enabled or not supported");        }         handshakeHash.protocolDetermined(selectedVersion);        setVersion(selectedVersion);         m1.protocolVersion = protocolVersion;         //        // random ... save client and server values for later use        // in computing the master secret (from pre-master secret)        // and thence the other crypto keys.        //        // NOTE:  this use of three inputs to generating _each_ set        // of ciphers slows things down, but it does increase the        // security since each connection in the session can hold        // its own authenticated (and strong) keys.  One could make        // creation of a session a rare thing...        //        clnt_random = mesg.clnt_random;        svr_random = new RandomCookie(sslContext.getSecureRandom());        m1.svr_random = svr_random;         session = null; // forget about the current session        //        // Here we go down either of two paths:  (a) the fast one, where        // the client's asked to rejoin an existing session, and the server        // permits this; (b) the other one, where a new session is created.        //        if (mesg.sessionId.length() != 0) {            // client is trying to resume a session, let's see...             SSLSessionImpl previous = ((SSLSessionContextImpl)sslContext                        .engineGetServerSessionContext())                        .get(mesg.sessionId.getId());            //            // Check if we can use the fast path, resuming a session.  We            // can do so iff we have a valid record for that session, and            // the cipher suite for that session was on the list which the            // client requested, and if we're not forgetting any needed            // authentication on the part of the client.            //            if (previous != null) {                resumingSession = previous.isRejoinable();                 if (resumingSession) {                    ProtocolVersion oldVersion = previous.getProtocolVersion();                    // cannot resume session with different version                    if (oldVersion != protocolVersion) {                        resumingSession = false;                    }                }                 if (resumingSession &&                        (doClientAuth == SSLEngineImpl.clauth_required)) {                    try {                        previous.getPeerPrincipal();                    } catch (SSLPeerUnverifiedException e) {                        resumingSession = false;                    }                }                 // validate subject identity                if (resumingSession) {                    CipherSuite suite = previous.getSuite();                    if (suite.keyExchange == K_KRB5 ||                        suite.keyExchange == K_KRB5_EXPORT) {                        Principal localPrincipal = previous.getLocalPrincipal();                         Subject subject = null;                        try {                            subject = AccessController.doPrivileged(                                new PrivilegedExceptionAction<Subject>() {                                public Subject run() throws Exception {                                    return                                        Krb5Helper.getServerSubject(getAccSE());                            }});

Lines 1–500 of 1678.