Skip to content

Instantly share code, notes, and snippets.

@swankjesse
Created December 27, 2014 19:25
Show Gist options
  • Select an option

  • Save swankjesse/32d08aa551d470cb137a to your computer and use it in GitHub Desktop.

Select an option

Save swankjesse/32d08aa551d470cb137a to your computer and use it in GitHub Desktop.
Demonstrates a bug in Jetty ALPN where resumed sessions don't get protocol callbacks.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.eclipse.jetty.alpn.ALPN;
public class SessionResumeFail {
/** Returns an SSL context that connects to localhost. */
private SSLContext sslContext() throws Exception {
String privateKeyBase64 = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAN4n2hJGX77UpGt0gywS"
+ "lE5Hxl9efYv9chIvDp4+MpimCrVGbS2V+D2xeBIBVeMKEpAJIbqbgs307+7aAKJrN9S58J+bAZdDwsJ/gIZmiQ/+"
+ "kIlsHTIqXjFQ64F1D9bjPWNrkuQQFQG/bz4EWhEn5BgKrqBofFh1lhIbVF4OHQI/AgMBAAECgYBw4Sb5KjD0Me/p"
+ "eaY8+qPIsselJcn9G6aefrKazVfAIH5IZaSMCSu9jTNJUsxlNHUCIuG0g9gO9moDn1m7LWeMCR34r9LcNXdeM9M4"
+ "Tbw59WFPaJgR1s8iPogCq0Ej04HjyiItXPFpj4tBw6FJLDDKJpr/1M3qIpudkmJU5YUbYQJBAPIeKfuGwnUB14OI"
+ "SuTwVR65uemoXEqzBZBJrh8YvRmZMtXejKubO+vMRbGkat3OQL9bsdZP5Vmuduvro2ngVFsCQQDq5K5IZays5neV"
+ "OHBj4yYuoM5jT1TiB2pBhZ4sZEXhF0uVcX48yS7Fi+WavDF8l/2tAp97SBpL/ETx2MpTWN7tAkBBW2kjz7XQVgXy"
+ "KKlm8YVYvPP1og//ziGTWIBbPpdVQKibWO8KJ+Zd+Y9aq7J5W4LY5Qy1eG3F7rWFa9955id/AkBmUsoNJaWMCDWN"
+ "nnFvQfNPGqPtpRBgCkTiWBeoVOIQBPrbFf6c8jLawW968YJJOVi7dkoNBULXWQSbqgmxNySdAkAyCtczXfA6U8Xn"
+ "p9GWFO0fujHH3MAzeDYkGb5KlWXxse291eTXboHkaI6CYwskSig8yqKZg6195IWy2E6Imbaj";
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(privateKeyBase64));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
String certificateBase64 = "MIIBnjCCAQegAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlsb2NhbGh"
+ "vc3QwIBcNMTQxMjI3MTkwNDA2WhgPMjExNDEyMDMxOTA0MDZaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgk"
+ "qhkiG9w0BAQEFAAOBjQAwgYkCgYEA3ifaEkZfvtSka3SDLBKUTkfGX159i/1yEi8Onj4ymKYKtUZtLZX4PbF4EgF"
+ "V4woSkAkhupuCzfTv7toAoms31Lnwn5sBl0PCwn+AhmaJD/6QiWwdMipeMVDrgXUP1uM9Y2uS5BAVAb9vPgRaESf"
+ "kGAquoGh8WHWWEhtUXg4dAj8CAwEAATANBgkqhkiG9w0BAQsFAAOBgQAlTNKMihtmctR08lCklZafa2ZqmC2Jeud"
+ "LNnBB0nWmzv5RhU5BwI4YdskKY90kZaq6uAJraE8LGi7V1d/mvG+7SFEbM3mEzu+JE5Q4BYtURcH1ofFK+5bjxjF"
+ "F8Db4zoLyHBqUMrknjkLh84amDyysxhvalIbFNGTVq0mXwXpgeQ==";
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate certificate = certificateFactory.generateCertificate(
new ByteArrayInputStream(Base64.getDecoder().decode(certificateBase64)));
Certificate[] certificateChain = { certificate };
char[] password = "password".toCharArray();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
keyStore.setKeyEntry("private", privateKey, password, certificateChain);
keyStore.setCertificateEntry("cert", certificate);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
new SecureRandom());
return sslContext;
}
void test() throws Exception {
Server server = new Server();
new Thread(server).start();
SSLSocketFactory socketFactory = sslContext().getSocketFactory();
for (int i = 0; i < 3; i++) {
System.out.println(i);
Socket socket = new Socket("localhost", server.serverSocket.getLocalPort());
SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(
socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
ClientSocketHandler clientSocketHandler = new ClientSocketHandler(sslSocket);
clientSocketHandler.run();
}
server.serverSocket.close();
}
class Server implements Runnable {
final ServerSocket serverSocket;
Server() throws IOException {
serverSocket = ServerSocketFactory.getDefault().createServerSocket(0);
}
@Override public void run() {
try {
SSLSocketFactory socketFactory = sslContext().getSocketFactory();
while (true) {
Socket socket = serverSocket.accept();
SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(
socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
new Thread(new ServerSocketHandler(sslSocket)).start();
}
} catch (SocketException e) {
System.out.println("[S] shutdown: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ClientSocketHandler implements Runnable, ALPN.ClientProvider {
final SSLSocket socket;
String protocol = "none";
public ClientSocketHandler(SSLSocket socket) {
this.socket = socket;
}
@Override public void run() {
try {
ALPN.put(socket, this);
socket.startHandshake();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = reader.readLine();
System.out.println("[C] read: " + s + " using protocol " + protocol);
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override public boolean supports() {
System.out.println("[C] " + socket + " supports ALPN");
return true;
}
@Override public List<String> protocols() {
List<String> protocols = Arrays.asList("foo", "bar");
System.out.println("[C] " + socket + " protocols: " + protocols);
return protocols;
}
@Override public void unsupported() {
protocol = "unsupported";
ALPN.remove(socket);
System.out.println("[C] " + socket + " unsupported");
}
@Override public void selected(String protocol) {
this.protocol = protocol;
ALPN.remove(socket);
System.out.println("[C] " + socket + " selected " + protocol);
}
}
static class ServerSocketHandler implements Runnable, ALPN.ServerProvider {
final SSLSocket socket;
String protocol = "none";
public ServerSocketHandler(SSLSocket socket) {
this.socket = socket;
}
@Override public void run() {
try {
socket.setUseClientMode(false);
ALPN.put(socket, this);
socket.startHandshake();
socket.getOutputStream().write(protocol.getBytes());
socket.getOutputStream().close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override public void unsupported() {
protocol = "unsupported";
System.out.println("[S] " + socket + " unsupported");
ALPN.remove(socket);
}
@Override public String select(List<String> list) {
protocol = list.get(0);
System.out.println("[S] " + socket + " selected " + protocol + " from " + list);
ALPN.remove(socket);
return protocol;
}
}
public static void main(String[] args) throws Exception {
new SessionResumeFail().test();
}
}
@swankjesse

Copy link
Copy Markdown
Author

Executing this with java version "1.8.0_25" and ALPN 8.0.0.v20140317. Fixed in more recent versions of ALPN.

0
[C] 5c7fa833[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=59361]] supports ALPN
[C] 5c7fa833[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=59361]] protocols: [foo, bar]
[S] 53222672[SSL_NULL_WITH_NULL_NULL: Socket[addr=/127.0.0.1,port=59361,localport=62214]] selected foo from [foo, bar]
[C] 5c7fa833[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=59361]] selected foo
[C] read: foo using protocol foo
1
[C] 7cd62f43[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=52723]] supports ALPN
[C] 7cd62f43[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=52723]] protocols: [foo, bar]
[S] 5314af94[SSL_NULL_WITH_NULL_NULL: Socket[addr=/127.0.0.1,port=52723,localport=62214]] selected foo from [foo, bar]
[C] read: foo using protocol none
2
[C] 5622fdf[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=56697]] supports ALPN
[C] 5622fdf[SSL_NULL_WITH_NULL_NULL: Socket[addr=localhost/127.0.0.1,port=62214,localport=56697]] protocols: [foo, bar]
[S] 4a648883[SSL_NULL_WITH_NULL_NULL: Socket[addr=/127.0.0.1,port=56697,localport=62214]] selected foo from [foo, bar]
[C] read: foo using protocol none
[S] shutdown: Socket closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment