Skip to content

Instantly share code, notes, and snippets.

@swankjesse
Created March 11, 2019 02:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save swankjesse/589e3e1c4a566e4a9aaec66f0f3a6223 to your computer and use it in GitHub Desktop.
Save swankjesse/589e3e1c4a566e4a9aaec66f0f3a6223 to your computer and use it in GitHub Desktop.
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Collection;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
/**
* This test demonstrates that {@link SSLSession#getPeerCertificates} throws on Java 11 when a
* handshake was negotiated with the pre_shared_key extension.
*
* <p>This test completes normally on Java 8 but crashes on Java 11.
*/
public final class PreSharedKeyTest {
private char[] password = "password".toCharArray(); // Any password will work.
public static void main(String[] args) throws Exception {
PreSharedKeyTest preSharedKeyTest = new PreSharedKeyTest();
int port = preSharedKeyTest.runServer();
preSharedKeyTest.runClient(port);
}
int runServer() throws Exception {
SSLSocketFactory sslSocketFactory = newSslSocketFactory(true);
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress("localhost", 0), 50);
new Thread("server") {
@Override public void run() {
try {
for (int i = 0; i < 2; i++) {
serverHandshake(sslSocketFactory, serverSocket);
}
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
return serverSocket.getLocalPort();
}
void serverHandshake(
SSLSocketFactory sslSocketFactory, ServerSocket serverSocket) throws Exception {
try (Socket rawSocket = serverSocket.accept();
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, null, true)) {
sslSocket.setUseClientMode(false);
sslSocket.startHandshake();
}
}
void runClient(int port) throws Exception {
SSLSocketFactory sslSocketFactory = newSslSocketFactory(false);
clientHandshake(sslSocketFactory, "localhost", port);
clientHandshake(sslSocketFactory, "localhost", port);
}
void clientHandshake(SSLSocketFactory sslSocketFactory, String host, int port) throws Exception {
try (Socket rawSocket = SocketFactory.getDefault().createSocket(host, port);
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, host, port, true)) {
if (false) {
// Workaround by removing the `if (false)` here.
sslSocket.setEnabledProtocols(new String[] { "TLSv1.2" });
}
sslSocket.startHandshake();
SSLSession session = sslSocket.getSession();
sslSocket.getInputStream().read();
Certificate[] peerCertificates = session.getPeerCertificates(); // Crash here on Java 11.
System.out.println("handshake success: peer="
+ ((X509Certificate) peerCertificates[0]).getSubjectX500Principal().getName());
}
}
SSLSocketFactory newSslSocketFactory(boolean server) throws Exception {
Certificate certificate = decodePem(""
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBMjCB2qADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMTCWxvY2FsaG9z\n"
+ "dDAgFw0xOTAzMTEwMDU3MzVaGA8yMTE5MDIxNTAwNTczNVowFDESMBAGA1UEAxMJ\n"
+ "bG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFmkkkDjWmtErc3NI\n"
+ "7WnUXDHz8TuBPHVyk1OPEexcQhIFaabFCqO6CLwwiEm3Nmcbkw2c1Y0MKQXSLyPd\n"
+ "uBF4EaMbMBkwFwYDVR0RAQH/BA0wC4IJbG9jYWxob3N0MAoGCCqGSM49BAMCA0cA\n"
+ "MEQCIC/3TRDB7PUtDo685hjNhWMrOZADvl8ZXFxTLBWoeRmwAiBVkoKiATUShtqo\n"
+ "aMx+zeozvmB//hu5LL2Ll/WbbWSWVQ==\n"
+ "-----END CERTIFICATE-----");
X509TrustManager trustManager = newTrustManager(certificate);
KeyManager[] keyManagers = null;
if (server) {
PrivateKey privateKey = decodePkcs8("MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJz"
+ "AlAgEBBCBOV/y8HlypWRB6axJz9uoevM+Oe/8IzTBW/OzCPNy04A==");
keyManagers = new KeyManager[] { newKeyManager(certificate, privateKey) };
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, new TrustManager[] {trustManager}, null);
return sslContext.getSocketFactory();
}
Certificate decodePem(String pem) throws CertificateException {
Collection<? extends Certificate> certificates = CertificateFactory.getInstance("X.509")
.generateCertificates(new ByteArrayInputStream(pem.getBytes(StandardCharsets.US_ASCII)));
return certificates.iterator().next();
}
PrivateKey decodePkcs8(String base64pkcs8) throws Exception {
byte[] privateKeyBytes = Base64.getDecoder().decode(base64pkcs8);
return KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
}
X509KeyManager newKeyManager(Certificate certificate, PrivateKey privateKey) throws Exception {
KeyStore keyStore = newEmptyKeyStore();
keyStore.setKeyEntry("private", privateKey, password, new Certificate[] {certificate});
KeyManagerFactory factory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
factory.init(keyStore, password);
return (X509KeyManager) factory.getKeyManagers()[0];
}
X509TrustManager newTrustManager(Certificate certificate) throws Exception {
KeyStore keyStore = newEmptyKeyStore();
keyStore.setCertificateEntry("localhost", certificate);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
return (X509TrustManager) trustManagerFactory.getTrustManagers()[0];
}
KeyStore newEmptyKeyStore() throws Exception {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment