--- old/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java 2017-10-06 17:48:33.531248114 -0300 +++ new/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java 2017-10-06 17:48:33.432248172 -0300 @@ -106,12 +106,22 @@ byte[] serverRandom = spec.getServerRandom(); if (protocolVersion >= 0x0301) { - byte[] seed = concat(clientRandom, serverRandom); + byte[] label; + byte[] seed; + byte[] extendedMasterSecretSessionHash = + spec.getExtendedMasterSecretSessionHash(); + if (extendedMasterSecretSessionHash != null) { + label = LABEL_EXTENDED_MASTER_SECRET; + seed = extendedMasterSecretSessionHash; + } else { + label = LABEL_MASTER_SECRET; + seed = concat(clientRandom, serverRandom); + } master = ((protocolVersion >= 0x0303) ? - doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48, + doTLS12PRF(premaster, label, seed, 48, spec.getPRFHashAlg(), spec.getPRFHashLength(), spec.getPRFBlockSize()) : - doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48)); + doTLS10PRF(premaster, label, seed, 48)); } else { master = new byte[48]; MessageDigest md5 = MessageDigest.getInstance("MD5"); --- old/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java 2017-10-06 17:48:33.864247915 -0300 +++ new/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java 2017-10-06 17:48:33.765247974 -0300 @@ -55,6 +55,10 @@ static final byte[] LABEL_MASTER_SECRET = // "master secret" { 109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 }; + static final byte[] LABEL_EXTENDED_MASTER_SECRET = // "extended master secret" + { 101, 120, 116, 101, 110, 100, 101, 100, 32, 109, 97, 115, 116, + 101, 114, 32, 115, 101, 99, 114, 101, 116 }; + static final byte[] LABEL_KEY_EXPANSION = // "key expansion" { 107, 101, 121, 32, 101, 120, 112, 97, 110, 115, 105, 111, 110 }; --- old/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java 2017-10-06 17:48:34.199247716 -0300 +++ new/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java 2017-10-06 17:48:34.101247774 -0300 @@ -47,7 +47,7 @@ private final SecretKey premasterSecret; private final int majorVersion, minorVersion; - private final byte[] clientRandom, serverRandom; + private final byte[] clientRandom, serverRandom, extendedMasterSecretSessionHash; private final String prfHashAlg; private final int prfHashLength; private final int prfBlockSize; @@ -64,6 +64,7 @@ * @param minorVersion the minor number of the protocol version * @param clientRandom the client's random value * @param serverRandom the server's random value + * @param extendedMasterSecretSessionHash the session hash for Extended Master Secret * @param prfHashAlg the name of the TLS PRF hash algorithm to use. * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. * @param prfHashLength the output length of the TLS PRF hash algorithm. @@ -79,6 +80,7 @@ public TlsMasterSecretParameterSpec(SecretKey premasterSecret, int majorVersion, int minorVersion, byte[] clientRandom, byte[] serverRandom, + byte[] extendedMasterSecretSessionHash, String prfHashAlg, int prfHashLength, int prfBlockSize) { if (premasterSecret == null) { throw new NullPointerException("premasterSecret must not be null"); @@ -88,6 +90,9 @@ this.minorVersion = checkVersion(minorVersion); this.clientRandom = clientRandom.clone(); this.serverRandom = serverRandom.clone(); + this.extendedMasterSecretSessionHash = + (extendedMasterSecretSessionHash != null ? + extendedMasterSecretSessionHash.clone() : null); this.prfHashAlg = prfHashAlg; this.prfHashLength = prfHashLength; this.prfBlockSize = prfBlockSize; @@ -147,6 +152,17 @@ } /** + * Returns a copy of the Extended Master Secret session hash. + * + * @return a copy of the Extended Master Secret session hash. + */ + public byte[] getExtendedMasterSecretSessionHash() { + return (extendedMasterSecretSessionHash != null ? + extendedMasterSecretSessionHash.clone() : + null); + } + + /** * Obtains the PRF hash algorithm to use in the PRF calculation. * * @return the hash algorithm. --- old/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java 2017-10-06 17:48:34.529247520 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java 2017-10-06 17:48:34.429247579 -0300 @@ -777,7 +777,8 @@ && (type != ExtensionType.EXT_ALPN) && (type != ExtensionType.EXT_RENEGOTIATION_INFO) && (type != ExtensionType.EXT_STATUS_REQUEST) - && (type != ExtensionType.EXT_STATUS_REQUEST_V2)) { + && (type != ExtensionType.EXT_STATUS_REQUEST_V2) + && (type != ExtensionType.EXT_EXTENDED_MASTER_SECRET)) { // Note: Better to check client requested extensions rather // than all supported extensions. fatalSE(Alerts.alert_unsupported_extension, @@ -796,6 +797,35 @@ if (debug != null && Debug.isOn("handshake")) { System.out.println("** " + cipherSuite); } + + if (useExtendedMasterSecretExtension) { + // check Extended Master Secret extension + ExtendedMasterSecretExtension extendedMasterSecretExtension = (ExtendedMasterSecretExtension) + mesg.extensions.get(ExtensionType.EXT_EXTENDED_MASTER_SECRET); + if (extendedMasterSecretExtension != null) { + if (resumingSession) { + if (!session.getUseExtendedMasterSecret()) { + // Full-handshake was not using Extended Master Secret + // However, Extended Master Secret was received on + // session resumption. + throw new SSLHandshakeException( + "Unexpected Extended Master Secret extension on session resumption"); + } + } else { + session.setUseExtendedMasterSecret(); + } + } else { + if (resumingSession) { + if (session.getUseExtendedMasterSecret()) { + // Full-handshake was using Extended Master Secret + // However, Extended Master Secret was not received + // on session resumption. + throw new SSLHandshakeException( + "Extended Master Secret extension missing on session resumption"); + } + } + } + } } /* @@ -1539,6 +1569,15 @@ clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs); } + // add Extended Master Secret extension + if (useExtendedMasterSecretExtension) { + if (maxProtocolVersion.useTLS10PlusSpec()) { + if (!resumingSession || session.getUseExtendedMasterSecret()) { + clientHelloMessage.addExtendedMasterSecretExtension(); + } + } + } + // add server_name extension if (enableSNIExtension) { if (session != null) { --- old/src/java.base/share/classes/sun/security/ssl/ExtensionType.java 2017-10-06 17:48:34.880247311 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/ExtensionType.java 2017-10-06 17:48:34.783247369 -0300 @@ -105,6 +105,10 @@ static final ExtensionType EXT_STATUS_REQUEST_V2 = e(0x0011, "status_request_v2"); // IANA registry value: 17 + // extensions defined in RFC 7627 + static final ExtensionType EXT_EXTENDED_MASTER_SECRET = + e(0x0017, "extended_master_secret"); // IANA registry value: 23 + // extensions defined in RFC 5746 static final ExtensionType EXT_RENEGOTIATION_INFO = e(0xff01, "renegotiation_info"); // IANA registry value: 65281 --- old/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java 2017-10-06 17:48:35.211247114 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java 2017-10-06 17:48:35.112247173 -0300 @@ -389,6 +389,10 @@ extensions.add(signatureAlgorithm); } + void addExtendedMasterSecretExtension() { + extensions.add(new ExtendedMasterSecretExtension()); + } + void addMFLExtension(int maximumPacketSize) { HelloExtension maxFragmentLength = new MaxFragmentLengthExtension(maximumPacketSize); @@ -1441,7 +1445,7 @@ } else { sig = getSignature(privateKey.getAlgorithm()); } - sig.initSign(privateKey); // where is the SecureRandom? + sig.initSign(privateKey, sr); updateSignature(sig, clntNonce, svrNonce); signatureBytes = sig.sign(); --- old/src/java.base/share/classes/sun/security/ssl/Handshaker.java 2017-10-06 17:48:35.565246904 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/Handshaker.java 2017-10-06 17:48:35.467246962 -0300 @@ -225,6 +225,10 @@ Debug.getBooleanProperty( "jdk.tls.rejectClientInitiatedRenegotiation", false); + // To switch off the extended_master_secret extension. + static final boolean useExtendedMasterSecretExtension = + Debug.getBooleanProperty("jsse.useExtendedMasterSecret", true); + // need to dispose the object when it is invalidated boolean invalidated; @@ -1276,10 +1280,15 @@ int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); + byte[] sessionHash = null; + if (session.getUseExtendedMasterSecret()){ + sessionHash = handshakeHash.getFinishedHash(); + } + @SuppressWarnings("deprecation") TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF), - clnt_random.random_bytes, svr_random.random_bytes, + clnt_random.random_bytes, svr_random.random_bytes, sessionHash, prfHashAlg, prfHashLength, prfBlockSize); try { --- old/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java 2017-10-06 17:48:35.915246696 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java 2017-10-06 17:48:35.813246756 -0300 @@ -93,6 +93,8 @@ extension = new CertStatusReqExtension(s, extlen); } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) { extension = new CertStatusReqListV2Extension(s, extlen); + } else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) { + extension = new ExtendedMasterSecretExtension(s, extlen); } else { extension = new UnknownExtension(s, extlen, extType); } --- old/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java 2017-10-06 17:48:36.243246500 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java 2017-10-06 17:48:36.143246560 -0300 @@ -91,6 +91,7 @@ private byte compressionMethod; private CipherSuite cipherSuite; private SecretKey masterSecret; + private boolean useExtendedMasterSecret; /* * Information not part of the SSLv3 protocol spec, but used @@ -196,6 +197,10 @@ } } + void setUseExtendedMasterSecret() { + useExtendedMasterSecret = true; + } + /** * Returns the master secret ... treat with extreme caution! */ @@ -203,6 +208,10 @@ return masterSecret; } + boolean getUseExtendedMasterSecret() { + return useExtendedMasterSecret; + } + void setPeerCertificates(X509Certificate[] peer) { if (peerCerts == null) { peerCerts = peer; --- old/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java 2017-10-06 17:48:36.583246298 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java 2017-10-06 17:48:36.484246357 -0300 @@ -850,6 +850,35 @@ handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); } + if (useExtendedMasterSecretExtension) { + // check Extended Master Secret extension + ExtendedMasterSecretExtension extendedMasterSecretExtension = (ExtendedMasterSecretExtension) + mesg.extensions.get(ExtensionType.EXT_EXTENDED_MASTER_SECRET); + if (extendedMasterSecretExtension != null) { + if (resumingSession) { + if (!session.getUseExtendedMasterSecret()) { + // Full-handshake was not using Extended Master Secret + // However, Extended Master Secret was received on + // session resumption. + throw new SSLHandshakeException( + "Unexpected Extended Master Secret extension on session resumption"); + } + } else { + session.setUseExtendedMasterSecret(); + } + } else { + if (resumingSession) { + if (session.getUseExtendedMasterSecret()) { + // Full-handshake was using Extended Master Secret + // However, Extended Master Secret was not received + // on session resumption. + throw new SSLHandshakeException( + "Extended Master Secret extension missing on session resumption"); + } + } + } + } + m1.cipherSuite = cipherSuite; m1.sessionId = session.getSessionId(); m1.compression_method = session.getCompression(); @@ -886,6 +915,12 @@ m1.extensions.add(maxFragLenExt); } + if (useExtendedMasterSecretExtension) { + if (session.getUseExtendedMasterSecret()) { + m1.extensions.add(new ExtendedMasterSecretExtension()); + } + } + StaplingParameters staplingParams = processStapling(mesg); if (staplingParams != null) { // We now can safely assert status_request[_v2] in our --- /dev/null 2017-10-06 10:29:22.105213047 -0300 +++ new/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java 2017-10-06 17:48:36.840246145 -0300 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * + * 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; + +/** + * Extended Master Secret TLS extension (TLS 1.0+). This extension + * defines how to calculate the TLS connection master secret and + * mitigates some types of man-in-the-middle attacks. + * + * See further information in + * RFC 7627. + * + * @author Martin Balao (mbalao@redhat.com) + */ + +final class ExtendedMasterSecretExtension extends HelloExtension { + + ExtendedMasterSecretExtension() { + super(ExtensionType.EXT_EXTENDED_MASTER_SECRET); + } + + ExtendedMasterSecretExtension(HandshakeInStream s, int len) { + this(); + } + + @Override + int length() { + return 2 + 2; // extension_type + extension_data length + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); // ExtensionType extension_type; + s.putInt16(0); // extension_data length + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Extension " + type); + return sb.toString(); + } +} \ No newline at end of file --- /dev/null 2017-10-06 10:29:22.105213047 -0300 +++ new/test/sun/security/ssl/ClientHandshaker/ExtendedMasterSecretTest.java 2017-10-06 17:48:37.171245949 -0300 @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * + * 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8148421 + * @compile --add-exports java.base/sun.security.util=ALL-UNNAMED --add-exports java.base/sun.security.ssl=ALL-UNNAMED --add-exports java.base/java.security=ALL-UNNAMED ExtendedMasterSecretTest.java + * @run main/othervm ExtendedMasterSecretTest + * @summary test for Extended Master Secret TLS extension. + * @author Martin Balao (mbalao@redhat.com) + * + */ + +import java.io.ByteArrayInputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Base64; +import java.security.KeyStore; +import java.security.ProviderException; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLEngineResult.Status; + +import sun.security.ssl.SSLEngineImpl; +import sun.security.util.HexDumpEncoder; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import javax.net.ssl.SSLEngineResult.HandshakeStatus; + +import java.security.SecureRandomSpi; + +final public class ExtendedMasterSecretTest { + + // Test data + + private static final byte[] expectedMasterKey = { + (byte)0xC3, (byte)0x77, (byte)0x02, (byte)0x3C, (byte)0xA4, (byte)0x81, (byte)0xBE, + (byte)0x1E, (byte)0xE7, (byte)0x33, (byte)0x81, (byte)0x3A, (byte)0x7E, (byte)0xE2, + (byte)0x14, (byte)0x21, (byte)0x4C, (byte)0xFF, (byte)0x45, (byte)0xE8, (byte)0xAD, + (byte)0x27, (byte)0xCD, (byte)0x8B, (byte)0xE2, (byte)0x13, (byte)0x1F, (byte)0xA9, + (byte)0x67, (byte)0x06, (byte)0xEF, (byte)0x39, (byte)0xA1, (byte)0x61, (byte)0xF4, + (byte)0x56, (byte)0xE0, (byte)0xDA, (byte)0x7C, (byte)0x2E, (byte)0xE6, (byte)0xD2, + (byte)0xD0, (byte)0xF5, (byte)0xC6, (byte)0x15, (byte)0xB5, (byte)0x61 + }; + + private static final String ksString = + "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"; + private static KeyStore ks; + private static KeyStore ts; + private static char[] passphrase; + + private static SSLEngine clientSSLEngine; + private static SSLEngine serverSSLEngine; + + // Test methods + + public static void main(String args[]) throws Exception { + + initialize(); + + // SSLEngine code based on RedhandshakeFinished.java + + boolean dataDone = false; + + ByteBuffer clientOut = null; + ByteBuffer clientIn = null; + ByteBuffer serverOut = null; + ByteBuffer serverIn = null; + ByteBuffer cTOs; + ByteBuffer sTOc; + + SSLSession session = clientSSLEngine.getSession(); + int appBufferMax = session.getApplicationBufferSize(); + int netBufferMax = session.getPacketBufferSize(); + + clientIn = ByteBuffer.allocate(appBufferMax + 50); + serverIn = ByteBuffer.allocate(appBufferMax + 50); + + cTOs = ByteBuffer.allocateDirect(netBufferMax); + sTOc = ByteBuffer.allocateDirect(netBufferMax); + + clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); + serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); + + SSLEngineResult clientResult; + SSLEngineResult serverResult; + + while (!dataDone) { + clientResult = clientSSLEngine.wrap(clientOut, cTOs); + runDelegatedTasks(clientResult, clientSSLEngine); + serverResult = serverSSLEngine.wrap(serverOut, sTOc); + runDelegatedTasks(serverResult, serverSSLEngine); + cTOs.flip(); + sTOc.flip(); + + System.out.println("Client -> Network"); + printTlsNetworkPacket("", cTOs); + System.out.println(""); + System.out.println("Server -> Network"); + printTlsNetworkPacket("", sTOc); + + clientResult = clientSSLEngine.unwrap(sTOc, clientIn); + runDelegatedTasks(clientResult, clientSSLEngine); + serverResult = serverSSLEngine.unwrap(cTOs, serverIn); + runDelegatedTasks(serverResult, serverSSLEngine); + + cTOs.compact(); + sTOc.compact(); + + if (!dataDone && (clientOut.limit() == serverIn.position()) && + (serverOut.limit() == clientIn.position())) { + + /* + * A sanity check to ensure we got what was sent. + */ + checkTransfer(serverOut, clientIn); + checkTransfer(clientOut, serverIn); + + dataDone = true; + } + } + + Class SSLSessionImplClass = Class.forName("sun.security.ssl.SSLSessionImpl"); + Method getMasterSecretMethod = SSLSessionImplClass.getDeclaredMethod("getMasterSecret"); + getMasterSecretMethod.setAccessible(true); + + byte[] clientMasterKey = ((SecretKey)getMasterSecretMethod.invoke(clientSSLEngine.getSession())).getEncoded(); + byte[] serverMasterKey = ((SecretKey)getMasterSecretMethod.invoke(serverSSLEngine.getSession())).getEncoded(); + + System.out.println("Client Extended Master Key"); + showBinaryPacket(clientMasterKey); + System.out.println("......................."); + + System.out.println("Server Extended Master Key"); + showBinaryPacket(serverMasterKey); + System.out.println("......................."); + + if (!Arrays.equals(clientMasterKey, expectedMasterKey) || + !Arrays.equals(serverMasterKey, expectedMasterKey)) { + throw new Exception("Master key does not match expected master key."); + } + + System.out.println("Test passed - OK"); + } + + static void printTlsNetworkPacket(String prefix, ByteBuffer bb) { + HexDumpEncoder dump = new HexDumpEncoder(); + System.out.println(prefix); + try { + dump.encodeBuffer(bb.slice(), System.out); + } catch (Exception e) { + // ignore + } + System.out.flush(); + } + + private static void checkTransfer(ByteBuffer a, ByteBuffer b) + throws Exception { + a.flip(); + b.flip(); + + if (!a.equals(b)) { + throw new Exception("Data didn't transfer cleanly"); + } else { + } + + a.position(a.limit()); + b.position(b.limit()); + a.limit(a.capacity()); + b.limit(b.capacity()); + } + + private static void runDelegatedTasks(SSLEngineResult result, + SSLEngine engine) throws Exception { + + if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + runnable.run(); + } + HandshakeStatus hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new Exception( + "handshake shouldn't need additional tasks"); + } + } + } + + private static void initialize() throws Exception { + ks = KeyStore.getInstance("JKS"); + ts = KeyStore.getInstance("JKS"); + passphrase = "123456".toCharArray(); + ks.load(new ByteArrayInputStream(Base64.getDecoder().decode(ksString)), passphrase); + ts.load(new ByteArrayInputStream(Base64.getDecoder().decode(ksString)), passphrase); + + clientSSLEngine = createSSLEngine(true); + serverSSLEngine = createSSLEngine(false); + + String[] preferredSuites = new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" }; + + serverSSLEngine.setEnabledCipherSuites(preferredSuites); + clientSSLEngine.setEnabledCipherSuites(preferredSuites); + } + + static private SSLEngine createSSLEngine(boolean client) throws Exception { + SSLEngine ssle; + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); + kmf.init(ks, passphrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(ts); + + SSLContext sslCtx = SSLContext.getInstance("TLSv1.2"); + + SecureRandom random = new SecureRandom(); + + // Patch to remove randomness so we can assert obtained values against + // pre-computed / expected values. + Class randomClass = random.getClass(); + Field secureRandomSpiField = randomClass.getDeclaredField("secureRandomSpi"); + secureRandomSpiField.setAccessible(true); + secureRandomSpiField.set(random, new NoRandom()); + + sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), random); + + ssle = sslCtx.createSSLEngine("localhost", 443); + ssle.setUseClientMode(client); + SSLParameters sslParameters = ssle.getSSLParameters(); + ssle.setSSLParameters(sslParameters); + + return ssle; + } + + private static void showBinaryPacket(byte[] binaryPacket) throws Exception { + int columnCounter = 0; + for (byte binaryPacketByte : binaryPacket) { + System.out.print(String.format("%02X ", binaryPacketByte)); + if ((columnCounter + 1) % 8 == 0) { + System.out.print("\n"); + } + columnCounter++; + } + System.out.print("\n"); + } + + private static class NoRandom extends SecureRandomSpi { + @Override + protected void engineSetSeed(byte[] seed) { + } + + @Override + protected void engineNextBytes(byte[] bytes) { + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte)0; + } + } + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return new byte[numBytes]; + } + } +}