< 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 >