src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java
Print this page
@@ -71,29 +71,48 @@
*
* @author David Brownell
*/
public abstract class HandshakeMessage {
- HandshakeMessage() { }
+ /* Class and subclass dynamic debugging support */
+ public static final Debug debug = Debug.getInstance("ssl");
// enum HandshakeType:
- static final byte ht_hello_request = 0;
- static final byte ht_client_hello = 1;
- static final byte ht_server_hello = 2;
+ 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;
- static final byte ht_server_key_exchange = 12;
- static final byte ht_certificate_request = 13;
- static final byte ht_server_hello_done = 14;
- static final byte ht_certificate_verify = 15;
- static final byte ht_client_key_exchange = 16;
+ 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;
+ 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
- /* Class and subclass dynamic debugging support */
- public static final Debug debug = Debug.getInstance("ssl");
+ 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.
@@ -107,20 +126,10 @@
b = newarray;
}
return b;
}
- /*
- * 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);
-
private static byte[] genPad(int b, int count) {
byte[] padding = new byte[count];
Arrays.fill(padding, (byte)b);
return padding;
}
@@ -139,10 +148,11 @@
+ ", 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.
@@ -197,11 +207,74 @@
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).
@@ -214,23 +287,32 @@
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 final static byte[] NULL_COMPRESSION = new byte[] {0};
ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
- SessionId sessionId, CipherSuiteList cipherSuites) {
+ 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;
+ }
if (cipherSuites.containsEC()) {
extensions.add(SupportedEllipticCurvesExtension.DEFAULT);
extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT);
}
@@ -237,15 +319,25 @@
clnt_random = new RandomCookie(generator);
compression_methods = NULL_COMPRESSION;
}
- ClientHello(HandshakeInStream s, int messageLength) throws IOException {
+ 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);
}
@@ -277,10 +369,32 @@
HelloExtension signatureAlgorithm =
new SignatureAlgorithmsExtension(algorithms);
extensions.add(signatureAlgorithm);
}
+ 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());
+ }
+
@Override
int messageType() { return ht_client_hello; }
@Override
int messageLength() {
@@ -288,24 +402,19 @@
* 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 {
- s.putInt8(protocolVersion.major);
- s.putInt8(protocolVersion.minor);
- clnt_random.send(s);
- s.putBytes8(sessionId.getId());
- cipherSuites.send(s);
- s.putBytes8(compression_methods);
- extensions.send(s);
+ send(s, true); // Count hello verify cookie.
}
@Override
void print(PrintStream s) throws IOException {
s.println("*** ClientHello, " + protocolVersion);
@@ -315,17 +424,36 @@
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
*
@@ -738,11 +866,11 @@
// The DH key has been validated in the constructor of DHCrypt.
setValues(obj);
Signature sig;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
this.preferableSignatureAlgorithm = null;
if (key.getAlgorithm().equals("DSA")) {
@@ -799,11 +927,11 @@
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.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.valueOf(hash, signature, 0);
@@ -832,11 +960,11 @@
input.read(signature);
}
Signature sig;
String algorithm = publicKey.getAlgorithm();
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
switch (algorithm) {
case "DSA":
@@ -912,11 +1040,11 @@
temp += dh_p.length;
temp += dh_g.length;
temp += dh_Ys.length;
if (signature != null) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord();
}
temp += signature.length;
if (dhKeyExchangeFix) {
@@ -932,11 +1060,11 @@
s.putBytes16(dh_p);
s.putBytes16(dh_g);
s.putBytes16(dh_Ys);
if (signature != null) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
if (dhKeyExchangeFix) {
@@ -957,11 +1085,11 @@
Debug.println(s, "Server DH Public Key", dh_Ys);
if (signature == null) {
s.println("Anonymous");
} else {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
s.println("Signed with a DSA or RSA public key");
@@ -1019,11 +1147,11 @@
// ECDH_anon
return;
}
Signature sig;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
sig = getSignature(privateKey.getAlgorithm());
}
@@ -1082,11 +1210,11 @@
// ECDH_anon
return;
}
// read the signature and hash algorithm
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.valueOf(hash, signature, 0);
@@ -1103,11 +1231,11 @@
// read the signature
signatureBytes = input.getBytes16();
// verify the signature
Signature sig;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
sig = getSignature(signingKey.getAlgorithm());
}
@@ -1155,11 +1283,11 @@
@Override
int messageLength() {
int sigLen = 0;
if (signatureBytes != null) {
sigLen = 2 + signatureBytes.length;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sigLen += SignatureAndHashAlgorithm.sizeInRecord();
}
}
return 4 + pointBytes.length + sigLen;
@@ -1170,11 +1298,11 @@
s.putInt8(CURVE_NAMED_CURVE);
s.putInt16(curveId);
s.putBytes8(pointBytes);
if (signatureBytes != null) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
s.putBytes16(signatureBytes);
@@ -1187,11 +1315,11 @@
if (debug != null && Debug.isOn("verbose")) {
if (signatureBytes == null) {
s.println("Anonymous");
} else {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
}
@@ -1313,11 +1441,11 @@
// 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.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
if (signAlgs == null || signAlgs.isEmpty()) {
throw new SSLProtocolException(
"No supported signature algorithms");
}
@@ -1337,11 +1465,11 @@
// Read the certificate_types.
types = input.getBytes8();
// Read the supported_signature_algorithms for TLS 1.2 or later.
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
algorithmsLen = input.getInt16();
if (algorithmsLen < 2) {
throw new SSLProtocolException(
"Invalid supported_signature_algorithms field");
}
@@ -1404,11 +1532,11 @@
@Override
int messageLength() {
int len = 1 + types.length + 2;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
len += algorithmsLen + 2;
}
for (int i = 0; i < authorities.length; i++) {
len += authorities[i].length();
@@ -1421,11 +1549,11 @@
void send(HandshakeOutStream output) throws IOException {
// put certificate_types
output.putBytes8(types);
// put supported_signature_algorithms
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
output.putInt16(algorithmsLen);
for (SignatureAndHashAlgorithm algorithm : algorithms) {
output.putInt8(algorithm.getHashValue()); // hash
output.putInt8(algorithm.getSignatureValue()); // signature
}
@@ -1476,11 +1604,11 @@
s.print(", ");
}
}
s.println();
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
StringBuilder sb = new StringBuilder();
boolean opened = false;
for (SignatureAndHashAlgorithm signAlg : algorithms) {
if (opened) {
sb.append(", ").append(signAlg.getAlgorithmName());
@@ -1574,11 +1702,11 @@
this.protocolVersion = protocolVersion;
String algorithm = privateKey.getAlgorithm();
Signature sig = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else {
sig = getSignature(protocolVersion, algorithm);
}
@@ -1596,11 +1724,11 @@
ProtocolVersion protocolVersion) throws IOException {
this.protocolVersion = protocolVersion;
// read the signature and hash algorithm
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
int hashAlg = input.getInt8(); // hash algorithm
int signAlg = input.getInt8(); // signature algorithm
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0);
@@ -1632,11 +1760,11 @@
boolean verify(ProtocolVersion protocolVersion,
HandshakeHash handshakeHash, PublicKey publicKey,
SecretKey masterSecret) throws GeneralSecurityException {
String algorithm = publicKey.getAlgorithm();
Signature sig = null;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName());
} else {
sig = getSignature(protocolVersion, algorithm);
}
@@ -1674,15 +1802,15 @@
ProtocolVersion protocolVersion,
HandshakeHash handshakeHash, String algorithm, SecretKey masterKey)
throws SignatureException {
if (algorithm.equals("RSA")) {
- if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+ if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone();
- if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+ 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
@@ -1690,14 +1818,14 @@
RSASignature.setHashes(sig, md5Clone, shaClone);
} else { // TLS1.2+
sig.update(handshakeHash.getAllHandshakeMessages());
}
} else { // DSA, ECDSA
- if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+ if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest shaClone = handshakeHash.getSHAClone();
- if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+ if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
sig.update(shaClone.digest());
} else { // TLS1.2+
@@ -1809,20 +1937,20 @@
@Override
int messageLength() {
int temp = 2;
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord();
}
return temp + signature.length;
}
@Override
void send(HandshakeOutStream s) throws IOException {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
}
s.putBytes16(signature);
@@ -1831,11 +1959,11 @@
@Override
void print(PrintStream s) throws IOException {
s.println("*** CertificateVerify");
if (debug != null && Debug.isOn("verbose")) {
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+ if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName());
}
}
}
@@ -1897,11 +2025,11 @@
*/
Finished(ProtocolVersion protocolVersion, HandshakeInStream input,
CipherSuite cipherSuite) throws IOException {
this.protocolVersion = protocolVersion;
this.cipherSuite = cipherSuite;
- int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36;
+ int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36;
verifyData = new byte[msgLen];
input.read(verifyData);
}
/*
@@ -1930,26 +2058,26 @@
tlsLabel = "server finished";
} else {
throw new RuntimeException("Invalid sender: " + sender);
}
- if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ if (protocolVersion.useTLS10PlusSpec()) {
// TLS 1.0+
try {
byte [] seed;
String prfAlg;
PRF prf;
// Get the KeyGenerator alg and calculate the seed.
- if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
- // TLS 1.2
+ if (protocolVersion.useTLS12PlusSpec()) {
+ // TLS 1.2+ or DTLS 1.2+
seed = handshakeHash.getFinishedHash();
prfAlg = "SunTls12Prf";
prf = cipherSuite.prfAlg;
} else {
- // TLS 1.0/1.1
+ // 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);