Author: markt
Date: Fri Jan 13 12:50:01 2017
New Revision: 1778575
URL: http://svn.apache.org/viewvc?rev=1778575&view=rev
Log:
Adding ALPN support for JSSE with Java 9
Expand the data extracted from the TLS client hello to include the client
requested ALPN names.
Modified:
tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
Modified:
tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java?rev=1778575&r1=1778574&r2=1778575&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
(original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
Fri Jan 13 12:50:01 2017
@@ -27,7 +27,8 @@ import org.apache.tomcat.util.net.openss
import org.apache.tomcat.util.res.StringManager;
/**
- * This class extracts the SNI host name from a TLS client-hello message.
+ * This class extracts the SNI host name and ALPN protocols from a TLS
+ * client-hello message.
*/
public class TLSClientHelloExtractor {
@@ -37,9 +38,12 @@ public class TLSClientHelloExtractor {
private final ExtractorResult result;
private final List<Cipher> clientRequestedCiphers;
private final String sniValue;
+ private final List<String> clientRequestedApplicationProtocols;
private static final int TLS_RECORD_HEADER_LEN = 5;
+ private static final int TLS_EXTENSION_SERVER_NAME = 0;
+ private static final int TLS_EXTENSION_ALPN = 16;
/**
* Creates the instance of the parser and processes the provided buffer.
The
@@ -59,6 +63,7 @@ public class TLSClientHelloExtractor {
int limit = netInBuffer.limit();
ExtractorResult result = ExtractorResult.NOT_PRESENT;
List<Cipher> clientRequestedCiphers = new ArrayList<>();
+ List<String> clientRequestedApplicationProtocols = new ArrayList<>();
String sniValue = null;
try {
// Switch to read mode.
@@ -116,16 +121,32 @@ public class TLSClientHelloExtractor {
// Extension length
skipBytes(netInBuffer, 2);
- // Read the extensions until we run out of data or find the SNI
- while (netInBuffer.hasRemaining() && sniValue == null) {
- sniValue = readSniExtension(netInBuffer);
- }
- if (sniValue != null) {
- result = ExtractorResult.COMPLETE;
+ // Read the extensions until we run out of data or find the data
+ // we need
+ while (netInBuffer.hasRemaining() &&
+ (sniValue == null ||
clientRequestedApplicationProtocols.size() == 0)) {
+ // Extension type is two byte
+ char extensionType = netInBuffer.getChar();
+ // Extension size is another two bytes
+ char extensionDataSize = netInBuffer.getChar();
+ switch (extensionType) {
+ case TLS_EXTENSION_SERVER_NAME: {
+ sniValue = readSniExtension(netInBuffer);
+ break;
+ }
+ case TLS_EXTENSION_ALPN:
+ readAlpnExtension(netInBuffer,
clientRequestedApplicationProtocols);
+ break;
+ default: {
+ skipBytes(netInBuffer, extensionDataSize);
+ }
+ }
}
+ result = ExtractorResult.COMPLETE;
} finally {
this.result = result;
this.clientRequestedCiphers = clientRequestedCiphers;
+ this.clientRequestedApplicationProtocols =
clientRequestedApplicationProtocols;
this.sniValue = sniValue;
// Whatever happens, return the buffer to its original state
netInBuffer.limit(limit);
@@ -157,6 +178,14 @@ public class TLSClientHelloExtractor {
}
+ public List<String> getClientRequestedApplicationProtocols() {
+ if (result == ExtractorResult.COMPLETE || result ==
ExtractorResult.NOT_PRESENT) {
+ return clientRequestedApplicationProtocols;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
private static ExtractorResult handleIncompleteRead(ByteBuffer bb) {
if (bb.limit() == bb.capacity()) {
// Buffer not big enough
@@ -223,23 +252,30 @@ public class TLSClientHelloExtractor {
private static String readSniExtension(ByteBuffer bb) {
- // SNI extension is type 0
- char extensionType = bb.getChar();
- // Next byte is data size
- char extensionDataSize = bb.getChar();
- if (extensionType == 0) {
- // First 2 bytes are size of server name list (only expecting one)
- // Next byte is type (0 for hostname)
- skipBytes(bb, 3);
- // Next 2 bytes are length of host name
- char serverNameSize = bb.getChar();
- byte[] serverNameBytes = new byte[serverNameSize];
- bb.get(serverNameBytes);
- return new String(serverNameBytes, StandardCharsets.UTF_8);
- } else {
- skipBytes(bb, extensionDataSize);
+ // First 2 bytes are size of server name list (only expecting one)
+ // Next byte is type (0 for hostname)
+ skipBytes(bb, 3);
+ // Next 2 bytes are length of host name
+ char serverNameSize = bb.getChar();
+ byte[] serverNameBytes = new byte[serverNameSize];
+ bb.get(serverNameBytes);
+ return new String(serverNameBytes, StandardCharsets.UTF_8);
+ }
+
+
+ private static void readAlpnExtension(ByteBuffer bb, List<String>
protocolNames) {
+ // First 2 bytes are size of the protocol list
+ char toRead = bb.getChar();
+ byte[] inputBuffer = new byte[255];
+ while (toRead > 0) {
+ // Each list entry has one byte for length followed by a string of
+ // that length
+ int len = bb.get() & 0xFF;
+ bb.get(inputBuffer, 0, len);
+ protocolNames.add(new String(inputBuffer, 0, len,
StandardCharsets.UTF_8));
+ toRead--;
+ toRead -= len;
}
- return null;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]