Skip to content

Commit fe5cccc

Browse files
author
Bradford Wetmore
committed
8254631: Better support ALPN byte wire values in SunJSSE
Reviewed-by: xuelei, dfuchs
1 parent 541c7f7 commit fe5cccc

File tree

6 files changed

+450
-11
lines changed

6 files changed

+450
-11
lines changed

src/java.base/share/classes/javax/net/ssl/SSLEngine.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,48 @@
336336
* started, an {@code SSLEngine} can not switch between client and server
337337
* modes, even when performing renegotiations.
338338
* <P>
339+
* The ApplicationProtocol {@code String} values returned by the methods
340+
* in this class are in the network byte representation sent by the peer.
341+
* The bytes could be directly compared, or converted to its Unicode
342+
* {code String} format for comparison.
343+
*
344+
* <blockquote><pre>
345+
* String networkString = sslEngine.getHandshakeApplicationProtocol();
346+
* byte[] bytes = networkString.getBytes(StandardCharsets.ISO_8859_1);
347+
*
348+
* //
349+
* // Match using bytes:
350+
* //
351+
* // "http/1.1" (7-bit ASCII values same in UTF-8)
352+
* // MEETEI MAYEK LETTERS "HUK UN I" (Unicode 0xabcd->0xabcf)
353+
* //
354+
* String HTTP1_1 = "http/1.1";
355+
* byte[] HTTP1_1_BYTES = HTTP1_1.getBytes(StandardCharsets.UTF_8);
356+
*
357+
* byte[] HUK_UN_I_BYTES = new byte[] {
358+
* (byte) 0xab, (byte) 0xcd,
359+
* (byte) 0xab, (byte) 0xce,
360+
* (byte) 0xab, (byte) 0xcf};
361+
*
362+
* if ((Arrays.compare(bytes, HTTP1_1_BYTES) == 0 )
363+
* || Arrays.compare(bytes, HUK_UN_I_BYTES) == 0) {
364+
* ...
365+
* }
366+
*
367+
* //
368+
* // Alternatively match using string.equals() if we know the ALPN value
369+
* // was encoded from a {@code String} using a certain character set,
370+
* // for example {@code UTF-8}. The ALPN value must first be properly
371+
* // decoded to a Unicode {@code String} before use.
372+
* //
373+
* String unicodeString = new String(bytes, StandardCharsets.UTF_8);
374+
* if (unicodeString.equals(HTTP1_1)
375+
* || unicodeString.equals("\u005cuabcd\u005cuabce\u005cuabcf")) {
376+
* ...
377+
* }
378+
* </pre></blockquote>
379+
*
380+
* <P>
339381
* Applications might choose to process delegated tasks in different
340382
* threads. When an {@code SSLEngine}
341383
* is created, the current {@link java.security.AccessControlContext}

src/java.base/share/classes/javax/net/ssl/SSLParameters.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -646,6 +646,27 @@ public String[] getApplicationProtocols() {
646646
* requested by the peer, the underlying protocol will determine what
647647
* action to take. (For example, ALPN will send a
648648
* {@code "no_application_protocol"} alert and terminate the connection.)
649+
* <p>
650+
* The {@code String} values must be presented using the network
651+
* byte representation expected by the peer. For example, if an ALPN
652+
* {@code String} should be exchanged using {@code UTF-8}, the
653+
* {@code String} should be converted to its {@code byte[]} representation
654+
* and stored as a byte-oriented {@code String} before calling this method.
655+
*
656+
* <blockquote><pre>
657+
* // MEETEI MAYEK LETTERS HUK UN I (Unicode 0xabcd->0xabcf): 2 bytes
658+
* byte[] bytes = "\u005cuabcd\u005cuabce\u005cuabcf"
659+
* .getBytes(StandardCharsets.UTF_8);
660+
* String HUK_UN_I = new String(bytes, StandardCharsets.ISO_8859_1);
661+
*
662+
* // 0x00-0xFF: 1 byte
663+
* String rfc7301Grease8F = "\u005c008F\u005c008F";
664+
*
665+
* SSLParameters p = sslSocket.getSSLParameters();
666+
* p.setApplicationProtocols(new String[] {
667+
* "h2", "http/1.1", rfc7301Grease8F, HUK_UN_I});
668+
* sslSocket.setSSLParameters(p);
669+
* </pre></blockquote>
649670
*
650671
* @implSpec
651672
* This method will make a copy of the {@code protocols} array.

src/java.base/share/classes/javax/net/ssl/SSLSocket.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -130,6 +130,47 @@
130130
* socket can not switch between client and server modes, even when
131131
* performing renegotiations.
132132
*
133+
* <P> The ApplicationProtocol {@code String} values returned by the methods
134+
* in this class are in the network byte representation sent by the peer.
135+
* The bytes could be directly compared, or converted to its Unicode
136+
* {code String} format for comparison.
137+
*
138+
* <blockquote><pre>
139+
* String networkString = sslSocket.getHandshakeApplicationProtocol();
140+
* byte[] bytes = networkString.getBytes(StandardCharsets.ISO_8859_1);
141+
*
142+
* //
143+
* // Match using bytes:
144+
* //
145+
* // "http/1.1" (7-bit ASCII values same in UTF-8)
146+
* // MEETEI MAYEK LETTERS "HUK UN I" (Unicode 0xabcd->0xabcf)
147+
* //
148+
* String HTTP1_1 = "http/1.1";
149+
* byte[] HTTP1_1_BYTES = HTTP1_1.getBytes(StandardCharsets.UTF_8);
150+
*
151+
* byte[] HUK_UN_I_BYTES = new byte[] {
152+
* (byte) 0xab, (byte) 0xcd,
153+
* (byte) 0xab, (byte) 0xce,
154+
* (byte) 0xab, (byte) 0xcf};
155+
*
156+
* if ((Arrays.compare(bytes, HTTP1_1_BYTES) == 0 )
157+
* || Arrays.compare(bytes, HUK_UN_I_BYTES) == 0) {
158+
* ...
159+
* }
160+
*
161+
* //
162+
* // Alternatively match using string.equals() if we know the ALPN value
163+
* // was encoded from a {@code String} using a certain character set,
164+
* // for example {@code UTF-8}. The ALPN value must first be properly
165+
* // decoded to a Unicode {@code String} before use.
166+
* //
167+
* String unicodeString = new String(bytes, StandardCharsets.UTF_8);
168+
* if (unicodeString.equals(HTTP1_1)
169+
* || unicodeString.equals("\u005cuabcd\u005cuabce\u005cuabcf")) {
170+
* ...
171+
* }
172+
* </pre></blockquote>
173+
*
133174
* @apiNote
134175
* When the connection is no longer needed, the client and server
135176
* applications should each close both sides of their respective connection.

src/java.base/share/classes/sun/security/ssl/AlpnExtension.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@
2727

2828
import java.io.IOException;
2929
import java.nio.ByteBuffer;
30-
import java.nio.charset.StandardCharsets;
30+
import java.nio.charset.Charset;
31+
import java.security.AccessController;
32+
import java.security.PrivilegedAction;
33+
import java.security.Security;
3134
import java.util.Arrays;
3235
import java.util.Collections;
3336
import java.util.LinkedList;
@@ -59,6 +62,20 @@ final class AlpnExtension {
5962

6063
static final SSLStringizer alpnStringizer = new AlpnStringizer();
6164

65+
// Encoding Charset to convert between String and byte[]
66+
static final Charset alpnCharset;
67+
68+
static {
69+
String alpnCharsetString = AccessController.doPrivileged(
70+
(PrivilegedAction<String>) ()
71+
-> Security.getProperty("jdk.tls.alpnCharset"));
72+
if ((alpnCharsetString == null)
73+
|| (alpnCharsetString.length() == 0)) {
74+
alpnCharsetString = "ISO_8859_1";
75+
}
76+
alpnCharset = Charset.forName(alpnCharsetString);
77+
}
78+
6279
/**
6380
* The "application_layer_protocol_negotiation" extension.
6481
*
@@ -101,7 +118,7 @@ private AlpnSpec(HandshakeContext hc,
101118
"extension: empty application protocol name"));
102119
}
103120

104-
String appProtocol = new String(bytes, StandardCharsets.UTF_8);
121+
String appProtocol = new String(bytes, alpnCharset);
105122
protocolNames.add(appProtocol);
106123
}
107124

@@ -168,10 +185,10 @@ public byte[] produce(ConnectionContext context,
168185
return null;
169186
}
170187

171-
// Produce the extension.
188+
// Produce the extension: first find the overall length
172189
int listLength = 0; // ProtocolNameList length
173190
for (String ap : laps) {
174-
int length = ap.getBytes(StandardCharsets.UTF_8).length;
191+
int length = ap.getBytes(alpnCharset).length;
175192
if (length == 0) {
176193
// log the configuration problem
177194
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@@ -223,8 +240,10 @@ public byte[] produce(ConnectionContext context,
223240
byte[] extData = new byte[listLength + 2];
224241
ByteBuffer m = ByteBuffer.wrap(extData);
225242
Record.putInt16(m, listLength);
243+
244+
// opaque ProtocolName<1..2^8-1>;
226245
for (String ap : laps) {
227-
Record.putBytes8(m, ap.getBytes(StandardCharsets.UTF_8));
246+
Record.putBytes8(m, ap.getBytes(alpnCharset));
228247
}
229248

230249
// Update the context.
@@ -414,14 +433,14 @@ public byte[] produce(ConnectionContext context,
414433
}
415434

416435
// opaque ProtocolName<1..2^8-1>, RFC 7301.
417-
int listLen = shc.applicationProtocol.length() + 1;
418-
// 1: length byte
436+
byte[] bytes = shc.applicationProtocol.getBytes(alpnCharset);
437+
int listLen = bytes.length + 1; // 1: length byte
438+
419439
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
420440
byte[] extData = new byte[listLen + 2]; // 2: list length
421441
ByteBuffer m = ByteBuffer.wrap(extData);
422442
Record.putInt16(m, listLen);
423-
Record.putBytes8(m,
424-
shc.applicationProtocol.getBytes(StandardCharsets.UTF_8));
443+
Record.putBytes8(m, bytes);
425444

426445
// Update the context.
427446
shc.conContext.applicationProtocol = shc.applicationProtocol;

src/java.base/share/conf/security/java.security

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,3 +1309,13 @@ jdk.io.permissionsUseCanonicalPath=false
13091309
# System value prevails. The default value of the property is "false".
13101310
#
13111311
#jdk.security.allowNonCaAnchor=true
1312+
1313+
#
1314+
# The default Character set name (java.nio.charset.Charset.forName())
1315+
# for converting TLS ALPN values between byte arrays and Strings.
1316+
# Prior versions of the JDK may use UTF-8 as the default charset. If
1317+
# you experience interoperability issues, setting this property to UTF-8
1318+
# may help.
1319+
#
1320+
# jdk.tls.alpnCharset=UTF-8
1321+
jdk.tls.alpnCharset=ISO_8859_1

0 commit comments

Comments
 (0)