Skip to content

Commit 6ddc107

Browse files
committed
Add extraction of group and signature information from client hello
The information should be enough to select which certificate to present to the client.
1 parent e37f9eb commit 6ddc107

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.apache.tomcat.util.buf.HexUtils;
3030
import org.apache.tomcat.util.http.parser.HttpParser;
3131
import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
32+
import org.apache.tomcat.util.net.openssl.ciphers.Group;
33+
import org.apache.tomcat.util.net.openssl.ciphers.SignatureAlgorithm;
3234
import org.apache.tomcat.util.res.StringManager;
3335

3436
/**
@@ -45,10 +47,14 @@ public class TLSClientHelloExtractor {
4547
private final String sniValue;
4648
private final List<String> clientRequestedApplicationProtocols;
4749
private final List<String> clientRequestedProtocols;
50+
private final List<Group> clientSupportedGroups;
51+
private final List<SignatureAlgorithm> clientSignatureAlgorithms;
4852

4953
private static final int TLS_RECORD_HEADER_LEN = 5;
5054

5155
private static final int TLS_EXTENSION_SERVER_NAME = 0;
56+
private static final int TLS_EXTENSION_SUPPORTED_GROUPS = 10;
57+
private static final int TLS_EXTENSION_SIGNATURE_ALGORITHMS = 13;
5258
private static final int TLS_EXTENSION_ALPN = 16;
5359
private static final int TLS_EXTENSION_SUPPORTED_VERSION = 43;
5460

@@ -77,6 +83,8 @@ public TLSClientHelloExtractor(ByteBuffer netInBuffer) throws IOException {
7783
List<String> clientRequestedCipherNames = new ArrayList<>();
7884
List<String> clientRequestedApplicationProtocols = new ArrayList<>();
7985
List<String> clientRequestedProtocols = new ArrayList<>();
86+
List<Group> clientSupportedGroups = new ArrayList<>();
87+
List<SignatureAlgorithm> clientSignatureAlgorithms = new ArrayList<>();
8088
String sniValue = null;
8189
try {
8290
// Switch to read mode.
@@ -158,6 +166,12 @@ public TLSClientHelloExtractor(ByteBuffer netInBuffer) throws IOException {
158166
sniValue = readSniExtension(netInBuffer);
159167
break;
160168
}
169+
case TLS_EXTENSION_SUPPORTED_GROUPS:
170+
readSupportedGroups(netInBuffer, clientSupportedGroups);
171+
break;
172+
case TLS_EXTENSION_SIGNATURE_ALGORITHMS:
173+
readSignatureAlgorithms(netInBuffer, clientSignatureAlgorithms);
174+
break;
161175
case TLS_EXTENSION_ALPN:
162176
readAlpnExtension(netInBuffer, clientRequestedApplicationProtocols);
163177
break;
@@ -182,6 +196,14 @@ public TLSClientHelloExtractor(ByteBuffer netInBuffer) throws IOException {
182196
this.clientRequestedApplicationProtocols = clientRequestedApplicationProtocols;
183197
this.sniValue = sniValue;
184198
this.clientRequestedProtocols = clientRequestedProtocols;
199+
this.clientSupportedGroups = clientSupportedGroups;
200+
this.clientSignatureAlgorithms = clientSignatureAlgorithms;
201+
if (log.isTraceEnabled()) {
202+
log.trace("TLS Client Hello: " + clientRequestedCiphers + " Names " + clientRequestedCipherNames +
203+
" Protocols " + clientRequestedApplicationProtocols + " sniValue " + sniValue +
204+
" clientRequestedProtocols " + clientRequestedProtocols + " clientSupportedGroups " + clientSupportedGroups +
205+
" clientSignatureAlgorithms " + clientSignatureAlgorithms);
206+
}
185207
// Whatever happens, return the buffer to its original state
186208
netInBuffer.limit(limit);
187209
netInBuffer.position(pos);
@@ -413,6 +435,34 @@ private static void readSupportedVersions(ByteBuffer bb, List<String> protocolNa
413435
}
414436

415437

438+
private static void readSupportedGroups(ByteBuffer bb, List<Group> groups) {
439+
// First 2 bytes are size of the group list
440+
int toRead = bb.getChar() / 2;
441+
// Then the list of protocols
442+
for (int i = 0; i < toRead; i++) {
443+
char id = bb.getChar();
444+
Group group = Group.valueOf(id);
445+
if (group != null) {
446+
groups.add(group);
447+
}
448+
}
449+
}
450+
451+
452+
private static void readSignatureAlgorithms(ByteBuffer bb, List<SignatureAlgorithm> signatureAlgorithms) {
453+
// First 2 bytes are size of the signature algorithm list
454+
int toRead = bb.getChar() / 2;
455+
// Then the list of protocols
456+
for (int i = 0; i < toRead; i++) {
457+
char id = bb.getChar();
458+
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.valueOf(id);
459+
if (signatureAlgorithm != null) {
460+
signatureAlgorithms.add(signatureAlgorithm);
461+
}
462+
}
463+
}
464+
465+
416466
public enum ExtractorResult {
417467
COMPLETE,
418468
NOT_PRESENT,
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tomcat.util.net.openssl.ciphers;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
public enum Group {
23+
24+
// Elliptic Curve Groups (ECDHE)
25+
secp256r1(0x0017),
26+
secp384r1(0x0018),
27+
secp521r1(0x0019),
28+
x25519(0x001D),
29+
x448(0x001E),
30+
31+
// Finite Field Groups (DHE)
32+
ffdhe2048(0x0100),
33+
ffdhe3072(0x0101),
34+
ffdhe4096(0x0102),
35+
ffdhe6144(0x0103),
36+
ffdhe8192(0x0104),
37+
38+
// Post-Quantum Key Exchange
39+
mlkem512(0x0200),
40+
mlkem768(0x0201),
41+
mlkem1024(0x0202),
42+
43+
// Hybrid Key Exchange
44+
SecP256r1MLKEM768(0x11EB),
45+
X25519MLKEM768(0x11EC),
46+
SecP384r1MLKEM1024(0x11ED);
47+
48+
private final int id;
49+
50+
Group(int id) {
51+
this.id = id;
52+
}
53+
54+
/**
55+
* @return the id
56+
*/
57+
public int getId() {
58+
return this.id;
59+
}
60+
61+
private static final Map<Integer,Group> idMap = new HashMap<>();
62+
63+
static {
64+
for (Group group : values()) {
65+
int id = group.getId();
66+
67+
if (id > 0 && id < 0xFFFF) {
68+
idMap.put(Integer.valueOf(id), group);
69+
}
70+
}
71+
}
72+
73+
74+
public static Group valueOf(int groupId) {
75+
return idMap.get(Integer.valueOf(groupId));
76+
}
77+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tomcat.util.net.openssl.ciphers;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
public enum SignatureAlgorithm {
23+
24+
// RSASSA-PKCS1-v1_5 algorithms
25+
rsa_pkcs1_sha256(0x0401),
26+
rsa_pkcs1_sha384(0x0501),
27+
rsa_pkcs1_sha512(0x0601),
28+
29+
// ECDSA algorithms
30+
ecdsa_secp256r1_sha256(0x0403),
31+
ecdsa_secp384r1_sha384(0x0503),
32+
ecdsa_secp521r1_sha512(0x0603),
33+
34+
// RSASSA-PSS algorithms with public key OID rsaEncryption
35+
rsa_pss_rsae_sha256(0x0804),
36+
rsa_pss_rsae_sha384(0x0805),
37+
rsa_pss_rsae_sha512(0x0806),
38+
39+
// EdDSA algorithms */
40+
ed25519(0x0807),
41+
ed448(0x0808),
42+
43+
// RSASSA-PSS algorithms with public key OID RSASSA-PSS
44+
rsa_pss_pss_sha256(0x0809),
45+
rsa_pss_pss_sha384(0x080a),
46+
rsa_pss_pss_sha512(0x080b),
47+
48+
// Legacy algorithms */
49+
rsa_pkcs1_sha1(0x0201),
50+
ecdsa_sha1(0x0203),
51+
52+
// ML-DSA algorithms
53+
mldsa44(0x0904),
54+
mldsa65(0x0905),
55+
mldsa87(0x0906);
56+
57+
private final int id;
58+
59+
SignatureAlgorithm(int id) {
60+
this.id = id;
61+
}
62+
63+
/**
64+
* @return the id
65+
*/
66+
public int getId() {
67+
return this.id;
68+
}
69+
70+
private static final Map<Integer,SignatureAlgorithm> idMap = new HashMap<>();
71+
72+
static {
73+
for (SignatureAlgorithm group : values()) {
74+
int id = group.getId();
75+
76+
if (id > 0 && id < 0xFFFF) {
77+
idMap.put(Integer.valueOf(id), group);
78+
}
79+
}
80+
}
81+
82+
83+
public static SignatureAlgorithm valueOf(int groupId) {
84+
return idMap.get(Integer.valueOf(groupId));
85+
}
86+
}

0 commit comments

Comments
 (0)