< prev index next >

src/java.base/share/classes/sun/security/ssl/SSLHandshake.java

Print this page

        

*** 1,7 **** /* ! * 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 --- 1,7 ---- /* ! * 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
*** 23,2430 **** * 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<SNIServerName> serverNames) { ! try { ! extensions.add(new ServerNameExtension(serverNames)); ! } catch (IOException ioe) { ! // ignore the exception and return ! } ! } ! ! // add signature_algorithm extension ! void addSignatureAlgorithmsExtension( ! Collection<SignatureAndHashAlgorithm> 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<CertStatusReqItemV2> 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<byte[]> encodedChain; ! ! private int messageLength; ! ! CertificateMsg(X509Certificate[] certs) { ! chain = certs; ! } ! ! CertificateMsg(HandshakeInStream input) throws IOException { ! int chainLen = input.getInt24(); ! List<Certificate> 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<byte[]>(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("<Empty>"); ! } 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<byte[]> 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<X509Certificate, byte[]> 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("<Zero-length entry>"); ! } ! } ! } ! } ! } ! ! /** ! * 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<byte[]> 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<SignatureAndHashAlgorithm> 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<SignatureAndHashAlgorithm> 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<SignatureAndHashAlgorithm> algorithms; ! ! // length of supported_signature_algorithms ! private int algorithmsLen; ! ! CertificateRequest(X509Certificate[] ca, KeyExchange keyExchange, ! Collection<SignatureAndHashAlgorithm> 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<SignatureAndHashAlgorithm>(signAlgs); ! algorithmsLen = ! SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); ! } else { ! algorithms = new ArrayList<SignatureAndHashAlgorithm>(); ! 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<SignatureAndHashAlgorithm>(); ! 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<SignatureAndHashAlgorithm>(); ! algorithmsLen = 0; ! } ! ! // read the certificate_authorities ! int len = input.getInt16(); ! ArrayList<DistinguishedName> 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<SignatureAndHashAlgorithm> 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("<Empty>"); ! } 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<SignatureAndHashAlgorithm> 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 - // - - } --- 23,562 ---- * 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<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! HelloRequest.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! HelloRequest.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! CLIENT_HELLO ((byte)0x01, "client_hello", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! ClientHello.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ClientHello.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! SERVER_HELLO ((byte)0x02, "server_hello", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! ServerHello.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ServerHello.t12HandshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ServerHello.t13HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! HELLO_RETRY_REQUEST ((byte)0x02, "hello_retry_request", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! ServerHello.handshakeConsumer, // Use ServerHello consumer ! ProtocolVersion.PROTOCOLS_TO_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ServerHello.hrrHandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! HELLO_VERIFY_REQUEST ((byte)0x03, "hello_verify_request", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! HelloVerifyRequest.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! HelloVerifyRequest.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! NewSessionTicket.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! 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<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! EncryptedExtensions.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! EncryptedExtensions.handshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! CERTIFICATE ((byte)0x0B, "certificate", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateMessage.t12HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateMessage.t13HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateMessage.t12HandshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateMessage.t13HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! SERVER_KEY_EXCHANGE ((byte)0x0C, "server_key_exchange", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! ServerKeyExchange.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ServerKeyExchange.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! CERTIFICATE_REQUEST ((byte)0x0D, "certificate_request", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateRequest.t10HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_11 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateRequest.t12HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_12 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateRequest.t13HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateRequest.t10HandshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_11 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateRequest.t12HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_12 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateRequest.t13HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! SERVER_HELLO_DONE ((byte)0x0E, "server_hello_done", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! ServerHelloDone.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ServerHelloDone.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! CERTIFICATE_VERIFY ((byte)0x0F, "certificate_verify", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateVerify.s30HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_30 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateVerify.t10HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_10_11 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateVerify.t12HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_12 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateVerify.t13HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateVerify.s30HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_30 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateVerify.t10HandshakeProducer, ! ProtocolVersion.PROTOCOLS_10_11 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateVerify.t12HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_12 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateVerify.t13HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! CLIENT_KEY_EXCHANGE ((byte)0x10, "client_key_exchange", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! ClientKeyExchange.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! ClientKeyExchange.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! })), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! FINISHED ((byte)0x14, "finished", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! Finished.t12HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ), ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! Finished.t13HandshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! Finished.t12HandshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ), ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! Finished.t13HandshakeProducer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! })), ! ! CERTIFICATE_URL ((byte)0x15, "certificate_url"), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! CERTIFICATE_STATUS ((byte)0x16, "certificate_status", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! CertificateStatus.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! CertificateStatus.handshakeProducer, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! }), ! (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeAbsence, ProtocolVersion[]>( ! CertificateStatus.handshakeAbsence, ! ProtocolVersion.PROTOCOLS_TO_12 ! ) ! })), ! ! SUPPLEMENTAL_DATA ((byte)0x17, "supplemental_data"), ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! KEY_UPDATE ((byte)0x18, "key_update", ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( ! KeyUpdate.handshakeConsumer, ! ProtocolVersion.PROTOCOLS_OF_13 ! ) ! }), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { ! new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( ! 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<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers; ! final Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers; ! final Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsences; ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! SSLHandshake(byte id, String name) { ! this(id, name, ! (Map.Entry<SSLConsumer, ProtocolVersion[]>[])( ! new Map.Entry[0]), ! (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])( ! new Map.Entry[0]), ! (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])( ! new Map.Entry[0])); ! } ! ! @SuppressWarnings({"unchecked", "rawtypes"}) ! SSLHandshake(byte id, String name, ! Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers, ! Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers) { ! this(id, name, handshakeConsumers, handshakeProducers, ! (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])( ! new Map.Entry[0])); ! } ! ! SSLHandshake(byte id, String name, ! Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers, ! Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers, ! Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] 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<SSLConsumer, ! ProtocolVersion[]> 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<HandshakeProducer, ! ProtocolVersion[]> 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(); } } }
< prev index next >