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;
public abstract class HandshakeMessage {
public static final Debug debug = Debug.getInstance("ssl");
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_verify_request = 3;
static final byte ht_new_session_ticket = 4;
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_finished = 20;
static final byte ht_certificate_url = 21;
static final byte ht_certificate_status = 22;
static final byte ht_supplemental_data = 23;
static final byte ht_not_applicable = -1;
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);
HandshakeMessage() {
}
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;
}
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();
}
abstract int messageType();
abstract int messageLength();
abstract void send(HandshakeOutStream s) throws IOException;
abstract void print(PrintStream p) throws IOException;
static final class HelloRequest extends HandshakeMessage {
@Override
int messageType() { return ht_hello_request; }
HelloRequest() { }
HelloRequest(HandshakeInStream in) throws IOException
{
}
@Override
int messageLength() { return 0; }
@Override
void send(HandshakeOutStream out) throws IOException
{
}
@Override
void print(PrintStream out) throws IOException
{
out.println("*** HelloRequest (empty)");
}
}
static final class HelloVerifyRequest extends HandshakeMessage {
ProtocolVersion protocolVersion;
byte[] cookie;
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();
HelloCookieManager.checkCookie(protocolVersion, cookie);
}
@Override
int messageType() {
return ht_hello_verify_request;
}
@Override
int messageLength() {
return 2 + cookie.length;
}
@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);
}
}
}
static final class ClientHello extends HandshakeMessage {
ProtocolVersion protocolVersion;
RandomCookie clnt_random;
SessionId sessionId;
byte[] cookie;
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;
}
void addRenegotiationInfoExtension(byte[] clientVerifyData) {
HelloExtension renegotiationInfo = new RenegotiationInfoExtension(
clientVerifyData, new byte[0]);
extensions.add(renegotiationInfo);
}
void addSNIExtension(List<SNIServerName> serverNames) {
try {
extensions.add(new ServerNameExtension(serverNames));
} catch (IOException ioe) {
}
}
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) {
HandshakeOutStream hos = new HandshakeOutStream(null);
try {
send(hos, false);
} catch (IOException ioe) {
}
cookieDigest.update(hos.toByteArray());
}
void addCertStatusRequestExtension() {
extensions.add(new CertStatusReqExtension(StatusRequestType.OCSP,
new OCSPStatusRequest()));
}
void addCertStatusReqListV2Extension() {
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));
}
void addALPNExtension(String[] applicationProtocols) throws SSLException {
extensions.add(new ALPNExtension(applicationProtocols));
}
@Override
int messageType() { return ht_client_hello; }
@Override
int messageLength() {
return (2 + 32 + 1 + 2 + 1
+ sessionId.length()
+ (isDTLS ? (1 + cookie.length) : 0)
+ (cipherSuites.size() * 2)
+ compression_methods.length)
+ extensions.length();
}
@Override
void send(HandshakeOutStream s) throws IOException {
send(s, true);
}
@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);
}
}
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() {
}
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()
{
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("***");
}
}
}
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();
}
}
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; }
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) {
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 {
encodedResponses.add(new byte[0]);
encodedResponsesLen += 3;
}
}
} else {
throw new IllegalArgumentException(
"Unsupported StatusResponseType: " + statusType);
}
}
CertificateStatus(HandshakeInStream input) throws IOException {
encodedResponsesLen = 0;
encodedResponses = new ArrayList<>();
statusType = StatusRequestType.get(input.getInt8());
if (statusType == StatusRequestType.OCSP) {
byte[] respDER = input.getBytes24();
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;
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);
}
}
@Override
int messageLength() {
int len = 1;
if (messageLength == -1) {
if (statusType == StatusRequestType.OCSP) {
len += encodedResponsesLen;
} else if (statusType == StatusRequestType.OCSP_MULTI) {
len += 3 + encodedResponsesLen;
}
messageLength = len;
}
return messageLength;
}
@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 {
throw new SSLHandshakeException("Unsupported status_type: " +
statusType.id);
}
}
@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>");
}
}
}
}
}
StatusRequestType getType() {
return statusType;
}
List<byte[]> getResponses() {
return Collections.unmodifiableList(encodedResponses);
}
}
abstract static class ServerKeyExchange extends HandshakeMessage
{
@Override
int messageType() { return ht_server_key_exchange; }
}
static final
class RSA_ServerKeyExchange extends ServerKeyExchange
{
private byte[] rsa_modulus;
private byte[] rsa_exponent;
private Signature signature;
private byte[] signatureBytes;
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);
}
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();
}
RSA_ServerKeyExchange(HandshakeInStream input)
throws IOException, NoSuchAlgorithmException {
signature = RSASignature.getInstance();
rsa_modulus = input.getBytes16();
rsa_exponent = input.getBytes16();
signatureBytes = input.getBytes16();
}
PublicKey getPublicKey() {
try {
KeyFactory kfac = JsseJce.getKeyFactory("RSA");
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);
}
}
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);
}
}
}
static final
class DH_ServerKeyExchange extends ServerKeyExchange
{
private static final boolean dhKeyExchangeFix =
Debug.getBooleanProperty("com.sun.net.ssl.dhKeyExchangeFix", true);
private byte[] dh_p;
private byte[] dh_g;
private byte[] dh_Ys;
private byte[] signature;
ProtocolVersion protocolVersion;
private SignatureAndHashAlgorithm preferableSignatureAlgorithm;
DH_ServerKeyExchange(DHCrypt obj, ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
this.preferableSignatureAlgorithm = null;
setValues(obj);
signature = null;
}
DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte[] clntNonce,
byte[] svrNonce, SecureRandom sr,
SignatureAndHashAlgorithm signAlgorithm,
ProtocolVersion protocolVersion) throws GeneralSecurityException {
this.protocolVersion = protocolVersion;
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();
}
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;
}
DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey,
byte[] clntNonce, byte[] svrNonce, int messageSize,
Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs,
ProtocolVersion protocolVersion)
throws IOException, GeneralSecurityException {
this.protocolVersion = protocolVersion;
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)));
if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8();
int signature = input.getInt8();
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.valueOf(hash, signature, 0);
if (!localSupportedSignAlgs.contains(
preferableSignatureAlgorithm)) {
throw new SSLHandshakeException(
"Unsupported SignatureAndHashAlgorithm in " +
"ServerKeyExchange message: " +
preferableSignatureAlgorithm);
}
} else {
this.preferableSignatureAlgorithm = null;
}
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");
}
}
BigInteger getModulus() {
return new BigInteger(1, dh_p);
}
BigInteger getBase() {
return new BigInteger(1, dh_g);
}
BigInteger getServerPublicKey() {
return new BigInteger(1, dh_Ys);
}
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;
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");
}
}
}
}
static final
class ECDH_ServerKeyExchange extends ServerKeyExchange {
private static final int CURVE_EXPLICIT_PRIME = 1;
private static final int CURVE_EXPLICIT_CHAR2 = 2;
private static final int CURVE_NAMED_CURVE = 3;
private int groupId;
private byte[] pointBytes;
private byte[] signatureBytes;
private ECPublicKey publicKey;
ProtocolVersion protocolVersion;
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) ){
throw new SSLHandshakeException(
"Unnamed EC parameter spec: " + params);
}
groupId = namedGroup.id;
if (privateKey == null) {
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();
}
ECDH_ServerKeyExchange(HandshakeInStream input, PublicKey signingKey,
byte[] clntNonce, byte[] svrNonce,
Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs,
ProtocolVersion protocolVersion)
throws IOException, GeneralSecurityException {
this.protocolVersion = protocolVersion;
int curveType = input.getInt8();
ECParameterSpec parameters;
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) {
return;
}
if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8();
int signature = input.getInt8();
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.valueOf(hash, signature, 0);
if (!localSupportedSignAlgs.contains(
preferableSignatureAlgorithm)) {
throw new SSLHandshakeException(
"Unsupported SignatureAndHashAlgorithm in " +
"ServerKeyExchange message: " +
preferableSignatureAlgorithm);
}
}
signatureBytes = input.getBytes16();
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");
}
}
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 {
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() + ">");
}
}
static final
class CertificateRequest extends HandshakeMessage
{
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;
static final int cct_rsa_ephemeral_dh = 5;
static final int cct_dss_ephemeral_dh = 6;
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;
DistinguishedName[] authorities;
ProtocolVersion protocolVersion;
private Collection<SignatureAndHashAlgorithm> algorithms;
private int algorithmsLen;
CertificateRequest(X509Certificate[] ca, KeyExchange keyExchange,
Collection<SignatureAndHashAlgorithm> signAlgs,
ProtocolVersion protocolVersion) throws IOException {
this.protocolVersion = protocolVersion;
authorities = new DistinguishedName[ca.length];
for (int i = 0; i < ca.length; i++) {
X500Principal x500Principal = ca[i].getSubjectX500Principal();
authorities[i] = new DistinguishedName(x500Principal);
}
this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
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;
types = input.getBytes8();
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) {
int hash = input.getInt8();
int signature = input.getInt8();
SignatureAndHashAlgorithm algorithm =
SignatureAndHashAlgorithm.valueOf(hash, signature,
++sequence);
algorithms.add(algorithm);
remains -= 2;
}
if (remains != 0) {
throw new SSLProtocolException(
"Invalid supported_signature_algorithms field. remains: " +
remains);
}
} else {
algorithms = new ArrayList<SignatureAndHashAlgorithm>();
algorithmsLen = 0;
}
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 {
output.putBytes8(types);
if (protocolVersion.useTLS12PlusSpec()) {
output.putInt16(algorithmsLen);
for (SignatureAndHashAlgorithm algorithm : algorithms) {
output.putInt8(algorithm.getHashValue());
output.putInt8(algorithm.getSignatureValue());
}
}
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);
}
}
}
}
}
static final
class ServerHelloDone extends HandshakeMessage
{
@Override
int messageType() { return ht_server_hello_done; }
ServerHelloDone() { }
ServerHelloDone(HandshakeInStream input)
{
}
@Override
int messageLength()
{
return 0;
}
@Override
void send(HandshakeOutStream s) throws IOException
{
}
@Override
void print(PrintStream s) throws IOException
{
s.println("*** ServerHelloDone");
}
}
static final class CertificateVerify extends HandshakeMessage {
private byte[] signature;
ProtocolVersion protocolVersion;
private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
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();
}
CertificateVerify(HandshakeInStream input,
Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs,
ProtocolVersion protocolVersion) throws IOException {
this.protocolVersion = protocolVersion;
if (protocolVersion.useTLS12PlusSpec()) {
int hashAlg = input.getInt8();
int signAlg = input.getInt8();
preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0);
if (!localSupportedSignAlgs.contains(
preferableSignatureAlgorithm)) {
throw new SSLHandshakeException(
"Unsupported SignatureAndHashAlgorithm in " +
"CertificateVerify message: " + preferableSignatureAlgorithm);
}
}
signature = input.getBytes16();
}
SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() {
return preferableSignatureAlgorithm;
}
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);
}
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);
}
}
private static void updateSignature(Signature sig,
ProtocolVersion protocolVersion,
HandshakeHash handshakeHash, String algorithm, SecretKey masterKey)
throws SignatureException {
if (algorithm.equals("RSA")) {
if (!protocolVersion.useTLS12PlusSpec()) {
MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone();
if (!protocolVersion.useTLS10PlusSpec()) {
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
RSASignature.setHashes(sig, md5Clone, shaClone);
} else {
sig.update(handshakeHash.getAllHandshakeMessages());
}
} else {
if (!protocolVersion.useTLS12PlusSpec()) {
MessageDigest shaClone = handshakeHash.getSHAClone();
if (!protocolVersion.useTLS10PlusSpec()) {
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
}
sig.update(shaClone.digest());
} else {
sig.update(handshakeHash.getAllHandshakeMessages());
}
}
}
private static void updateDigest(MessageDigest md,
byte[] pad1, byte[] pad2,
SecretKey masterSecret) {
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());
}
}
}
}
static final class Finished extends HandshakeMessage {
static final int CLIENT = 1;
static final int SERVER = 2;
private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
private byte[] verifyData;
private ProtocolVersion protocolVersion;
private CipherSuite cipherSuite;
Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash,
int sender, SecretKey master, CipherSuite cipherSuite) {
this.protocolVersion = protocolVersion;
this.cipherSuite = cipherSuite;
verifyData = getFinished(handshakeHash, sender, master);
}
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);
}
boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) {
byte[] myFinished = getFinished(handshakeHash, sender, master);
return MessageDigest.isEqual(myFinished, verifyData);
}
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()) {
try {
byte[] seed;
String prfAlg;
PRF prf;
if (protocolVersion.useTLS12PlusSpec()) {
seed = handshakeHash.getFinishedHash();
prfAlg = "SunTls12Prf";
prf = cipherSuite.prfAlg;
} else {
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();
@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 {
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) {
throw new RuntimeException("Digest failed", e);
}
return finished;
}
}
private static void updateDigest(MessageDigest md, byte[] sender,
byte[] pad1, byte[] pad2, SecretKey masterSecret) {
md.update(sender);
CertificateVerify.updateDigest(md, pad1, pad2, masterSecret);
}
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("***");
}
}
}
}