--- old/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java 2018-05-11 15:09:59.897506000 -0700 +++ /dev/null 2018-05-11 10:42:23.849000000 -0700 @@ -1,2430 +0,0 @@ -/* - * Copyright (c) 1996, 2017, 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.math.BigInteger; -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; -import java.security.cert.*; -import java.security.cert.Certificate; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import java.lang.reflect.*; - -import javax.security.auth.x500.X500Principal; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.crypto.spec.DHPublicKeySpec; - -import javax.net.ssl.*; - -import sun.security.internal.spec.TlsPrfParameterSpec; -import sun.security.ssl.CipherSuite.*; -import static sun.security.ssl.CipherSuite.PRF.*; -import sun.security.util.KeyUtil; -import sun.security.util.MessageDigestSpi2; -import sun.security.provider.certpath.OCSPResponse; - -/** - * Many data structures are involved in the handshake messages. These - * classes are used as structures, with public data members. They are - * not visible outside the SSL package. - * - * Handshake messages all have a common header format, and they are all - * encoded in a "handshake data" SSL record substream. The base class - * here (HandshakeMessage) provides a common framework and records the - * SSL record type of the particular handshake message. - * - * This file contains subclasses for all the basic handshake messages. - * All handshake messages know how to encode and decode themselves on - * SSL streams; this facilitates using the same code on SSL client and - * server sides, although they don't send and receive the same messages. - * - * Messages also know how to print themselves, which is quite handy - * for debugging. They always identify their type, and can optionally - * dump all of their content. - * - * @author David Brownell - */ -public abstract class HandshakeMessage { - - /* Class and subclass dynamic debugging support */ - public static final Debug debug = Debug.getInstance("ssl"); - - // enum HandshakeType: - // - // Please update the isUnsupported() method accordingly if the handshake - // types get updated in the future. - static final byte ht_hello_request = 0; // RFC 5246 - static final byte ht_client_hello = 1; // RFC 5246 - static final byte ht_server_hello = 2; // RFC 5246 - static final byte ht_hello_verify_request = 3; // RFC 6347 - static final byte ht_new_session_ticket = 4; // RFC 4507 - - static final byte ht_certificate = 11; // RFC 5246 - static final byte ht_server_key_exchange = 12; // RFC 5246 - static final byte ht_certificate_request = 13; // RFC 5246 - static final byte ht_server_hello_done = 14; // RFC 5246 - static final byte ht_certificate_verify = 15; // RFC 5246 - static final byte ht_client_key_exchange = 16; // RFC 5246 - - static final byte ht_finished = 20; // RFC 5246 - static final byte ht_certificate_url = 21; // RFC 6066 - static final byte ht_certificate_status = 22; // RFC 6066 - static final byte ht_supplemental_data = 23; // RFC 4680 - - static final byte ht_not_applicable = -1; // N/A - - /* - * SSL 3.0 MAC padding constants. - * Also used by CertificateVerify and Finished during the handshake. - */ - static final byte[] MD5_pad1 = genPad(0x36, 48); - static final byte[] MD5_pad2 = genPad(0x5c, 48); - - static final byte[] SHA_pad1 = genPad(0x36, 40); - static final byte[] SHA_pad2 = genPad(0x5c, 40); - - // default constructor - HandshakeMessage() { - } - - /** - * Utility method to convert a BigInteger to a byte array in unsigned - * format as needed in the handshake messages. BigInteger uses - * 2's complement format, i.e. it prepends an extra zero if the MSB - * is set. We remove that. - */ - static byte[] toByteArray(BigInteger bi) { - byte[] b = bi.toByteArray(); - if ((b.length > 1) && (b[0] == 0)) { - int n = b.length - 1; - byte[] newarray = new byte[n]; - System.arraycopy(b, 1, newarray, 0, n); - b = newarray; - } - return b; - } - - static boolean isUnsupported(byte handshakeType) { - return (handshakeType != ht_hello_request) && - (handshakeType != ht_client_hello) && - (handshakeType != ht_server_hello) && - (handshakeType != ht_hello_verify_request) && - (handshakeType != ht_new_session_ticket) && - (handshakeType != ht_certificate) && - (handshakeType != ht_server_key_exchange) && - (handshakeType != ht_certificate_request) && - (handshakeType != ht_server_hello_done) && - (handshakeType != ht_certificate_verify) && - (handshakeType != ht_client_key_exchange) && - (handshakeType != ht_finished) && - (handshakeType != ht_certificate_url) && - (handshakeType != ht_certificate_status) && - (handshakeType != ht_supplemental_data); - } - - private static byte[] genPad(int b, int count) { - byte[] padding = new byte[count]; - Arrays.fill(padding, (byte)b); - return padding; - } - - /* - * Write a handshake message on the (handshake) output stream. - * This is just a four byte header followed by the data. - * - * NOTE that huge messages -- notably, ones with huge cert - * chains -- are handled correctly. - */ - final void write(HandshakeOutStream s) throws IOException { - int len = messageLength(); - if (len >= Record.OVERFLOW_OF_INT24) { - throw new SSLException("Handshake message too big" - + ", type = " + messageType() + ", len = " + len); - } - s.write(messageType()); - s.putInt24(len); - send(s); - s.complete(); - } - - /* - * Subclasses implement these methods so those kinds of - * messages can be emitted. Base class delegates to subclass. - */ - abstract int messageType(); - abstract int messageLength(); - abstract void send(HandshakeOutStream s) throws IOException; - - /* - * Write a descriptive message on the output stream; for debugging. - */ - abstract void print(PrintStream p) throws IOException; - -// -// NOTE: the rest of these classes are nested within this one, and are -// imported by other classes in this package. There are a few other -// handshake message classes, not neatly nested here because of current -// licensing requirement for native (RSA) methods. They belong here, -// but those native methods complicate things a lot! -// - - -/* - * HelloRequest ... SERVER --> CLIENT - * - * Server can ask the client to initiate a new handshake, e.g. to change - * session parameters after a connection has been (re)established. - */ -static final class HelloRequest extends HandshakeMessage { - @Override - int messageType() { return ht_hello_request; } - - HelloRequest() { } - - HelloRequest(HandshakeInStream in) throws IOException - { - // nothing in this message - } - - @Override - int messageLength() { return 0; } - - @Override - void send(HandshakeOutStream out) throws IOException - { - // nothing in this messaage - } - - @Override - void print(PrintStream out) throws IOException - { - out.println("*** HelloRequest (empty)"); - } - -} - -/* - * HelloVerifyRequest ... SERVER --> CLIENT [DTLS only] - * - * The definition of HelloVerifyRequest is as follows: - * - * struct { - * ProtocolVersion server_version; - * opaque cookie<0..2^8-1>; - * } HelloVerifyRequest; - * - * For DTLS protocols, once the client has transmitted the ClientHello message, - * it expects to see a HelloVerifyRequest from the server. However, if the - * server's message is lost, the client knows that either the ClientHello or - * the HelloVerifyRequest has been lost and retransmits. [RFC 6347] - */ -static final class HelloVerifyRequest extends HandshakeMessage { - ProtocolVersion protocolVersion; - byte[] cookie; // 1 to 2^8 - 1 bytes - - HelloVerifyRequest(HelloCookieManager helloCookieManager, - ClientHello clientHelloMsg) { - - this.protocolVersion = clientHelloMsg.protocolVersion; - this.cookie = helloCookieManager.getCookie(clientHelloMsg); - } - - HelloVerifyRequest( - HandshakeInStream input, int messageLength) throws IOException { - - this.protocolVersion = - ProtocolVersion.valueOf(input.getInt8(), input.getInt8()); - this.cookie = input.getBytes8(); - - // Is it a valid cookie? - HelloCookieManager.checkCookie(protocolVersion, cookie); - } - - @Override - int messageType() { - return ht_hello_verify_request; - } - - @Override - int messageLength() { - return 2 + cookie.length; // 2: the length of protocolVersion - } - - @Override - void send(HandshakeOutStream hos) throws IOException { - hos.putInt8(protocolVersion.major); - hos.putInt8(protocolVersion.minor); - hos.putBytes8(cookie); - } - - @Override - void print(PrintStream out) throws IOException { - out.println("*** HelloVerifyRequest"); - if (debug != null && Debug.isOn("verbose")) { - out.println("server_version: " + protocolVersion); - Debug.println(out, "cookie", cookie); - } - } -} - -/* - * ClientHello ... CLIENT --> SERVER - * - * Client initiates handshake by telling server what it wants, and what it - * can support (prioritized by what's first in the ciphe suite list). - * - * By RFC2246:7.4.1.2 it's explicitly anticipated that this message - * will have more data added at the end ... e.g. what CAs the client trusts. - * Until we know how to parse it, we will just read what we know - * about, and let our caller handle the jumps over unknown data. - */ -static final class ClientHello extends HandshakeMessage { - - ProtocolVersion protocolVersion; - RandomCookie clnt_random; - SessionId sessionId; - byte[] cookie; // DTLS only - private CipherSuiteList cipherSuites; - private final boolean isDTLS; - byte[] compression_methods; - - HelloExtensions extensions = new HelloExtensions(); - - private static final byte[] NULL_COMPRESSION = new byte[] {0}; - - ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, - SessionId sessionId, CipherSuiteList cipherSuites, - boolean isDTLS) { - - this.isDTLS = isDTLS; - this.protocolVersion = protocolVersion; - this.sessionId = sessionId; - this.cipherSuites = cipherSuites; - if (isDTLS) { - this.cookie = new byte[0]; - } else { - this.cookie = null; - } - - clnt_random = new RandomCookie(generator); - compression_methods = NULL_COMPRESSION; - } - - ClientHello(HandshakeInStream s, - int messageLength, boolean isDTLS) throws IOException { - - this.isDTLS = isDTLS; - - protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8()); - clnt_random = new RandomCookie(s); - sessionId = new SessionId(s.getBytes8()); - sessionId.checkLength(protocolVersion); - if (isDTLS) { - cookie = s.getBytes8(); - } else { - cookie = null; - } - - cipherSuites = new CipherSuiteList(s); - compression_methods = s.getBytes8(); - if (messageLength() != messageLength) { - extensions = new HelloExtensions(s); - } - } - - CipherSuiteList getCipherSuites() { - return cipherSuites; - } - - // add renegotiation_info extension - void addRenegotiationInfoExtension(byte[] clientVerifyData) { - HelloExtension renegotiationInfo = new RenegotiationInfoExtension( - clientVerifyData, new byte[0]); - extensions.add(renegotiationInfo); - } - - // add server_name extension - void addSNIExtension(List serverNames) { - try { - extensions.add(new ServerNameExtension(serverNames)); - } catch (IOException ioe) { - // ignore the exception and return - } - } - - // add signature_algorithm extension - void addSignatureAlgorithmsExtension( - Collection algorithms) { - HelloExtension signatureAlgorithm = - new SignatureAlgorithmsExtension(algorithms); - extensions.add(signatureAlgorithm); - } - - void addExtendedMasterSecretExtension() { - extensions.add(new ExtendedMasterSecretExtension()); - } - - void addMFLExtension(int maximumPacketSize) { - HelloExtension maxFragmentLength = - new MaxFragmentLengthExtension(maximumPacketSize); - extensions.add(maxFragmentLength); - } - - void updateHelloCookie(MessageDigest cookieDigest) { - // - // Just use HandshakeOutStream to compute the hello verify cookie. - // Not actually used to output handshake message records. - // - HandshakeOutStream hos = new HandshakeOutStream(null); - - try { - send(hos, false); // Do not count hello verify cookie. - } catch (IOException ioe) { - // unlikely to happen - } - - cookieDigest.update(hos.toByteArray()); - } - - // Add status_request extension type - void addCertStatusRequestExtension() { - extensions.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest())); - } - - // Add status_request_v2 extension type - void addCertStatusReqListV2Extension() { - // Create a default OCSPStatusRequest that we can use for both - // OCSP_MULTI and OCSP request list items. - OCSPStatusRequest osr = new OCSPStatusRequest(); - List itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - osr)); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); - extensions.add(new CertStatusReqListV2Extension(itemList)); - } - - // add application_layer_protocol_negotiation extension - void addALPNExtension(String[] applicationProtocols) throws SSLException { - extensions.add(new ALPNExtension(applicationProtocols)); - } - - @Override - int messageType() { return ht_client_hello; } - - @Override - int messageLength() { - /* - * Add fixed size parts of each field... - * version + random + session + cipher + compress - */ - return (2 + 32 + 1 + 2 + 1 - + sessionId.length() /* ... + variable parts */ - + (isDTLS ? (1 + cookie.length) : 0) - + (cipherSuites.size() * 2) - + compression_methods.length) - + extensions.length(); - } - - @Override - void send(HandshakeOutStream s) throws IOException { - send(s, true); // Count hello verify cookie. - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** ClientHello, " + protocolVersion); - - if (debug != null && Debug.isOn("verbose")) { - s.print("RandomCookie: "); - clnt_random.print(s); - - s.print("Session ID: "); - s.println(sessionId); - - if (isDTLS) { - Debug.println(s, "cookie", cookie); - } - - s.println("Cipher Suites: " + cipherSuites); - - Debug.println(s, "Compression Methods", compression_methods); - extensions.print(s); - s.println("***"); - } - } - - private void send(HandshakeOutStream s, - boolean computeCookie) throws IOException { - s.putInt8(protocolVersion.major); - s.putInt8(protocolVersion.minor); - clnt_random.send(s); - s.putBytes8(sessionId.getId()); - if (isDTLS && computeCookie) { - s.putBytes8(cookie); - } - cipherSuites.send(s); - s.putBytes8(compression_methods); - extensions.send(s); - } - -} - -/* - * ServerHello ... SERVER --> CLIENT - * - * Server chooses protocol options from among those it supports and the - * client supports. Then it sends the basic session descriptive parameters - * back to the client. - */ -static final -class ServerHello extends HandshakeMessage -{ - @Override - int messageType() { return ht_server_hello; } - - ProtocolVersion protocolVersion; - RandomCookie svr_random; - SessionId sessionId; - CipherSuite cipherSuite; - byte compression_method; - HelloExtensions extensions = new HelloExtensions(); - - ServerHello() { - // empty - } - - ServerHello(HandshakeInStream input, int messageLength) - throws IOException { - protocolVersion = ProtocolVersion.valueOf(input.getInt8(), - input.getInt8()); - svr_random = new RandomCookie(input); - sessionId = new SessionId(input.getBytes8()); - sessionId.checkLength(protocolVersion); - cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8()); - compression_method = (byte)input.getInt8(); - if (messageLength() != messageLength) { - extensions = new HelloExtensions(input); - } - } - - @Override - int messageLength() - { - // almost fixed size, except session ID and extensions: - // major + minor = 2 - // random = 32 - // session ID len field = 1 - // cipher suite + compression = 3 - // extensions: if present, 2 + length of extensions - return 38 + sessionId.length() + extensions.length(); - } - - @Override - void send(HandshakeOutStream s) throws IOException - { - s.putInt8(protocolVersion.major); - s.putInt8(protocolVersion.minor); - svr_random.send(s); - s.putBytes8(sessionId.getId()); - s.putInt8(cipherSuite.id >> 8); - s.putInt8(cipherSuite.id & 0xff); - s.putInt8(compression_method); - extensions.send(s); - } - - @Override - void print(PrintStream s) throws IOException - { - s.println("*** ServerHello, " + protocolVersion); - - if (debug != null && Debug.isOn("verbose")) { - s.print("RandomCookie: "); - svr_random.print(s); - - s.print("Session ID: "); - s.println(sessionId); - - s.println("Cipher Suite: " + cipherSuite); - s.println("Compression Method: " + compression_method); - extensions.print(s); - s.println("***"); - } - } -} - - -/* - * CertificateMsg ... send by both CLIENT and SERVER - * - * Each end of a connection may need to pass its certificate chain to - * the other end. Such chains are intended to validate an identity with - * reference to some certifying authority. Examples include companies - * like Verisign, or financial institutions. There's some control over - * the certifying authorities which are sent. - * - * NOTE: that these messages might be huge, taking many handshake records. - * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14 - * bytes each ... up to 2^32 records sent on the output stream. - */ -static final -class CertificateMsg extends HandshakeMessage -{ - @Override - int messageType() { return ht_certificate; } - - private X509Certificate[] chain; - - private List encodedChain; - - private int messageLength; - - CertificateMsg(X509Certificate[] certs) { - chain = certs; - } - - CertificateMsg(HandshakeInStream input) throws IOException { - int chainLen = input.getInt24(); - List v = new ArrayList<>(4); - - CertificateFactory cf = null; - while (chainLen > 0) { - byte[] cert = input.getBytes24(); - chainLen -= (3 + cert.length); - try { - if (cf == null) { - cf = CertificateFactory.getInstance("X.509"); - } - v.add(cf.generateCertificate(new ByteArrayInputStream(cert))); - } catch (CertificateException e) { - throw (SSLProtocolException)new SSLProtocolException( - e.getMessage()).initCause(e); - } - } - - chain = v.toArray(new X509Certificate[v.size()]); - } - - @Override - int messageLength() { - if (encodedChain == null) { - messageLength = 3; - encodedChain = new ArrayList(chain.length); - try { - for (X509Certificate cert : chain) { - byte[] b = cert.getEncoded(); - encodedChain.add(b); - messageLength += b.length + 3; - } - } catch (CertificateEncodingException e) { - encodedChain = null; - throw new RuntimeException("Could not encode certificates", e); - } - } - return messageLength; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt24(messageLength() - 3); - for (byte[] b : encodedChain) { - s.putBytes24(b); - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** Certificate chain"); - - if (chain.length == 0) { - s.println(""); - } else if (debug != null && Debug.isOn("verbose")) { - for (int i = 0; i < chain.length; i++) { - s.println("chain [" + i + "] = " + chain[i]); - } - } - s.println("***"); - } - - X509Certificate[] getCertificateChain() { - return chain.clone(); - } -} - -/* - * CertificateStatus ... SERVER --> CLIENT - * - * When a ClientHello asserting the status_request or status_request_v2 - * extensions is accepted by the server, it will fetch and return one - * or more status responses in this handshake message. - * - * NOTE: Like the Certificate handshake message, this can potentially - * be a very large message both due to the size of multiple status - * responses and the certificate chains that are often attached to them. - * Up to 2^24 bytes of status responses may be sent, possibly fragmented - * over multiple TLS records. - */ -static final class CertificateStatus extends HandshakeMessage -{ - private final StatusRequestType statusType; - private int encodedResponsesLen; - private int messageLength = -1; - private List encodedResponses; - - @Override - int messageType() { return ht_certificate_status; } - - /** - * Create a CertificateStatus message from the certificates and their - * respective OCSP responses - * - * @param type an indication of the type of response (OCSP or OCSP_MULTI) - * @param responses a {@code List} of OCSP responses in DER-encoded form. - * For the OCSP type, only the first entry in the response list is - * used, and must correspond to the end-entity certificate sent to the - * peer. Zero-length or null values for the response data are not - * allowed for the OCSP type. For the OCSP_MULTI type, each entry in - * the list should match its corresponding certificate sent in the - * Server Certificate message. Where an OCSP response does not exist, - * either a zero-length array or a null value should be used. - * - * @throws SSLException if an unsupported StatusRequestType or invalid - * OCSP response data is provided. - */ - CertificateStatus(StatusRequestType type, X509Certificate[] chain, - Map responses) { - statusType = type; - encodedResponsesLen = 0; - encodedResponses = new ArrayList<>(chain.length); - - Objects.requireNonNull(chain, "Null chain not allowed"); - Objects.requireNonNull(responses, "Null responses not allowed"); - - if (statusType == StatusRequestType.OCSP) { - // Just get the response for the end-entity certificate - byte[] respDER = responses.get(chain[0]); - if (respDER != null && respDER.length > 0) { - encodedResponses.add(respDER); - encodedResponsesLen = 3 + respDER.length; - } else { - throw new IllegalArgumentException("Zero-length or null " + - "OCSP Response"); - } - } else if (statusType == StatusRequestType.OCSP_MULTI) { - for (X509Certificate cert : chain) { - byte[] respDER = responses.get(cert); - if (respDER != null) { - encodedResponses.add(respDER); - encodedResponsesLen += (respDER.length + 3); - } else { - // If we cannot find a response for a given certificate - // then use a zero-length placeholder. - encodedResponses.add(new byte[0]); - encodedResponsesLen += 3; - } - } - } else { - throw new IllegalArgumentException( - "Unsupported StatusResponseType: " + statusType); - } - } - - /** - * Decode the CertificateStatus handshake message coming from a - * {@code HandshakeInputStream}. - * - * @param input the {@code HandshakeInputStream} containing the - * CertificateStatus message bytes. - * - * @throws SSLHandshakeException if a zero-length response is found in the - * OCSP response type, or an unsupported response type is detected. - * @throws IOException if a decoding error occurs. - */ - CertificateStatus(HandshakeInStream input) throws IOException { - encodedResponsesLen = 0; - encodedResponses = new ArrayList<>(); - - statusType = StatusRequestType.get(input.getInt8()); - if (statusType == StatusRequestType.OCSP) { - byte[] respDER = input.getBytes24(); - // Convert the incoming bytes to a OCSPResponse strucutre - if (respDER.length > 0) { - encodedResponses.add(respDER); - encodedResponsesLen = 3 + respDER.length; - } else { - throw new SSLHandshakeException("Zero-length OCSP Response"); - } - } else if (statusType == StatusRequestType.OCSP_MULTI) { - int respListLen = input.getInt24(); - encodedResponsesLen = respListLen; - - // Add each OCSP reponse into the array list in the order - // we receive them off the wire. A zero-length array is - // allowed for ocsp_multi, and means that a response for - // a given certificate is not available. - while (respListLen > 0) { - byte[] respDER = input.getBytes24(); - encodedResponses.add(respDER); - respListLen -= (respDER.length + 3); - } - - if (respListLen != 0) { - throw new SSLHandshakeException( - "Bad OCSP response list length"); - } - } else { - throw new SSLHandshakeException("Unsupported StatusResponseType: " + - statusType); - } - } - - /** - * Get the length of the CertificateStatus message. - * - * @return the length of the message in bytes. - */ - @Override - int messageLength() { - int len = 1; // Length + Status type - - if (messageLength == -1) { - if (statusType == StatusRequestType.OCSP) { - len += encodedResponsesLen; - } else if (statusType == StatusRequestType.OCSP_MULTI) { - len += 3 + encodedResponsesLen; - } - messageLength = len; - } - - return messageLength; - } - - /** - * Encode the CertificateStatus handshake message and place it on a - * {@code HandshakeOutputStream}. - * - * @param s the HandshakeOutputStream that will the message bytes. - * - * @throws IOException if an encoding error occurs. - */ - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt8(statusType.id); - if (statusType == StatusRequestType.OCSP) { - s.putBytes24(encodedResponses.get(0)); - } else if (statusType == StatusRequestType.OCSP_MULTI) { - s.putInt24(encodedResponsesLen); - for (byte[] respBytes : encodedResponses) { - if (respBytes != null) { - s.putBytes24(respBytes); - } else { - s.putBytes24(null); - } - } - } else { - // It is highly unlikely that we will fall into this section of - // the code. - throw new SSLHandshakeException("Unsupported status_type: " + - statusType.id); - } - } - - /** - * Display a human-readable representation of the CertificateStatus message. - * - * @param s the PrintStream used to display the message data. - * - * @throws IOException if any errors occur while parsing the OCSP response - * bytes into a readable form. - */ - @Override - void print(PrintStream s) throws IOException { - s.println("*** CertificateStatus"); - if (debug != null && Debug.isOn("verbose")) { - s.println("Type: " + statusType); - if (statusType == StatusRequestType.OCSP) { - OCSPResponse oResp = new OCSPResponse(encodedResponses.get(0)); - s.println(oResp); - } else if (statusType == StatusRequestType.OCSP_MULTI) { - int numResponses = encodedResponses.size(); - s.println(numResponses + - (numResponses == 1 ? " entry:" : " entries:")); - for (byte[] respDER : encodedResponses) { - if (respDER.length > 0) { - OCSPResponse oResp = new OCSPResponse(respDER); - s.println(oResp); - } else { - s.println(""); - } - } - } - } - } - - /** - * Get the type of CertificateStatus message - * - * @return the {@code StatusRequestType} for this CertificateStatus - * message. - */ - StatusRequestType getType() { - return statusType; - } - - /** - * Get the list of non-zero length OCSP responses. - * The responses returned in this list can be used to map to - * {@code X509Certificate} objects provided by the peer and - * provided to a {@code PKIXRevocationChecker}. - * - * @return an unmodifiable List of zero or more byte arrays, each one - * consisting of a single status response. - */ - List getResponses() { - return Collections.unmodifiableList(encodedResponses); - } -} - -/* - * ServerKeyExchange ... SERVER --> CLIENT - * - * The cipher suite selected, when combined with the certificate exchanged, - * implies one of several different kinds of key exchange. Most current - * cipher suites require the server to send more than its certificate. - * - * The primary exceptions are when a server sends an encryption-capable - * RSA public key in its cert, to be used with RSA (or RSA_export) key - * exchange; and when a server sends its Diffie-Hellman cert. Those kinds - * of key exchange do not require a ServerKeyExchange message. - * - * Key exchange can be viewed as having three modes, which are explicit - * for the Diffie-Hellman flavors and poorly specified for RSA ones: - * - * - "Ephemeral" keys. Here, a "temporary" key is allocated by the - * server, and signed. Diffie-Hellman keys signed using RSA or - * DSS are ephemeral (DHE flavor). RSA keys get used to do the same - * thing, to cut the key size down to 512 bits (export restrictions) - * or for signing-only RSA certificates. - * - * - Anonymity. Here no server certificate is sent, only the public - * key of the server. This case is subject to man-in-the-middle - * attacks. This can be done with Diffie-Hellman keys (DH_anon) or - * with RSA keys, but is only used in SSLv3 for DH_anon. - * - * - "Normal" case. Here a server certificate is sent, and the public - * key there is used directly in exchanging the premaster secret. - * For example, Diffie-Hellman "DH" flavor, and any RSA flavor with - * only 512 bit keys. - * - * If a server certificate is sent, there is no anonymity. However, - * when a certificate is sent, ephemeral keys may still be used to - * exchange the premaster secret. That's how RSA_EXPORT often works, - * as well as how the DHE_* flavors work. - */ -abstract static class ServerKeyExchange extends HandshakeMessage -{ - @Override - int messageType() { return ht_server_key_exchange; } -} - - -/* - * Using RSA for Key Exchange: exchange a session key that's not as big - * as the signing-only key. Used for export applications, since exported - * RSA encryption keys can't be bigger than 512 bytes. - * - * This is never used when keys are 512 bits or smaller, and isn't used - * on "US Domestic" ciphers in any case. - */ -static final -class RSA_ServerKeyExchange extends ServerKeyExchange -{ - private byte[] rsa_modulus; // 1 to 2^16 - 1 bytes - private byte[] rsa_exponent; // 1 to 2^16 - 1 bytes - - private Signature signature; - private byte[] signatureBytes; - - /* - * Hash the nonces and the ephemeral RSA public key. - */ - private void updateSignature(byte[] clntNonce, byte[] svrNonce) - throws SignatureException { - int tmp; - - signature.update(clntNonce); - signature.update(svrNonce); - - tmp = rsa_modulus.length; - signature.update((byte)(tmp >> 8)); - signature.update((byte)(tmp & 0x0ff)); - signature.update(rsa_modulus); - - tmp = rsa_exponent.length; - signature.update((byte)(tmp >> 8)); - signature.update((byte)(tmp & 0x0ff)); - signature.update(rsa_exponent); - } - - - /* - * Construct an RSA server key exchange message, using data - * known _only_ to the server. - * - * The client knows the public key corresponding to this private - * key, from the Certificate message sent previously. To comply - * with US export regulations we use short RSA keys ... either - * long term ones in the server's X509 cert, or else ephemeral - * ones sent using this message. - */ - RSA_ServerKeyExchange(PublicKey ephemeralKey, PrivateKey privateKey, - RandomCookie clntNonce, RandomCookie svrNonce, SecureRandom sr) - throws GeneralSecurityException { - RSAPublicKeySpec rsaKey = JsseJce.getRSAPublicKeySpec(ephemeralKey); - rsa_modulus = toByteArray(rsaKey.getModulus()); - rsa_exponent = toByteArray(rsaKey.getPublicExponent()); - signature = RSASignature.getInstance(); - signature.initSign(privateKey, sr); - updateSignature(clntNonce.random_bytes, svrNonce.random_bytes); - signatureBytes = signature.sign(); - } - - - /* - * Parse an RSA server key exchange message, using data known - * to the client (and, in some situations, eavesdroppers). - */ - RSA_ServerKeyExchange(HandshakeInStream input) - throws IOException, NoSuchAlgorithmException { - signature = RSASignature.getInstance(); - rsa_modulus = input.getBytes16(); - rsa_exponent = input.getBytes16(); - signatureBytes = input.getBytes16(); - } - - /* - * Get the ephemeral RSA public key that will be used in this - * SSL connection. - */ - PublicKey getPublicKey() { - try { - KeyFactory kfac = JsseJce.getKeyFactory("RSA"); - // modulus and exponent are always positive - RSAPublicKeySpec kspec = new RSAPublicKeySpec( - new BigInteger(1, rsa_modulus), - new BigInteger(1, rsa_exponent)); - return kfac.generatePublic(kspec); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /* - * Verify the signed temporary key using the hashes computed - * from it and the two nonces. This is called by clients - * with "exportable" RSA flavors. - */ - boolean verify(PublicKey certifiedKey, RandomCookie clntNonce, - RandomCookie svrNonce) throws GeneralSecurityException { - signature.initVerify(certifiedKey); - updateSignature(clntNonce.random_bytes, svrNonce.random_bytes); - return signature.verify(signatureBytes); - } - - @Override - int messageLength() { - return 6 + rsa_modulus.length + rsa_exponent.length - + signatureBytes.length; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putBytes16(rsa_modulus); - s.putBytes16(rsa_exponent); - s.putBytes16(signatureBytes); - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** RSA ServerKeyExchange"); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "RSA Modulus", rsa_modulus); - Debug.println(s, "RSA Public Exponent", rsa_exponent); - } - } -} - - -/* - * Using Diffie-Hellman algorithm for key exchange. All we really need to - * do is securely get Diffie-Hellman keys (using the same P, G parameters) - * to our peer, then we automatically have a shared secret without need - * to exchange any more data. (D-H only solutions, such as SKIP, could - * eliminate key exchange negotiations and get faster connection setup. - * But they still need a signature algorithm like DSS/DSA to support the - * trusted distribution of keys without relying on unscalable physical - * key distribution systems.) - * - * This class supports several DH-based key exchange algorithms, though - * perhaps eventually each deserves its own class. Notably, this has - * basic support for DH_anon and its DHE_DSS and DHE_RSA signed variants. - */ -static final -class DH_ServerKeyExchange extends ServerKeyExchange -{ - // Fix message encoding, see 4348279 - private static final boolean dhKeyExchangeFix = - Debug.getBooleanProperty("com.sun.net.ssl.dhKeyExchangeFix", true); - - private byte[] dh_p; // 1 to 2^16 - 1 bytes - private byte[] dh_g; // 1 to 2^16 - 1 bytes - private byte[] dh_Ys; // 1 to 2^16 - 1 bytes - - private byte[] signature; - - // protocol version being established using this ServerKeyExchange message - ProtocolVersion protocolVersion; - - // the preferable signature algorithm used by this ServerKeyExchange message - private SignatureAndHashAlgorithm preferableSignatureAlgorithm; - - /* - * Construct from initialized DH key object, for DH_anon - * key exchange. - */ - DH_ServerKeyExchange(DHCrypt obj, ProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - this.preferableSignatureAlgorithm = null; - - // The DH key has been validated in the constructor of DHCrypt. - setValues(obj); - signature = null; - } - - /* - * Construct from initialized DH key object and the key associated - * with the cert chain which was sent ... for DHE_DSS and DHE_RSA - * key exchange. (Constructor called by server.) - */ - DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte[] clntNonce, - byte[] svrNonce, SecureRandom sr, - SignatureAndHashAlgorithm signAlgorithm, - ProtocolVersion protocolVersion) throws GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - // The DH key has been validated in the constructor of DHCrypt. - setValues(obj); - - Signature sig; - if (protocolVersion.useTLS12PlusSpec()) { - this.preferableSignatureAlgorithm = signAlgorithm; - sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); - } else { - this.preferableSignatureAlgorithm = null; - if (key.getAlgorithm().equals("DSA")) { - sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); - } else { - sig = RSASignature.getInstance(); - } - } - - sig.initSign(key, sr); - updateSignature(sig, clntNonce, svrNonce); - signature = sig.sign(); - } - - /* - * Construct a DH_ServerKeyExchange message from an input - * stream, as if sent from server to client for use with - * DH_anon key exchange - */ - DH_ServerKeyExchange(HandshakeInStream input, - ProtocolVersion protocolVersion) - throws IOException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - this.preferableSignatureAlgorithm = null; - - dh_p = input.getBytes16(); - dh_g = input.getBytes16(); - dh_Ys = input.getBytes16(); - KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys), - new BigInteger(1, dh_p), - new BigInteger(1, dh_g))); - - signature = null; - } - - /* - * Construct a DH_ServerKeyExchange message from an input stream - * and a certificate, as if sent from server to client for use with - * DHE_DSS or DHE_RSA key exchange. (Called by client.) - */ - DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey, - byte[] clntNonce, byte[] svrNonce, int messageSize, - Collection localSupportedSignAlgs, - ProtocolVersion protocolVersion) - throws IOException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - // read params: ServerDHParams - dh_p = input.getBytes16(); - dh_g = input.getBytes16(); - dh_Ys = input.getBytes16(); - KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys), - new BigInteger(1, dh_p), - new BigInteger(1, dh_g))); - - // read the signature and hash algorithm - if (protocolVersion.useTLS12PlusSpec()) { - int hash = input.getInt8(); // hash algorithm - int signature = input.getInt8(); // signature algorithm - - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, 0); - - // Is it a local supported signature algorithm? - if (!localSupportedSignAlgs.contains( - preferableSignatureAlgorithm)) { - throw new SSLHandshakeException( - "Unsupported SignatureAndHashAlgorithm in " + - "ServerKeyExchange message: " + - preferableSignatureAlgorithm); - } - } else { - this.preferableSignatureAlgorithm = null; - } - - // read the signature - byte[] signature; - if (dhKeyExchangeFix) { - signature = input.getBytes16(); - } else { - messageSize -= (dh_p.length + 2); - messageSize -= (dh_g.length + 2); - messageSize -= (dh_Ys.length + 2); - - signature = new byte[messageSize]; - input.read(signature); - } - - Signature sig; - String algorithm = publicKey.getAlgorithm(); - if (protocolVersion.useTLS12PlusSpec()) { - sig = JsseJce.getSignature( - preferableSignatureAlgorithm.getAlgorithmName()); - } else { - switch (algorithm) { - case "DSA": - sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); - break; - case "RSA": - sig = RSASignature.getInstance(); - break; - default: - throw new SSLKeyException( - "neither an RSA or a DSA key: " + algorithm); - } - } - - sig.initVerify(publicKey); - updateSignature(sig, clntNonce, svrNonce); - - if (sig.verify(signature) == false ) { - throw new SSLKeyException("Server D-H key verification failed"); - } - } - - /* Return the Diffie-Hellman modulus */ - BigInteger getModulus() { - return new BigInteger(1, dh_p); - } - - /* Return the Diffie-Hellman base/generator */ - BigInteger getBase() { - return new BigInteger(1, dh_g); - } - - /* Return the server's Diffie-Hellman public key */ - BigInteger getServerPublicKey() { - return new BigInteger(1, dh_Ys); - } - - /* - * Update sig with nonces and Diffie-Hellman public key. - */ - private void updateSignature(Signature sig, byte[] clntNonce, - byte[] svrNonce) throws SignatureException { - int tmp; - - sig.update(clntNonce); - sig.update(svrNonce); - - tmp = dh_p.length; - sig.update((byte)(tmp >> 8)); - sig.update((byte)(tmp & 0x0ff)); - sig.update(dh_p); - - tmp = dh_g.length; - sig.update((byte)(tmp >> 8)); - sig.update((byte)(tmp & 0x0ff)); - sig.update(dh_g); - - tmp = dh_Ys.length; - sig.update((byte)(tmp >> 8)); - sig.update((byte)(tmp & 0x0ff)); - sig.update(dh_Ys); - } - - private void setValues(DHCrypt obj) { - dh_p = toByteArray(obj.getModulus()); - dh_g = toByteArray(obj.getBase()); - dh_Ys = toByteArray(obj.getPublicKey()); - } - - @Override - int messageLength() { - int temp = 6; // overhead for p, g, y(s) values. - - temp += dh_p.length; - temp += dh_g.length; - temp += dh_Ys.length; - - if (signature != null) { - if (protocolVersion.useTLS12PlusSpec()) { - temp += SignatureAndHashAlgorithm.sizeInRecord(); - } - - temp += signature.length; - if (dhKeyExchangeFix) { - temp += 2; - } - } - - return temp; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putBytes16(dh_p); - s.putBytes16(dh_g); - s.putBytes16(dh_Ys); - - if (signature != null) { - if (protocolVersion.useTLS12PlusSpec()) { - s.putInt8(preferableSignatureAlgorithm.getHashValue()); - s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); - } - - if (dhKeyExchangeFix) { - s.putBytes16(signature); - } else { - s.write(signature); - } - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** Diffie-Hellman ServerKeyExchange"); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "DH Modulus", dh_p); - Debug.println(s, "DH Base", dh_g); - Debug.println(s, "Server DH Public Key", dh_Ys); - - if (signature == null) { - s.println("Anonymous"); - } else { - if (protocolVersion.useTLS12PlusSpec()) { - s.println("Signature Algorithm " + - preferableSignatureAlgorithm.getAlgorithmName()); - } - - s.println("Signed with a DSA or RSA public key"); - } - } - } -} - -/* - * ECDH server key exchange message. Sent by the server for ECDHE and ECDH_anon - * ciphersuites to communicate its ephemeral public key (including the - * EC domain parameters). - * - * We support named curves only, no explicitly encoded curves. - */ -static final -class ECDH_ServerKeyExchange extends ServerKeyExchange { - - // constants for ECCurveType - private static final int CURVE_EXPLICIT_PRIME = 1; - private static final int CURVE_EXPLICIT_CHAR2 = 2; - private static final int CURVE_NAMED_CURVE = 3; - - // id of the named group we are using - private int groupId; - - // encoded public point - private byte[] pointBytes; - - // signature bytes (or null if anonymous) - private byte[] signatureBytes; - - // public key object encapsulated in this message - private ECPublicKey publicKey; - - // protocol version being established using this ServerKeyExchange message - ProtocolVersion protocolVersion; - - // the preferable signature algorithm used by this ServerKeyExchange message - private SignatureAndHashAlgorithm preferableSignatureAlgorithm; - - ECDH_ServerKeyExchange(ECDHCrypt obj, PrivateKey privateKey, - byte[] clntNonce, byte[] svrNonce, SecureRandom sr, - SignatureAndHashAlgorithm signAlgorithm, - ProtocolVersion protocolVersion) - throws SSLHandshakeException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - publicKey = (ECPublicKey)obj.getPublicKey(); - ECParameterSpec params = publicKey.getParams(); - ECPoint point = publicKey.getW(); - pointBytes = JsseJce.encodePoint(point, params.getCurve()); - - NamedGroup namedGroup = NamedGroup.valueOf(params); - if ((namedGroup == null) || (namedGroup.oid == null) ){ - // unlikely - throw new SSLHandshakeException( - "Unnamed EC parameter spec: " + params); - } - groupId = namedGroup.id; - - if (privateKey == null) { - // ECDH_anon - return; - } - - Signature sig; - if (protocolVersion.useTLS12PlusSpec()) { - this.preferableSignatureAlgorithm = signAlgorithm; - sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(privateKey.getAlgorithm()); - } - sig.initSign(privateKey, sr); - - updateSignature(sig, clntNonce, svrNonce); - signatureBytes = sig.sign(); - } - - /* - * Parse an ECDH server key exchange message. - */ - ECDH_ServerKeyExchange(HandshakeInStream input, PublicKey signingKey, - byte[] clntNonce, byte[] svrNonce, - Collection localSupportedSignAlgs, - ProtocolVersion protocolVersion) - throws IOException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - // read params: ServerECDHParams - int curveType = input.getInt8(); - ECParameterSpec parameters; - // These parsing errors should never occur as we negotiated - // the supported curves during the exchange of the Hello messages. - if (curveType == CURVE_NAMED_CURVE) { - groupId = input.getInt16(); - NamedGroup namedGroup = NamedGroup.valueOf(groupId); - if (namedGroup == null) { - throw new SSLHandshakeException( - "Unknown named group ID: " + groupId); - } - - if (!SupportedGroupsExtension.supports(namedGroup)) { - throw new SSLHandshakeException( - "Unsupported named group: " + namedGroup); - } - - if (namedGroup.oid == null) { - throw new SSLHandshakeException( - "Unknown named EC curve: " + namedGroup); - } - - parameters = JsseJce.getECParameterSpec(namedGroup.oid); - if (parameters == null) { - throw new SSLHandshakeException( - "No supported EC parameter for named group: " + namedGroup); - } - } else { - throw new SSLHandshakeException( - "Unsupported ECCurveType: " + curveType); - } - pointBytes = input.getBytes8(); - - ECPoint point = JsseJce.decodePoint(pointBytes, parameters.getCurve()); - KeyFactory factory = JsseJce.getKeyFactory("EC"); - publicKey = (ECPublicKey)factory.generatePublic( - new ECPublicKeySpec(point, parameters)); - - if (signingKey == null) { - // ECDH_anon - return; - } - - // read the signature and hash algorithm - if (protocolVersion.useTLS12PlusSpec()) { - int hash = input.getInt8(); // hash algorithm - int signature = input.getInt8(); // signature algorithm - - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, 0); - - // Is it a local supported signature algorithm? - if (!localSupportedSignAlgs.contains( - preferableSignatureAlgorithm)) { - throw new SSLHandshakeException( - "Unsupported SignatureAndHashAlgorithm in " + - "ServerKeyExchange message: " + - preferableSignatureAlgorithm); - } - } - - // read the signature - signatureBytes = input.getBytes16(); - - // verify the signature - Signature sig; - if (protocolVersion.useTLS12PlusSpec()) { - sig = JsseJce.getSignature( - preferableSignatureAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(signingKey.getAlgorithm()); - } - sig.initVerify(signingKey); - - updateSignature(sig, clntNonce, svrNonce); - - if (sig.verify(signatureBytes) == false ) { - throw new SSLKeyException( - "Invalid signature on ECDH server key exchange message"); - } - } - - /* - * Get the ephemeral EC public key encapsulated in this message. - */ - ECPublicKey getPublicKey() { - return publicKey; - } - - private static Signature getSignature(String keyAlgorithm) - throws NoSuchAlgorithmException { - switch (keyAlgorithm) { - case "EC": - return JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA); - case "RSA": - return RSASignature.getInstance(); - default: - throw new NoSuchAlgorithmException( - "neither an RSA or a EC key : " + keyAlgorithm); - } - } - - private void updateSignature(Signature sig, byte[] clntNonce, - byte[] svrNonce) throws SignatureException { - sig.update(clntNonce); - sig.update(svrNonce); - - sig.update((byte)CURVE_NAMED_CURVE); - sig.update((byte)(groupId >> 8)); - sig.update((byte)groupId); - sig.update((byte)pointBytes.length); - sig.update(pointBytes); - } - - @Override - int messageLength() { - int sigLen = 0; - if (signatureBytes != null) { - sigLen = 2 + signatureBytes.length; - if (protocolVersion.useTLS12PlusSpec()) { - sigLen += SignatureAndHashAlgorithm.sizeInRecord(); - } - } - - return 4 + pointBytes.length + sigLen; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt8(CURVE_NAMED_CURVE); - s.putInt16(groupId); - s.putBytes8(pointBytes); - - if (signatureBytes != null) { - if (protocolVersion.useTLS12PlusSpec()) { - s.putInt8(preferableSignatureAlgorithm.getHashValue()); - s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); - } - - s.putBytes16(signatureBytes); - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** ECDH ServerKeyExchange"); - - if (debug != null && Debug.isOn("verbose")) { - if (signatureBytes == null) { - s.println("Anonymous"); - } else { - if (protocolVersion.useTLS12PlusSpec()) { - s.println("Signature Algorithm " + - preferableSignatureAlgorithm.getAlgorithmName()); - } - } - - s.println("Server key: " + publicKey); - } - } -} - -static final class DistinguishedName { - - /* - * DER encoded distinguished name. - * TLS requires that its not longer than 65535 bytes. - */ - byte[] name; - - DistinguishedName(HandshakeInStream input) throws IOException { - name = input.getBytes16(); - } - - DistinguishedName(X500Principal dn) { - name = dn.getEncoded(); - } - - X500Principal getX500Principal() throws IOException { - try { - return new X500Principal(name); - } catch (IllegalArgumentException e) { - throw (SSLProtocolException)new SSLProtocolException( - e.getMessage()).initCause(e); - } - } - - int length() { - return 2 + name.length; - } - - void send(HandshakeOutStream output) throws IOException { - output.putBytes16(name); - } - - void print(PrintStream output) throws IOException { - X500Principal principal = new X500Principal(name); - output.println("<" + principal.toString() + ">"); - } -} - -/* - * CertificateRequest ... SERVER --> CLIENT - * - * Authenticated servers may ask clients to authenticate themselves - * in turn, using this message. - * - * Prior to TLS 1.2, the structure of the message is defined as: - * struct { - * ClientCertificateType certificate_types<1..2^8-1>; - * DistinguishedName certificate_authorities<0..2^16-1>; - * } CertificateRequest; - * - * In TLS 1.2, the structure is changed to: - * struct { - * ClientCertificateType certificate_types<1..2^8-1>; - * SignatureAndHashAlgorithm - * supported_signature_algorithms<2^16-1>; - * DistinguishedName certificate_authorities<0..2^16-1>; - * } CertificateRequest; - * - */ -static final -class CertificateRequest extends HandshakeMessage -{ - // enum ClientCertificateType - static final int cct_rsa_sign = 1; - static final int cct_dss_sign = 2; - static final int cct_rsa_fixed_dh = 3; - static final int cct_dss_fixed_dh = 4; - - // The existance of these two values is a bug in the SSL specification. - // They are never used in the protocol. - static final int cct_rsa_ephemeral_dh = 5; - static final int cct_dss_ephemeral_dh = 6; - - // From RFC 4492 (ECC) - static final int cct_ecdsa_sign = 64; - static final int cct_rsa_fixed_ecdh = 65; - static final int cct_ecdsa_fixed_ecdh = 66; - - private static final byte[] TYPES_NO_ECC = { cct_rsa_sign, cct_dss_sign }; - private static final byte[] TYPES_ECC = - { cct_rsa_sign, cct_dss_sign, cct_ecdsa_sign }; - - byte[] types; // 1 to 255 types - DistinguishedName[] authorities; // 3 to 2^16 - 1 - // ... "3" because that's the smallest DER-encoded X500 DN - - // protocol version being established using this CertificateRequest message - ProtocolVersion protocolVersion; - - // supported_signature_algorithms for TLS 1.2 or later - private Collection algorithms; - - // length of supported_signature_algorithms - private int algorithmsLen; - - CertificateRequest(X509Certificate[] ca, KeyExchange keyExchange, - Collection signAlgs, - ProtocolVersion protocolVersion) throws IOException { - - this.protocolVersion = protocolVersion; - - // always use X500Principal - authorities = new DistinguishedName[ca.length]; - for (int i = 0; i < ca.length; i++) { - X500Principal x500Principal = ca[i].getSubjectX500Principal(); - authorities[i] = new DistinguishedName(x500Principal); - } - // we support RSA, DSS, and ECDSA client authentication and they - // can be used with all ciphersuites. If this changes, the code - // needs to be adapted to take keyExchange into account. - // We only request ECDSA client auth if we have ECC crypto available. - this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC; - - // Use supported_signature_algorithms for TLS 1.2 or later. - if (protocolVersion.useTLS12PlusSpec()) { - if (signAlgs == null || signAlgs.isEmpty()) { - throw new SSLProtocolException( - "No supported signature algorithms"); - } - - algorithms = new ArrayList(signAlgs); - algorithmsLen = - SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); - } else { - algorithms = new ArrayList(); - algorithmsLen = 0; - } - } - - CertificateRequest(HandshakeInStream input, - ProtocolVersion protocolVersion) throws IOException { - - this.protocolVersion = protocolVersion; - - // Read the certificate_types. - types = input.getBytes8(); - - // Read the supported_signature_algorithms for TLS 1.2 or later. - if (protocolVersion.useTLS12PlusSpec()) { - algorithmsLen = input.getInt16(); - if (algorithmsLen < 2) { - throw new SSLProtocolException( - "Invalid supported_signature_algorithms field: " + - algorithmsLen); - } - - algorithms = new ArrayList(); - int remains = algorithmsLen; - int sequence = 0; - while (remains > 1) { // needs at least two bytes - int hash = input.getInt8(); // hash algorithm - int signature = input.getInt8(); // signature algorithm - - SignatureAndHashAlgorithm algorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, - ++sequence); - algorithms.add(algorithm); - remains -= 2; // one byte for hash, one byte for signature - } - - if (remains != 0) { - throw new SSLProtocolException( - "Invalid supported_signature_algorithms field. remains: " + - remains); - } - } else { - algorithms = new ArrayList(); - algorithmsLen = 0; - } - - // read the certificate_authorities - int len = input.getInt16(); - ArrayList v = new ArrayList<>(); - while (len >= 3) { - DistinguishedName dn = new DistinguishedName(input); - v.add(dn); - len -= dn.length(); - } - - if (len != 0) { - throw new SSLProtocolException( - "Bad CertificateRequest DN length: " + len); - } - - authorities = v.toArray(new DistinguishedName[v.size()]); - } - - X500Principal[] getAuthorities() throws IOException { - X500Principal[] ret = new X500Principal[authorities.length]; - for (int i = 0; i < authorities.length; i++) { - ret[i] = authorities[i].getX500Principal(); - } - return ret; - } - - Collection getSignAlgorithms() { - return algorithms; - } - - @Override - int messageType() { - return ht_certificate_request; - } - - @Override - int messageLength() { - int len = 1 + types.length + 2; - - if (protocolVersion.useTLS12PlusSpec()) { - len += algorithmsLen + 2; - } - - for (int i = 0; i < authorities.length; i++) { - len += authorities[i].length(); - } - - return len; - } - - @Override - void send(HandshakeOutStream output) throws IOException { - // put certificate_types - output.putBytes8(types); - - // put supported_signature_algorithms - if (protocolVersion.useTLS12PlusSpec()) { - output.putInt16(algorithmsLen); - for (SignatureAndHashAlgorithm algorithm : algorithms) { - output.putInt8(algorithm.getHashValue()); // hash - output.putInt8(algorithm.getSignatureValue()); // signature - } - } - - // put certificate_authorities - int len = 0; - for (int i = 0; i < authorities.length; i++) { - len += authorities[i].length(); - } - - output.putInt16(len); - for (int i = 0; i < authorities.length; i++) { - authorities[i].send(output); - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** CertificateRequest"); - - if (debug != null && Debug.isOn("verbose")) { - s.print("Cert Types: "); - for (int i = 0; i < types.length; i++) { - switch (types[i]) { - case cct_rsa_sign: - s.print("RSA"); break; - case cct_dss_sign: - s.print("DSS"); break; - case cct_rsa_fixed_dh: - s.print("Fixed DH (RSA sig)"); break; - case cct_dss_fixed_dh: - s.print("Fixed DH (DSS sig)"); break; - case cct_rsa_ephemeral_dh: - s.print("Ephemeral DH (RSA sig)"); break; - case cct_dss_ephemeral_dh: - s.print("Ephemeral DH (DSS sig)"); break; - case cct_ecdsa_sign: - s.print("ECDSA"); break; - case cct_rsa_fixed_ecdh: - s.print("Fixed ECDH (RSA sig)"); break; - case cct_ecdsa_fixed_ecdh: - s.print("Fixed ECDH (ECDSA sig)"); break; - default: - s.print("Type-" + (types[i] & 0xff)); break; - } - if (i != types.length - 1) { - s.print(", "); - } - } - s.println(); - - if (protocolVersion.useTLS12PlusSpec()) { - StringBuilder sb = new StringBuilder(); - boolean opened = false; - for (SignatureAndHashAlgorithm signAlg : algorithms) { - if (opened) { - sb.append(", ").append(signAlg.getAlgorithmName()); - } else { - sb.append(signAlg.getAlgorithmName()); - opened = true; - } - } - s.println("Supported Signature Algorithms: " + sb); - } - - s.println("Cert Authorities:"); - if (authorities.length == 0) { - s.println(""); - } else { - for (int i = 0; i < authorities.length; i++) { - authorities[i].print(s); - } - } - } - } -} - - -/* - * ServerHelloDone ... SERVER --> CLIENT - * - * When server's done sending its messages in response to the client's - * "hello" (e.g. its own hello, certificate, key exchange message, perhaps - * client certificate request) it sends this message to flag that it's - * done that part of the handshake. - */ -static final -class ServerHelloDone extends HandshakeMessage -{ - @Override - int messageType() { return ht_server_hello_done; } - - ServerHelloDone() { } - - ServerHelloDone(HandshakeInStream input) - { - // nothing to do - } - - @Override - int messageLength() - { - return 0; - } - - @Override - void send(HandshakeOutStream s) throws IOException - { - // nothing to send - } - - @Override - void print(PrintStream s) throws IOException - { - s.println("*** ServerHelloDone"); - } -} - - -/* - * CertificateVerify ... CLIENT --> SERVER - * - * Sent after client sends signature-capable certificates (e.g. not - * Diffie-Hellman) to verify. - */ -static final class CertificateVerify extends HandshakeMessage { - - // the signature bytes - private byte[] signature; - - // protocol version being established using this CertificateVerify message - ProtocolVersion protocolVersion; - - // the preferable signature algorithm used by this CertificateVerify message - private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; - - /* - * Create an RSA or DSA signed certificate verify message. - */ - CertificateVerify(ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, PrivateKey privateKey, - SecretKey masterSecret, SecureRandom sr, - SignatureAndHashAlgorithm signAlgorithm) - throws GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - String algorithm = privateKey.getAlgorithm(); - Signature sig = null; - if (protocolVersion.useTLS12PlusSpec()) { - this.preferableSignatureAlgorithm = signAlgorithm; - sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(protocolVersion, algorithm); - } - sig.initSign(privateKey, sr); - updateSignature(sig, protocolVersion, handshakeHash, algorithm, - masterSecret); - signature = sig.sign(); - } - - // - // Unmarshal the signed data from the input stream. - // - CertificateVerify(HandshakeInStream input, - Collection localSupportedSignAlgs, - ProtocolVersion protocolVersion) throws IOException { - - this.protocolVersion = protocolVersion; - - // read the signature and hash algorithm - if (protocolVersion.useTLS12PlusSpec()) { - int hashAlg = input.getInt8(); // hash algorithm - int signAlg = input.getInt8(); // signature algorithm - - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0); - - // Is it a local supported signature algorithm? - if (!localSupportedSignAlgs.contains( - preferableSignatureAlgorithm)) { - throw new SSLHandshakeException( - "Unsupported SignatureAndHashAlgorithm in " + - "CertificateVerify message: " + preferableSignatureAlgorithm); - } - } - - // read the signature - signature = input.getBytes16(); - } - - /* - * Get the preferable signature algorithm used by this message - */ - SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() { - return preferableSignatureAlgorithm; - } - - /* - * Verify a certificate verify message. Return the result of verification, - * if there is a problem throw a GeneralSecurityException. - */ - boolean verify(ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, PublicKey publicKey, - SecretKey masterSecret) throws GeneralSecurityException { - String algorithm = publicKey.getAlgorithm(); - Signature sig = null; - if (protocolVersion.useTLS12PlusSpec()) { - sig = JsseJce.getSignature( - preferableSignatureAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(protocolVersion, algorithm); - } - sig.initVerify(publicKey); - updateSignature(sig, protocolVersion, handshakeHash, algorithm, - masterSecret); - return sig.verify(signature); - } - - /* - * Get the Signature object appropriate for verification using the - * given signature algorithm and protocol version. - */ - private static Signature getSignature(ProtocolVersion protocolVersion, - String algorithm) throws GeneralSecurityException { - switch (algorithm) { - case "RSA": - return RSASignature.getInternalInstance(); - case "DSA": - return JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); - case "EC": - return JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); - default: - throw new SignatureException("Unrecognized algorithm: " - + algorithm); - } - } - - /* - * Update the Signature with the data appropriate for the given - * signature algorithm and protocol version so that the object is - * ready for signing or verifying. - */ - private static void updateSignature(Signature sig, - ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, String algorithm, SecretKey masterKey) - throws SignatureException { - - if (algorithm.equals("RSA")) { - if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1- - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - - if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3 - updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); - updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); - } - - // The signature must be an instance of RSASignature, need - // to use these hashes directly. - RSASignature.setHashes(sig, md5Clone, shaClone); - } else { // TLS1.2+ - sig.update(handshakeHash.getAllHandshakeMessages()); - } - } else { // DSA, ECDSA - if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1- - MessageDigest shaClone = handshakeHash.getSHAClone(); - - if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3 - updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); - } - - sig.update(shaClone.digest()); - } else { // TLS1.2+ - sig.update(handshakeHash.getAllHandshakeMessages()); - } - } - } - - /* - * Update the MessageDigest for SSLv3 certificate verify or finished - * message calculation. The digest must already have been updated with - * all preceding handshake messages. - * Used by the Finished class as well. - */ - private static void updateDigest(MessageDigest md, - byte[] pad1, byte[] pad2, - SecretKey masterSecret) { - // Digest the key bytes if available. - // Otherwise (sensitive key), try digesting the key directly. - // That is currently only implemented in SunPKCS11 using a private - // reflection API, so we avoid that if possible. - byte[] keyBytes = "RAW".equals(masterSecret.getFormat()) - ? masterSecret.getEncoded() : null; - if (keyBytes != null) { - md.update(keyBytes); - } else { - digestKey(md, masterSecret); - } - md.update(pad1); - byte[] temp = md.digest(); - - if (keyBytes != null) { - md.update(keyBytes); - } else { - digestKey(md, masterSecret); - } - md.update(pad2); - md.update(temp); - } - - private static void digestKey(MessageDigest md, SecretKey key) { - try { - if (md instanceof MessageDigestSpi2) { - ((MessageDigestSpi2)md).engineUpdate(key); - } else { - throw new Exception( - "Digest does not support implUpdate(SecretKey)"); - } - } catch (Exception e) { - throw new RuntimeException( - "Could not obtain encoded key and " - + "MessageDigest cannot digest key", e); - } - } - - @Override - int messageType() { - return ht_certificate_verify; - } - - @Override - int messageLength() { - int temp = 2; - - if (protocolVersion.useTLS12PlusSpec()) { - temp += SignatureAndHashAlgorithm.sizeInRecord(); - } - - return temp + signature.length; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - if (protocolVersion.useTLS12PlusSpec()) { - s.putInt8(preferableSignatureAlgorithm.getHashValue()); - s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); - } - - s.putBytes16(signature); - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** CertificateVerify"); - - if (debug != null && Debug.isOn("verbose")) { - if (protocolVersion.useTLS12PlusSpec()) { - s.println("Signature Algorithm " + - preferableSignatureAlgorithm.getAlgorithmName()); - } - } - } -} - - -/* - * FINISHED ... sent by both CLIENT and SERVER - * - * This is the FINISHED message as defined in the SSL and TLS protocols. - * Both protocols define this handshake message slightly differently. - * This class supports both formats. - * - * When handshaking is finished, each side sends a "change_cipher_spec" - * record, then immediately sends a "finished" handshake message prepared - * according to the newly adopted cipher spec. - * - * NOTE that until this is sent, no application data may be passed, unless - * some non-default cipher suite has already been set up on this connection - * connection (e.g. a previous handshake arranged one). - */ -static final class Finished extends HandshakeMessage { - - // constant for a Finished message sent by the client - static final int CLIENT = 1; - - // constant for a Finished message sent by the server - static final int SERVER = 2; - - // enum Sender: "CLNT" and "SRVR" - private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 }; - private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 }; - - /* - * Contents of the finished message ("checksum"). For TLS, it - * is 12 bytes long, for SSLv3 36 bytes. - */ - private byte[] verifyData; - - /* - * Current cipher suite we are negotiating. TLS 1.2 has - * ciphersuite-defined PRF algorithms. - */ - private ProtocolVersion protocolVersion; - private CipherSuite cipherSuite; - - /* - * Create a finished message to send to the remote peer. - */ - Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, - int sender, SecretKey master, CipherSuite cipherSuite) { - this.protocolVersion = protocolVersion; - this.cipherSuite = cipherSuite; - verifyData = getFinished(handshakeHash, sender, master); - } - - /* - * Constructor that reads FINISHED message from stream. - */ - Finished(ProtocolVersion protocolVersion, HandshakeInStream input, - CipherSuite cipherSuite) throws IOException { - this.protocolVersion = protocolVersion; - this.cipherSuite = cipherSuite; - int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36; - verifyData = new byte[msgLen]; - input.read(verifyData); - } - - /* - * Verify that the hashes here are what would have been produced - * according to a given set of inputs. This is used to ensure that - * both client and server are fully in sync, and that the handshake - * computations have been successful. - */ - boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) { - byte[] myFinished = getFinished(handshakeHash, sender, master); - return MessageDigest.isEqual(myFinished, verifyData); - } - - /* - * Perform the actual finished message calculation. - */ - private byte[] getFinished(HandshakeHash handshakeHash, - int sender, SecretKey masterKey) { - byte[] sslLabel; - String tlsLabel; - if (sender == CLIENT) { - sslLabel = SSL_CLIENT; - tlsLabel = "client finished"; - } else if (sender == SERVER) { - sslLabel = SSL_SERVER; - tlsLabel = "server finished"; - } else { - throw new RuntimeException("Invalid sender: " + sender); - } - - if (protocolVersion.useTLS10PlusSpec()) { - // TLS 1.0+ - try { - byte[] seed; - String prfAlg; - PRF prf; - - // Get the KeyGenerator alg and calculate the seed. - if (protocolVersion.useTLS12PlusSpec()) { - // TLS 1.2+ or DTLS 1.2+ - seed = handshakeHash.getFinishedHash(); - - prfAlg = "SunTls12Prf"; - prf = cipherSuite.prfAlg; - } else { - // TLS 1.0/1.1, DTLS 1.0 - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - seed = new byte[36]; - md5Clone.digest(seed, 0, 16); - shaClone.digest(seed, 16, 20); - - prfAlg = "SunTlsPrf"; - prf = P_NONE; - } - - String prfHashAlg = prf.getPRFHashAlg(); - int prfHashLength = prf.getPRFHashLength(); - int prfBlockSize = prf.getPRFBlockSize(); - - /* - * RFC 5246/7.4.9 says that finished messages can - * be ciphersuite-specific in both length/PRF hash - * algorithm. If we ever run across a different - * length, this call will need to be updated. - */ - @SuppressWarnings("deprecation") - TlsPrfParameterSpec spec = new TlsPrfParameterSpec( - masterKey, tlsLabel, seed, 12, - prfHashAlg, prfHashLength, prfBlockSize); - - KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); - kg.init(spec); - SecretKey prfKey = kg.generateKey(); - if ("RAW".equals(prfKey.getFormat()) == false) { - throw new ProviderException( - "Invalid PRF output, format must be RAW. " + - "Format received: " + prfKey.getFormat()); - } - byte[] finished = prfKey.getEncoded(); - return finished; - } catch (GeneralSecurityException e) { - throw new RuntimeException("PRF failed", e); - } - } else { - // SSLv3 - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - updateDigest(md5Clone, sslLabel, MD5_pad1, MD5_pad2, masterKey); - updateDigest(shaClone, sslLabel, SHA_pad1, SHA_pad2, masterKey); - byte[] finished = new byte[36]; - try { - md5Clone.digest(finished, 0, 16); - shaClone.digest(finished, 16, 20); - } catch (DigestException e) { - // cannot occur - throw new RuntimeException("Digest failed", e); - } - return finished; - } - } - - /* - * Update the MessageDigest for SSLv3 finished message calculation. - * The digest must already have been updated with all preceding handshake - * messages. This operation is almost identical to the certificate verify - * hash, reuse that code. - */ - private static void updateDigest(MessageDigest md, byte[] sender, - byte[] pad1, byte[] pad2, SecretKey masterSecret) { - md.update(sender); - CertificateVerify.updateDigest(md, pad1, pad2, masterSecret); - } - - // get the verify_data of the finished message - byte[] getVerifyData() { - return verifyData; - } - - @Override - int messageType() { return ht_finished; } - - @Override - int messageLength() { - return verifyData.length; - } - - @Override - void send(HandshakeOutStream out) throws IOException { - out.write(verifyData); - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** Finished"); - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "verify_data", verifyData); - s.println("***"); - } - } -} - -// -// END of nested classes -// - -} --- /dev/null 2018-05-11 10:42:23.849000000 -0700 +++ new/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java 2018-05-11 15:09:59.187140500 -0700 @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2006, 2018, 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.IOException; +import java.nio.ByteBuffer; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Map; +import javax.net.ssl.SSLException; + +enum SSLHandshake implements SSLConsumer, HandshakeProducer { + @SuppressWarnings({"unchecked", "rawtypes"}) + HELLO_REQUEST ((byte)0x00, "hello_request", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + HelloRequest.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + HelloRequest.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CLIENT_HELLO ((byte)0x01, "client_hello", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ClientHello.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ClientHello.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + SERVER_HELLO ((byte)0x02, "server_hello", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerHello.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerHello.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry( + ServerHello.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + HELLO_RETRY_REQUEST ((byte)0x02, "hello_retry_request", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerHello.handshakeConsumer, // Use ServerHello consumer + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerHello.hrrHandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + HELLO_VERIFY_REQUEST ((byte)0x03, "hello_verify_request", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + HelloVerifyRequest.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + HelloVerifyRequest.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + NewSessionTicket.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + NewSessionTicket.handshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"), + + @SuppressWarnings({"unchecked", "rawtypes"}) + ENCRYPTED_EXTENSIONS ((byte)0x08, "encrypted_extensions", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + EncryptedExtensions.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + EncryptedExtensions.handshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE ((byte)0x0B, "certificate", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateMessage.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry( + CertificateMessage.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateMessage.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry( + CertificateMessage.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + SERVER_KEY_EXCHANGE ((byte)0x0C, "server_key_exchange", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerKeyExchange.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerKeyExchange.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE_REQUEST ((byte)0x0D, "certificate_request", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateRequest.t10HandshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_11 + ), + new SimpleImmutableEntry( + CertificateRequest.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry( + CertificateRequest.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateRequest.t10HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_11 + ), + new SimpleImmutableEntry( + CertificateRequest.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry( + CertificateRequest.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + SERVER_HELLO_DONE ((byte)0x0E, "server_hello_done", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerHelloDone.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ServerHelloDone.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE_VERIFY ((byte)0x0F, "certificate_verify", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateVerify.s30HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_30 + ), + new SimpleImmutableEntry( + CertificateVerify.t10HandshakeConsumer, + ProtocolVersion.PROTOCOLS_10_11 + ), + new SimpleImmutableEntry( + CertificateVerify.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry( + CertificateVerify.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateVerify.s30HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_30 + ), + new SimpleImmutableEntry( + CertificateVerify.t10HandshakeProducer, + ProtocolVersion.PROTOCOLS_10_11 + ), + new SimpleImmutableEntry( + CertificateVerify.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry( + CertificateVerify.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CLIENT_KEY_EXCHANGE ((byte)0x10, "client_key_exchange", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ClientKeyExchange.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + ClientKeyExchange.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + FINISHED ((byte)0x14, "finished", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + Finished.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry( + Finished.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + Finished.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry( + Finished.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + CERTIFICATE_URL ((byte)0x15, "certificate_url"), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE_STATUS ((byte)0x16, "certificate_status", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateStatus.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateStatus.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + CertificateStatus.handshakeAbsence, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + SUPPLEMENTAL_DATA ((byte)0x17, "supplemental_data"), + + @SuppressWarnings({"unchecked", "rawtypes"}) + KEY_UPDATE ((byte)0x18, "key_update", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + KeyUpdate.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + KeyUpdate.handshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + MESSAGE_HASH ((byte)0xFE, "message_hash"), + NOT_APPLICABLE ((byte)0xFF, "not_applicable"); + + final byte id; + final String name; + final Map.Entry[] handshakeConsumers; + final Map.Entry[] handshakeProducers; + final Map.Entry[] handshakeAbsences; + + @SuppressWarnings({"unchecked", "rawtypes"}) + SSLHandshake(byte id, String name) { + this(id, name, + (Map.Entry[])( + new Map.Entry[0]), + (Map.Entry[])( + new Map.Entry[0]), + (Map.Entry[])( + new Map.Entry[0])); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + SSLHandshake(byte id, String name, + Map.Entry[] handshakeConsumers, + Map.Entry[] handshakeProducers) { + this(id, name, handshakeConsumers, handshakeProducers, + (Map.Entry[])( + new Map.Entry[0])); + } + + SSLHandshake(byte id, String name, + Map.Entry[] handshakeConsumers, + Map.Entry[] handshakeProducers, + Map.Entry[] handshakeAbsence) { + this.id = id; + this.name = name; + this.handshakeConsumers = handshakeConsumers; + this.handshakeProducers = handshakeProducers; + this.handshakeAbsences = handshakeAbsence; + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + SSLConsumer hc = getHandshakeConsumer(context); + if (hc != null) { + hc.consume(context, message); + } else { + throw new UnsupportedOperationException( + "Unsupported handshake consumer: " + this.name); + } + } + + private SSLConsumer getHandshakeConsumer(ConnectionContext context) { + if (handshakeConsumers.length == 0) { + return null; + } + + // The comsuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + ProtocolVersion protocolVersion; + if ((hc.negotiatedProtocol == null) || + (hc.negotiatedProtocol == ProtocolVersion.NONE)) { + protocolVersion = hc.maximumActiveProtocol; + } else { + protocolVersion = hc.negotiatedProtocol; + } + + for (Map.Entry phe : handshakeConsumers) { + for (ProtocolVersion pv : phe.getValue()) { + if (protocolVersion == pv) { + return phe.getKey(); + } + } + } + + return null; + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + HandshakeProducer hp = getHandshakeProducer(context); + if (hp != null) { + return hp.produce(context, message); + } else { + throw new UnsupportedOperationException( + "Unsupported handshake producer: " + this.name); + } + } + + private HandshakeProducer getHandshakeProducer( + ConnectionContext context) { + if (handshakeConsumers.length == 0) { + return null; + } + + // The comsuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + ProtocolVersion protocolVersion; + if ((hc.negotiatedProtocol == null) || + (hc.negotiatedProtocol == ProtocolVersion.NONE)) { + protocolVersion = hc.maximumActiveProtocol; + } else { + protocolVersion = hc.negotiatedProtocol; + } + + for (Map.Entry phe : handshakeProducers) { + for (ProtocolVersion pv : phe.getValue()) { + if (protocolVersion == pv) { + return phe.getKey(); + } + } + } + + return null; + } + + @Override + public String toString() { + return name; + } + + /* + static SSLHandshake valueOf(byte id) { + for (SSLHandshake hs : SSLHandshake.values()) { + if (hs.id == id) { + return hs; + } + } + + return null; + } + */ + + static String nameOf(byte id) { + // If two handshake message share the same handshake type, returns + // the first handshake message name. + // + // It is not a big issue at present as only ServerHello and + // HellRetryRequest share a handshake type. + for (SSLHandshake hs : SSLHandshake.values()) { + if (hs.id == id) { + return hs.name; + } + } + + return "UNKNOWN-HANDSHAKE-MESSAGE(" + id + ")"; + } + + static final void kickstart(HandshakeContext context) throws IOException { + if (context instanceof ClientHandshakeContext) { + // For initial handshaking, including session resumption, + // ClientHello message is used as the kickstart message. + // + // (D)TLS 1.2 and older protocols support renegotiation on existing + // connections. A ClientHello messages is used to kickstart the + // renegotiation. + // + // (D)TLS 1.3 forbids renegotiation. The post-handshake KeyUpdate + // message is used to update the sending cryptographic keys. + if (context.conContext.isNegotiated && + context.conContext.protocolVersion.useTLS13PlusSpec()) { + // Use KeyUpdate message for renegotiation. + KeyUpdate.kickstartProducer.produce(context); + } else { + // Using ClientHello message for the initial handshaking + // (including session resumption) or renegotiation. + // SSLHandshake.CLIENT_HELLO.produce(context); + ClientHello.kickstartProducer.produce(context); + } + } else { + // The server side can delivering kickstart message after the + // connection has established. + // + // (D)TLS 1.2 and older protocols use HelloRequest to begin a + // negotiation process anew. + // + // While (D)TLS 1.3 uses the post-handshake KeyUpdate message + // to update the sending cryptographic keys. + if (context.conContext.protocolVersion.useTLS13PlusSpec()) { + // Use KeyUpdate message for renegotiation. + KeyUpdate.kickstartProducer.produce(context); + } else { + // SSLHandshake.HELLO_REQUEST.produce(context); + HelloRequest.kickstartProducer.produce(context); + } + } + } + + /** + * A (transparent) specification of handshake message. + */ + static abstract class HandshakeMessage { + final HandshakeContext handshakeContext; + + HandshakeMessage(HandshakeContext handshakeContext) { + this.handshakeContext = handshakeContext; + } + + abstract SSLHandshake handshakeType(); + abstract int messageLength(); + abstract void send(HandshakeOutStream hos) throws IOException; + + void write(HandshakeOutStream hos) throws IOException { + int len = messageLength(); + if (len >= Record.OVERFLOW_OF_INT24) { + throw new SSLException("Handshake message is overflow" + + ", type = " + handshakeType() + ", len = " + len); + } + hos.write(handshakeType().id); + hos.putInt24(len); + send(hos); + hos.complete(); + } + } +}