mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-02 04:00:16 +00:00
926 lines
40 KiB
Java
926 lines
40 KiB
Java
/*
|
|
* Copyright (c) 2015, 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.util.LinkedList;
|
|
import java.util.HashMap;
|
|
import javax.net.ssl.SSLProtocolException;
|
|
|
|
import sun.security.ssl.HandshakeMessage.*;
|
|
|
|
import static sun.security.ssl.CipherSuite.KeyExchange;
|
|
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
|
import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
|
|
import static sun.security.ssl.HandshakeMessage.*;
|
|
|
|
/*
|
|
* Handshake state manager.
|
|
*
|
|
* Messages flow for a full handshake:
|
|
*
|
|
* - -
|
|
* | HelloRequest (No.0, RFC 5246) [*] |
|
|
* | <-------------------------------------------- |
|
|
* | |
|
|
* | ClientHello (No.1, RFC 5246) |
|
|
* | --------------------------------------------> |
|
|
* | |
|
|
* | - HelloVerifyRequest (No.3, RFC 6347) - |
|
|
* | D | <-------------------------------------------- | D |
|
|
* | T | | T |
|
|
* | L | ClientHello (No.1, RFC 5246) | L |
|
|
* | S | --------------------------------------------> | S |
|
|
* | - - |
|
|
* | |
|
|
* C | ServerHello (No.2, RFC 5246) | S
|
|
* L | SupplementalData (No.23, RFC4680) [*] | E
|
|
* I | Certificate (No.11, RFC 5246) [*] | R
|
|
* E | CertificateStatus (No.22, RFC 6066) [*] | V
|
|
* N | ServerKeyExchange (No.12, RFC 5246) [*] | E
|
|
* T | CertificateRequest (No.13, RFC 5246) [*] | R
|
|
* | ServerHelloDone (No.14, RFC 5246) |
|
|
* | <-------------------------------------------- |
|
|
* | |
|
|
* | SupplementalData (No.23, RFC4680) [*] |
|
|
* | Certificate (No.11, RFC 5246) [*] Or |
|
|
* | CertificateURL (No.21, RFC6066) [*] |
|
|
* | ClientKeyExchange (No.16, RFC 5246) |
|
|
* | CertificateVerify (No.15, RFC 5246) [*] |
|
|
* | [ChangeCipherSpec] (RFC 5246) |
|
|
* | Finished (No.20, RFC 5246) |
|
|
* | --------------------------------------------> |
|
|
* | |
|
|
* | NewSessionTicket (No.4, RFC4507) [*] |
|
|
* | [ChangeCipherSpec] (RFC 5246) |
|
|
* | Finished (No.20, RFC 5246) |
|
|
* | <-------------------------------------------- |
|
|
* - -
|
|
* [*] Indicates optional or situation-dependent messages that are not
|
|
* always sent.
|
|
*
|
|
* Message flow for an abbreviated handshake:
|
|
* - -
|
|
* | ClientHello (No.1, RFC 5246) |
|
|
* | --------------------------------------------> |
|
|
* | |
|
|
* C | ServerHello (No.2, RFC 5246) | S
|
|
* L | NewSessionTicket (No.4, RFC4507) [*] | E
|
|
* I | [ChangeCipherSpec] (RFC 5246) | R
|
|
* E | Finished (No.20, RFC 5246) | V
|
|
* N | <-------------------------------------------- | E
|
|
* T | | R
|
|
* | [ChangeCipherSpec] (RFC 5246) |
|
|
* | Finished (No.20, RFC 5246) |
|
|
* | --------------------------------------------> |
|
|
* - -
|
|
*
|
|
*
|
|
* State machine of handshake states:
|
|
*
|
|
* +--------------+
|
|
* START -----> | HelloRequest |
|
|
* | +--------------+
|
|
* | |
|
|
* v v
|
|
* +---------------------+ --> +---------------------+
|
|
* | ClientHello | | HelloVerifyRequest |
|
|
* +---------------------+ <-- +---------------------+
|
|
* |
|
|
* |
|
|
* =========================================================================
|
|
* |
|
|
* v
|
|
* +---------------------+
|
|
* | ServerHello | ----------------------------------+------+
|
|
* +---------------------+ --> +-------------------------+ | |
|
|
* | | Server SupplementalData | | |
|
|
* | +-------------------------+ | |
|
|
* | | | |
|
|
* v v | |
|
|
* +---------------------+ | |
|
|
* +---- | Server Certificate | | |
|
|
* | +---------------------+ | |
|
|
* | | | |
|
|
* | | +--------------------+ | |
|
|
* | +-> | CertificateStatus | | |
|
|
* | | +--------------------+ v |
|
|
* | | | | +--------------------+ |
|
|
* | v v +--> | ServerKeyExchange | |
|
|
* | +---------------------+ | +--------------------+ |
|
|
* | | CertificateRequest | | | |
|
|
* | +---------------------+ <-+---------+ |
|
|
* | | | | |
|
|
* v v | | |
|
|
* +---------------------+ <-------+ | |
|
|
* | ServerHelloDone | <-----------------+ |
|
|
* +---------------------+ |
|
|
* | | |
|
|
* | | |
|
|
* | | |
|
|
* =========================================================================
|
|
* | | |
|
|
* | v |
|
|
* | +-------------------------+ |
|
|
* | | Client SupplementalData | --------------+ |
|
|
* | +-------------------------+ | |
|
|
* | | | |
|
|
* | v | |
|
|
* | +--------------------+ | |
|
|
* +-> | Client Certificate | ALT. | |
|
|
* | +--------------------+----------------+ | |
|
|
* | | CertificateURL | | |
|
|
* | +----------------+ | |
|
|
* v | |
|
|
* +-------------------+ <------------------------+ |
|
|
* | ClientKeyExchange | |
|
|
* +-------------------+ |
|
|
* | | |
|
|
* | v |
|
|
* | +-------------------+ |
|
|
* | | CertificateVerify | |
|
|
* | +-------------------+ |
|
|
* | | |
|
|
* v v |
|
|
* +-------------------------+ |
|
|
* | Client ChangeCipherSpec | <---------------+ |
|
|
* +-------------------------+ | |
|
|
* | | |
|
|
* v | |
|
|
* +-----------------+ (abbreviated) | |
|
|
* | Client Finished | -------------> END | |
|
|
* +-----------------+ (Abbreviated handshake) | |
|
|
* | | |
|
|
* | (full) | |
|
|
* | | |
|
|
* ================================ | |
|
|
* | | |
|
|
* | ================================
|
|
* | | |
|
|
* v | |
|
|
* +------------------+ | (abbreviated) |
|
|
* | NewSessionTicket | <--------------------------------+
|
|
* +------------------+ | |
|
|
* | | |
|
|
* v | |
|
|
* +-------------------------+ | (abbreviated) |
|
|
* | Server ChangeCipherSpec | <-------------------------------------+
|
|
* +-------------------------+ |
|
|
* | |
|
|
* v |
|
|
* +-----------------+ (abbreviated) |
|
|
* | Server Finished | -------------------------+
|
|
* +-----------------+
|
|
* | (full)
|
|
* v
|
|
* END (Full handshake)
|
|
*
|
|
*
|
|
* The scenarios of the use of this class:
|
|
* 1. Create an instance of HandshakeStateManager during the initializtion
|
|
* handshake.
|
|
* 2. If receiving a handshake message, call HandshakeStateManager.check()
|
|
* to make sure that the message is of the expected handshake type. And
|
|
* then call HandshakeStateManager.update() in case handshake states may
|
|
* be impacted by this new incoming handshake message.
|
|
* 3. On delivering a handshake message, call HandshakeStateManager.update()
|
|
* in case handshake states may by thie new outgoing handshake message.
|
|
* 4. On receiving and delivering ChangeCipherSpec message, call
|
|
* HandshakeStateManager.changeCipherSpec() to check the present sequence
|
|
* of this message, and update the states if necessary.
|
|
*/
|
|
final class HandshakeStateManager {
|
|
// upcoming handshake states.
|
|
private LinkedList<HandshakeState> upcomingStates;
|
|
private LinkedList<HandshakeState> alternatives;
|
|
|
|
private boolean isDTLS;
|
|
|
|
private final static boolean debugIsOn;
|
|
|
|
private final static HashMap<Byte, String> handshakeTypes;
|
|
|
|
static {
|
|
debugIsOn = (Handshaker.debug != null) &&
|
|
Debug.isOn("handshake") && Debug.isOn("verbose");
|
|
handshakeTypes = new HashMap<>(15);
|
|
|
|
handshakeTypes.put(ht_hello_request, "hello_request");
|
|
handshakeTypes.put(ht_client_hello, "client_hello");
|
|
handshakeTypes.put(ht_server_hello, "server_hello");
|
|
handshakeTypes.put(ht_hello_verify_request, "hello_verify_request");
|
|
handshakeTypes.put(ht_new_session_ticket, "session_ticket");
|
|
handshakeTypes.put(ht_certificate, "certificate");
|
|
handshakeTypes.put(ht_server_key_exchange, "server_key_exchange");
|
|
handshakeTypes.put(ht_certificate_request, "certificate_request");
|
|
handshakeTypes.put(ht_server_hello_done, "server_hello_done");
|
|
handshakeTypes.put(ht_certificate_verify, "certificate_verify");
|
|
handshakeTypes.put(ht_client_key_exchange, "client_key_exchange");
|
|
handshakeTypes.put(ht_finished, "finished");
|
|
handshakeTypes.put(ht_certificate_url, "certificate_url");
|
|
handshakeTypes.put(ht_certificate_status, "certificate_status");
|
|
handshakeTypes.put(ht_supplemental_data, "supplemental_data");
|
|
}
|
|
|
|
HandshakeStateManager(boolean isDTLS) {
|
|
this.upcomingStates = new LinkedList<>();
|
|
this.alternatives = new LinkedList<>();
|
|
this.isDTLS = isDTLS;
|
|
}
|
|
|
|
//
|
|
// enumation of handshake type
|
|
//
|
|
static enum HandshakeState {
|
|
HS_HELLO_REQUEST(
|
|
"hello_request",
|
|
HandshakeMessage.ht_hello_request),
|
|
HS_CLIENT_HELLO(
|
|
"client_hello",
|
|
HandshakeMessage.ht_client_hello),
|
|
HS_HELLO_VERIFY_REQUEST(
|
|
"hello_verify_request",
|
|
HandshakeMessage.ht_hello_verify_request),
|
|
HS_SERVER_HELLO(
|
|
"server_hello",
|
|
HandshakeMessage.ht_server_hello),
|
|
HS_SERVER_SUPPLEMENTAL_DATA(
|
|
"server supplemental_data",
|
|
HandshakeMessage.ht_supplemental_data, true),
|
|
HS_SERVER_CERTIFICATE(
|
|
"server certificate",
|
|
HandshakeMessage.ht_certificate),
|
|
HS_CERTIFICATE_STATUS(
|
|
"certificate_status",
|
|
HandshakeMessage.ht_certificate_status, true),
|
|
HS_SERVER_KEY_EXCHANGE(
|
|
"server_key_exchange",
|
|
HandshakeMessage.ht_server_key_exchange, true),
|
|
HS_CERTIFICATE_REQUEST(
|
|
"certificate_request",
|
|
HandshakeMessage.ht_certificate_request, true),
|
|
HS_SERVER_HELLO_DONE(
|
|
"server_hello_done",
|
|
HandshakeMessage.ht_server_hello_done),
|
|
HS_CLIENT_SUPPLEMENTAL_DATA(
|
|
"client supplemental_data",
|
|
HandshakeMessage.ht_supplemental_data, true),
|
|
HS_CLIENT_CERTIFICATE(
|
|
"client certificate",
|
|
HandshakeMessage.ht_certificate, true),
|
|
HS_CERTIFICATE_URL(
|
|
"certificate_url",
|
|
HandshakeMessage.ht_certificate_url, true),
|
|
HS_CLIENT_KEY_EXCHANGE(
|
|
"client_key_exchange",
|
|
HandshakeMessage.ht_client_key_exchange),
|
|
HS_CERTIFICATE_VERIFY(
|
|
"certificate_verify",
|
|
HandshakeMessage.ht_certificate_verify, true),
|
|
HS_CLIENT_CHANGE_CIPHER_SPEC(
|
|
"client change_cipher_spec",
|
|
HandshakeMessage.ht_not_applicable),
|
|
HS_CLEINT_FINISHED(
|
|
"client finished",
|
|
HandshakeMessage.ht_finished),
|
|
HS_NEW_SESSION_TICKET(
|
|
"session_ticket",
|
|
HandshakeMessage.ht_new_session_ticket),
|
|
HS_SERVER_CHANGE_CIPHER_SPEC(
|
|
"server change_cipher_spec",
|
|
HandshakeMessage.ht_not_applicable),
|
|
HS_SERVER_FINISHDE(
|
|
"server finished",
|
|
HandshakeMessage.ht_finished);
|
|
|
|
final String description;
|
|
final byte handshakeType;
|
|
final boolean isOptional;
|
|
|
|
HandshakeState(String description, byte handshakeType) {
|
|
this.description = description;
|
|
this.handshakeType = handshakeType;
|
|
this.isOptional = false;
|
|
}
|
|
|
|
HandshakeState(String description,
|
|
byte handshakeType, boolean isOptional) {
|
|
|
|
this.description = description;
|
|
this.handshakeType = handshakeType;
|
|
this.isOptional = isOptional;
|
|
}
|
|
|
|
public String toString() {
|
|
return description + "[" + handshakeType + "]" +
|
|
(isOptional ? "(optional)" : "");
|
|
}
|
|
}
|
|
|
|
boolean isEmpty() {
|
|
return upcomingStates.isEmpty();
|
|
}
|
|
|
|
void check(byte handshakeType) throws SSLProtocolException {
|
|
String exceptionMsg =
|
|
"Handshake message sequence violation, " + handshakeType;
|
|
|
|
if (debugIsOn) {
|
|
System.out.println(
|
|
"check handshake state: " + toString(handshakeType));
|
|
}
|
|
|
|
if (upcomingStates.isEmpty()) {
|
|
// Is it a kickstart message?
|
|
if ((handshakeType != HandshakeMessage.ht_hello_request) &&
|
|
(handshakeType != HandshakeMessage.ht_client_hello)) {
|
|
|
|
throw new SSLProtocolException(
|
|
"Handshake message sequence violation, " + handshakeType);
|
|
}
|
|
|
|
// It is a kickstart message.
|
|
return;
|
|
}
|
|
|
|
// Ignore the checking for HelloRequest messages as they are
|
|
// may be sent by the server at any time.
|
|
if (handshakeType == HandshakeMessage.ht_hello_request) {
|
|
return;
|
|
}
|
|
|
|
for (HandshakeState handshakeState : upcomingStates) {
|
|
if (handshakeState.handshakeType == handshakeType) {
|
|
// It's the expected next handshake type.
|
|
return;
|
|
}
|
|
|
|
if (handshakeState.isOptional) {
|
|
continue;
|
|
} else {
|
|
for (HandshakeState alternative : alternatives) {
|
|
if (alternative.handshakeType == handshakeType) {
|
|
return;
|
|
}
|
|
|
|
if (alternative.isOptional) {
|
|
continue;
|
|
} else {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// Not an expected Handshake message.
|
|
throw new SSLProtocolException(
|
|
"Handshake message sequence violation, " + handshakeType);
|
|
}
|
|
|
|
void update(HandshakeMessage handshakeMessage,
|
|
boolean isAbbreviated) throws SSLProtocolException {
|
|
|
|
byte handshakeType = (byte)handshakeMessage.messageType();
|
|
String exceptionMsg =
|
|
"Handshake message sequence violation, " + handshakeType;
|
|
|
|
if (debugIsOn) {
|
|
System.out.println(
|
|
"update handshake state: " + toString(handshakeType));
|
|
}
|
|
|
|
boolean hasPresentState = false;
|
|
switch (handshakeType) {
|
|
case HandshakeMessage.ht_hello_request:
|
|
//
|
|
// State machine:
|
|
// PRESENT: START
|
|
// TO : ClientHello
|
|
//
|
|
|
|
// No old state to update.
|
|
|
|
// Add the upcoming states.
|
|
if (!upcomingStates.isEmpty()) {
|
|
// A ClientHello message should be followed.
|
|
upcomingStates.add(HS_CLIENT_HELLO);
|
|
|
|
} // Otherwise, ignore this HelloRequest message.
|
|
|
|
break;
|
|
|
|
case HandshakeMessage.ht_client_hello:
|
|
//
|
|
// State machine:
|
|
// PRESENT: START
|
|
// HS_CLIENT_HELLO
|
|
// TO : HS_HELLO_VERIFY_REQUEST (DTLS)
|
|
// HS_SERVER_HELLO
|
|
//
|
|
|
|
// Check and update the present state.
|
|
if (!upcomingStates.isEmpty()) {
|
|
// The current state should be HS_CLIENT_HELLO.
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
if (handshakeState != HS_CLIENT_HELLO) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
}
|
|
|
|
// Add the upcoming states.
|
|
ClientHello clientHello = (ClientHello)handshakeMessage;
|
|
if (isDTLS) {
|
|
// Is it an initial ClientHello message?
|
|
if (clientHello.cookie == null ||
|
|
clientHello.cookie.length == 0) {
|
|
// Is it an abbreviated handshake?
|
|
if (clientHello.sessionId.length() != 0) {
|
|
// A HelloVerifyRequest message or a ServerHello
|
|
// message may follow the abbreviated session
|
|
// resuming handshake request.
|
|
upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
|
|
alternatives.add(HS_SERVER_HELLO);
|
|
} else {
|
|
// A HelloVerifyRequest message should follow
|
|
// the initial ClientHello message.
|
|
upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
|
|
}
|
|
} else {
|
|
// A HelloVerifyRequest may be followed if the cookie
|
|
// cannot be verified.
|
|
upcomingStates.add(HS_SERVER_HELLO);
|
|
alternatives.add(HS_HELLO_VERIFY_REQUEST);
|
|
}
|
|
} else {
|
|
upcomingStates.add(HS_SERVER_HELLO);
|
|
}
|
|
|
|
break;
|
|
|
|
case HandshakeMessage.ht_hello_verify_request:
|
|
//
|
|
// State machine:
|
|
// PRESENT: HS_HELLO_VERIFY_REQUEST
|
|
// TO : HS_CLIENT_HELLO
|
|
//
|
|
// Note that this state may have an alternative option.
|
|
|
|
// Check and update the present state.
|
|
if (!upcomingStates.isEmpty()) {
|
|
// The current state should be HS_HELLO_VERIFY_REQUEST.
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
HandshakeState alternative = null;
|
|
if (!alternatives.isEmpty()) {
|
|
alternative = alternatives.pop();
|
|
}
|
|
|
|
if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
|
|
(alternative != HS_HELLO_VERIFY_REQUEST)) {
|
|
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
} else {
|
|
// No present state.
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// Add the upcoming states.
|
|
upcomingStates.add(HS_CLIENT_HELLO);
|
|
|
|
break;
|
|
|
|
case HandshakeMessage.ht_server_hello:
|
|
//
|
|
// State machine:
|
|
// PRESENT: HS_SERVER_HELLO
|
|
// TO :
|
|
// Full handshake state stacks
|
|
// (ServerHello Flight)
|
|
// HS_SERVER_SUPPLEMENTAL_DATA [optional]
|
|
// --> HS_SERVER_CERTIFICATE [optional]
|
|
// --> HS_CERTIFICATE_STATUS [optional]
|
|
// --> HS_SERVER_KEY_EXCHANGE [optional]
|
|
// --> HS_CERTIFICATE_REQUEST [optional]
|
|
// --> HS_SERVER_HELLO_DONE
|
|
// (Client ClientKeyExchange Flight)
|
|
// --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
|
|
// --> HS_CLIENT_CERTIFICATE or
|
|
// HS_CERTIFICATE_URL
|
|
// --> HS_CLIENT_KEY_EXCHANGE
|
|
// --> HS_CERTIFICATE_VERIFY [optional]
|
|
// --> HS_CLIENT_CHANGE_CIPHER_SPEC
|
|
// --> HS_CLEINT_FINISHED
|
|
// (Server Finished Flight)
|
|
// --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
|
|
//
|
|
// Abbreviated handshake state stacks
|
|
// (Server Finished Flight)
|
|
// HS_NEW_SESSION_TICKET
|
|
// --> HS_SERVER_CHANGE_CIPHER_SPEC
|
|
// --> HS_SERVER_FINISHDE
|
|
// (Client Finished Flight)
|
|
// --> HS_CLIENT_CHANGE_CIPHER_SPEC
|
|
// --> HS_CLEINT_FINISHED
|
|
//
|
|
// Note that this state may have an alternative option.
|
|
|
|
// Check and update the present state.
|
|
if (!upcomingStates.isEmpty()) {
|
|
// The current state should be HS_SERVER_HELLO
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
HandshakeState alternative = null;
|
|
if (!alternatives.isEmpty()) {
|
|
alternative = alternatives.pop();
|
|
}
|
|
|
|
if ((handshakeState != HS_SERVER_HELLO) &&
|
|
(alternative != HS_SERVER_HELLO)) {
|
|
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
} else {
|
|
// No present state.
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// Add the upcoming states.
|
|
ServerHello serverHello = (ServerHello)handshakeMessage;
|
|
HelloExtensions hes = serverHello.extensions;
|
|
|
|
|
|
// Not support SessionTicket extension yet.
|
|
//
|
|
// boolean hasSessionTicketExt =
|
|
// (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
|
|
|
|
if (isAbbreviated) {
|
|
// Not support SessionTicket extension yet.
|
|
//
|
|
// // Mandatory NewSessionTicket message
|
|
// if (hasSessionTicketExt) {
|
|
// upcomingStates.add(HS_NEW_SESSION_TICKET);
|
|
// }
|
|
|
|
// Mandatory server ChangeCipherSpec and Finished messages
|
|
upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
|
|
upcomingStates.add(HS_SERVER_FINISHDE);
|
|
|
|
// Mandatory client ChangeCipherSpec and Finished messages
|
|
upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
|
|
upcomingStates.add(HS_CLEINT_FINISHED);
|
|
} else {
|
|
// Not support SupplementalData extension yet.
|
|
//
|
|
// boolean hasSupplementalDataExt =
|
|
// (hes.get(HandshakeMessage.ht_supplemental_data) != null);
|
|
|
|
// Not support CertificateStatus extension yet.
|
|
//
|
|
// boolean hasCertificateStatusExt =
|
|
// (hes.get(HandshakeMessage.ht_certificate_status) != null);
|
|
|
|
// Not support CertificateURL extension yet.
|
|
//
|
|
// boolean hasCertificateUrlExt =
|
|
// (hes.get(HandshakeMessage.ht_certificate_url) != null);
|
|
|
|
// Not support SupplementalData extension yet.
|
|
//
|
|
// // Optional SupplementalData message
|
|
// if (hasSupplementalDataExt) {
|
|
// upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
|
|
// }
|
|
|
|
// Need server Certificate message or not?
|
|
KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
|
|
if ((keyExchange != K_KRB5) &&
|
|
(keyExchange != K_KRB5_EXPORT) &&
|
|
(keyExchange != K_DH_ANON) &&
|
|
(keyExchange != K_ECDH_ANON)) {
|
|
// Mandatory Certificate message
|
|
upcomingStates.add(HS_SERVER_CERTIFICATE);
|
|
}
|
|
|
|
// Not support CertificateStatus extension yet.
|
|
//
|
|
// // Optional CertificateStatus message
|
|
// if (hasCertificateStatusExt) {
|
|
// upcomingStates.add(HS_CERTIFICATE_STATUS);
|
|
// }
|
|
|
|
// Need ServerKeyExchange message or not?
|
|
if ((keyExchange == K_RSA_EXPORT) ||
|
|
(keyExchange == K_DHE_RSA) ||
|
|
(keyExchange == K_DHE_DSS) ||
|
|
(keyExchange == K_DH_ANON) ||
|
|
(keyExchange == K_ECDHE_RSA) ||
|
|
(keyExchange == K_ECDHE_ECDSA) ||
|
|
(keyExchange == K_ECDH_ANON)) {
|
|
// Optional ServerKeyExchange message
|
|
upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
|
|
}
|
|
|
|
// Optional CertificateRequest message
|
|
upcomingStates.add(HS_CERTIFICATE_REQUEST);
|
|
|
|
// Mandatory ServerHelloDone message
|
|
upcomingStates.add(HS_SERVER_HELLO_DONE);
|
|
|
|
// Not support SupplementalData extension yet.
|
|
//
|
|
// // Optional SupplementalData message
|
|
// if (hasSupplementalDataExt) {
|
|
// upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
|
|
// }
|
|
|
|
// Optional client Certificate message
|
|
upcomingStates.add(HS_CLIENT_CERTIFICATE);
|
|
|
|
// Not support CertificateURL extension yet.
|
|
//
|
|
// // Alternative CertificateURL message, optional too.
|
|
// //
|
|
// // Please put CertificateURL rather than Certificate
|
|
// // message in the alternatives list. So that we can
|
|
// // simplify the process of this alternative pair later.
|
|
// if (hasCertificateUrlExt) {
|
|
// alternatives.add(HS_CERTIFICATE_URL);
|
|
// }
|
|
|
|
// Mandatory ClientKeyExchange message
|
|
upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
|
|
|
|
// Optional CertificateVerify message
|
|
upcomingStates.add(HS_CERTIFICATE_VERIFY);
|
|
|
|
// Mandatory client ChangeCipherSpec and Finished messages
|
|
upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
|
|
upcomingStates.add(HS_CLEINT_FINISHED);
|
|
|
|
// Not support SessionTicket extension yet.
|
|
//
|
|
// // Mandatory NewSessionTicket message
|
|
// if (hasSessionTicketExt) {
|
|
// upcomingStates.add(HS_NEW_SESSION_TICKET);
|
|
// }
|
|
|
|
// Mandatory server ChangeCipherSpec and Finished messages
|
|
upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
|
|
upcomingStates.add(HS_SERVER_FINISHDE);
|
|
}
|
|
|
|
break;
|
|
|
|
case HandshakeMessage.ht_certificate:
|
|
//
|
|
// State machine:
|
|
// PRESENT: HS_CERTIFICATE_URL or
|
|
// HS_CLIENT_CERTIFICATE
|
|
// TO : HS_CLIENT_KEY_EXCHANGE
|
|
//
|
|
// Or
|
|
//
|
|
// PRESENT: HS_SERVER_CERTIFICATE
|
|
// TO : HS_CERTIFICATE_STATUS [optional]
|
|
// HS_SERVER_KEY_EXCHANGE [optional]
|
|
// HS_CERTIFICATE_REQUEST [optional]
|
|
// HS_SERVER_HELLO_DONE
|
|
//
|
|
// Note that this state may have an alternative option.
|
|
|
|
// Check and update the present state.
|
|
while (!upcomingStates.isEmpty()) {
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
if (handshakeState.handshakeType == handshakeType) {
|
|
hasPresentState = true;
|
|
|
|
// The current state should be HS_CLIENT_CERTIFICATE or
|
|
// HS_SERVER_CERTIFICATE.
|
|
//
|
|
// Note that we won't put HS_CLIENT_CERTIFICATE into
|
|
// the alternative list.
|
|
if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
|
|
(handshakeState != HS_SERVER_CERTIFICATE)) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// Is it an expected client Certificate message?
|
|
boolean isClientMessage = false;
|
|
if (!upcomingStates.isEmpty()) {
|
|
// If the next expected message is ClientKeyExchange,
|
|
// this one should be an expected client Certificate
|
|
// message.
|
|
HandshakeState nextState = upcomingStates.getFirst();
|
|
if (nextState == HS_CLIENT_KEY_EXCHANGE) {
|
|
isClientMessage = true;
|
|
}
|
|
}
|
|
|
|
if (isClientMessage) {
|
|
if (handshakeState != HS_CLIENT_CERTIFICATE) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// Not support CertificateURL extension yet.
|
|
/*******************************************
|
|
// clear up the alternatives list
|
|
if (!alternatives.isEmpty()) {
|
|
HandshakeState alternative = alternatives.pop();
|
|
|
|
if (alternative != HS_CERTIFICATE_URL) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
}
|
|
********************************************/
|
|
} else {
|
|
if ((handshakeState != HS_SERVER_CERTIFICATE)) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
}
|
|
|
|
break;
|
|
} else if (!handshakeState.isOptional) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
} // Otherwise, looking for next state track.
|
|
}
|
|
|
|
// No present state.
|
|
if (!hasPresentState) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// no new upcoming states.
|
|
|
|
break;
|
|
|
|
// Not support CertificateURL extension yet.
|
|
/*************************************************/
|
|
case HandshakeMessage.ht_certificate_url:
|
|
//
|
|
// State machine:
|
|
// PRESENT: HS_CERTIFICATE_URL or
|
|
// HS_CLIENT_CERTIFICATE
|
|
// TO : HS_CLIENT_KEY_EXCHANGE
|
|
//
|
|
// Note that this state may have an alternative option.
|
|
|
|
// Check and update the present state.
|
|
while (!upcomingStates.isEmpty()) {
|
|
// The current state should be HS_CLIENT_CERTIFICATE.
|
|
//
|
|
// Note that we won't put HS_CLIENT_CERTIFICATE into
|
|
// the alternative list.
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
if (handshakeState.handshakeType ==
|
|
HS_CLIENT_CERTIFICATE.handshakeType) {
|
|
hasPresentState = true;
|
|
|
|
// Look for HS_CERTIFICATE_URL state track.
|
|
if (!alternatives.isEmpty()) {
|
|
HandshakeState alternative = alternatives.pop();
|
|
|
|
if (alternative != HS_CERTIFICATE_URL) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
} else {
|
|
// No alternative CertificateUR state track.
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
break;
|
|
} else if (!handshakeState.isOptional) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
} // Otherwise, looking for next state track.
|
|
|
|
}
|
|
|
|
// No present state.
|
|
if (!hasPresentState) {
|
|
// No present state.
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// no new upcoming states.
|
|
|
|
break;
|
|
/*************************************************/
|
|
|
|
default:
|
|
// Check and update the present state.
|
|
while (!upcomingStates.isEmpty()) {
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
if (handshakeState.handshakeType == handshakeType) {
|
|
hasPresentState = true;
|
|
break;
|
|
} else if (!handshakeState.isOptional) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
} // Otherwise, looking for next state track.
|
|
}
|
|
|
|
// No present state.
|
|
if (!hasPresentState) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// no new upcoming states.
|
|
}
|
|
|
|
if (debugIsOn) {
|
|
for (HandshakeState handshakeState : upcomingStates) {
|
|
System.out.println(
|
|
"upcoming handshake states: " + handshakeState);
|
|
}
|
|
for (HandshakeState handshakeState : alternatives) {
|
|
System.out.println(
|
|
"upcoming handshake alternative state: " + handshakeState);
|
|
}
|
|
}
|
|
}
|
|
|
|
void changeCipherSpec(boolean isInput,
|
|
boolean isClient) throws SSLProtocolException {
|
|
|
|
if (debugIsOn) {
|
|
System.out.println(
|
|
"update handshake state: change_cipher_spec");
|
|
}
|
|
|
|
String exceptionMsg = "ChangeCipherSpec message sequence violation";
|
|
|
|
HandshakeState expectedState;
|
|
if ((isClient && isInput) || (!isClient && !isInput)) {
|
|
expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
|
|
} else {
|
|
expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
|
|
}
|
|
|
|
boolean hasPresentState = false;
|
|
|
|
// Check and update the present state.
|
|
while (!upcomingStates.isEmpty()) {
|
|
HandshakeState handshakeState = upcomingStates.pop();
|
|
if (handshakeState == expectedState) {
|
|
hasPresentState = true;
|
|
break;
|
|
} else if (!handshakeState.isOptional) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
} // Otherwise, looking for next state track.
|
|
}
|
|
|
|
// No present state.
|
|
if (!hasPresentState) {
|
|
throw new SSLProtocolException(exceptionMsg);
|
|
}
|
|
|
|
// no new upcoming states.
|
|
|
|
if (debugIsOn) {
|
|
for (HandshakeState handshakeState : upcomingStates) {
|
|
System.out.println(
|
|
"upcoming handshake states: " + handshakeState);
|
|
}
|
|
for (HandshakeState handshakeState : alternatives) {
|
|
System.out.println(
|
|
"upcoming handshake alternative state: " + handshakeState);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static String toString(byte handshakeType) {
|
|
String s = handshakeTypes.get(handshakeType);
|
|
if (s == null) {
|
|
s = "unknown";
|
|
}
|
|
return (s + "[" + handshakeType + "]");
|
|
}
|
|
}
|
|
|