f62b5789ad
Reviewed-by: djelinski, jjiang, ssahoo
270 lines
9.8 KiB
Java
270 lines
9.8 KiB
Java
/*
|
|
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
|
* Copyright (C) 2021, 2024 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test 8263188
|
|
* @summary If TLS the server and client has no common signature algorithms,
|
|
* the connection should fail fast with "No supported signature algorithm".
|
|
* This test only covers TLS 1.2.
|
|
*
|
|
* @library /test/lib
|
|
* /javax/net/ssl/templates
|
|
*
|
|
* @run main/othervm
|
|
* -Djdk.tls.server.SignatureSchemes=ecdsa_secp384r1_sha384
|
|
* -Djdk.tls.client.SignatureSchemes=ecdsa_secp256r1_sha256,ecdsa_secp384r1_sha384
|
|
* -Dtest.clientAuth=false
|
|
* -Dtest.expectFail=false
|
|
* SigAlgosExtTestWithTLS12
|
|
* @run main/othervm
|
|
* -Djdk.tls.server.SignatureSchemes=ecdsa_secp384r1_sha384
|
|
* -Djdk.tls.client.SignatureSchemes=ecdsa_secp256r1_sha256
|
|
* -Dtest.clientAuth=false
|
|
* -Dtest.expectFail=true
|
|
* SigAlgosExtTestWithTLS12
|
|
* @run main/othervm
|
|
* -Djdk.tls.server.SignatureSchemes=ecdsa_secp256r1_sha256
|
|
* -Djdk.tls.client.SignatureSchemes=ecdsa_secp256r1_sha256
|
|
* -Dtest.clientAuth=true
|
|
* -Dtest.expectFail=true
|
|
* SigAlgosExtTestWithTLS12
|
|
*/
|
|
|
|
import javax.net.ssl.*;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.*;
|
|
|
|
public class SigAlgosExtTestWithTLS12 extends SSLEngineTemplate {
|
|
|
|
private static final boolean CLIENT_AUTH
|
|
= Boolean.getBoolean("test.clientAuth");
|
|
private static final boolean EXPECT_FAIL
|
|
= Boolean.getBoolean("test.expectFail");
|
|
|
|
private static final int TLS_HS_CERT_REQ = 13;
|
|
|
|
public SigAlgosExtTestWithTLS12() throws Exception {
|
|
super();
|
|
}
|
|
|
|
/*
|
|
* Create an instance of KeyManager for client use.
|
|
*/
|
|
@Override
|
|
protected KeyManager createClientKeyManager() throws Exception {
|
|
return createKeyManager(
|
|
new Cert[]{Cert.EE_ECDSA_SECP256R1, Cert.EE_ECDSA_SECP384R1},
|
|
getClientContextParameters());
|
|
}
|
|
|
|
@Override
|
|
public TrustManager createClientTrustManager() throws Exception {
|
|
return createTrustManager(
|
|
new Cert[]{Cert.CA_ECDSA_SECP256R1, Cert.CA_ECDSA_SECP384R1},
|
|
getServerContextParameters());
|
|
}
|
|
|
|
@Override
|
|
public KeyManager createServerKeyManager() throws Exception {
|
|
return createKeyManager(
|
|
new Cert[]{Cert.EE_ECDSA_SECP256R1, Cert.EE_ECDSA_SECP384R1},
|
|
getServerContextParameters());
|
|
}
|
|
|
|
@Override
|
|
public TrustManager createServerTrustManager() throws Exception {
|
|
return createTrustManager(
|
|
new Cert[]{Cert.CA_ECDSA_SECP256R1, Cert.CA_ECDSA_SECP384R1},
|
|
getServerContextParameters());
|
|
}
|
|
|
|
@Override
|
|
protected SSLEngine configureServerEngine(SSLEngine serverEngine) {
|
|
serverEngine.setUseClientMode(false);
|
|
serverEngine.setNeedClientAuth(CLIENT_AUTH);
|
|
return serverEngine;
|
|
}
|
|
|
|
@Override
|
|
protected SSLEngine configureClientEngine(SSLEngine clientEngine) {
|
|
clientEngine.setUseClientMode(true);
|
|
clientEngine.setEnabledProtocols(new String[] { "TLSv1.2" });
|
|
return clientEngine;
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
System.setProperty("javax.net.debug", "ssl:handshake");
|
|
|
|
try {
|
|
new SigAlgosExtTestWithTLS12().run();
|
|
if (EXPECT_FAIL) {
|
|
throw new RuntimeException(
|
|
"Expected SSLHandshakeException wasn't thrown");
|
|
}
|
|
} catch (SSLHandshakeException e) {
|
|
if (EXPECT_FAIL && e.getMessage().endsWith(
|
|
"No supported signature algorithm")) {
|
|
System.out.println("Expected SSLHandshakeException");
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void run() throws Exception {
|
|
boolean dataDone = false;
|
|
while (isOpen(clientEngine) || isOpen(serverEngine)) {
|
|
clientEngine.wrap(clientOut, cTOs);
|
|
cTOs.flip();
|
|
|
|
// Consume the ClientHello and get the server flight of handshake
|
|
// messages. We expect that it will be one TLS record containing
|
|
// multiple handshake messages, one of which is a CertificateRequest
|
|
// when the client authentication is required.
|
|
serverEngine.unwrap(cTOs, serverIn);
|
|
runDelegatedTasks(serverEngine);
|
|
|
|
// Wrap the server flight
|
|
serverEngine.wrap(serverOut, sTOc);
|
|
sTOc.flip();
|
|
|
|
if (CLIENT_AUTH && EXPECT_FAIL) {
|
|
twistCertReqMsg(sTOc);
|
|
}
|
|
|
|
clientEngine.unwrap(sTOc, clientIn);
|
|
runDelegatedTasks(clientEngine);
|
|
|
|
serverEngine.unwrap(cTOs, serverIn);
|
|
runDelegatedTasks(serverEngine);
|
|
|
|
cTOs.compact();
|
|
sTOc.compact();
|
|
|
|
if (!dataDone && (clientOut.limit() == serverIn.position()) &&
|
|
(serverOut.limit() == clientIn.position())) {
|
|
checkTransfer(serverOut, clientIn);
|
|
checkTransfer(clientOut, serverIn);
|
|
|
|
clientEngine.closeOutbound();
|
|
dataDone = true;
|
|
serverEngine.closeOutbound();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Twists signature schemes in CertificateRequest message for negative
|
|
* client authentication cases.
|
|
*
|
|
* @param tlsRecord a ByteBuffer containing a TLS record. It is assumed
|
|
* that the position of the ByteBuffer is on the first byte of the TLS
|
|
* record header.
|
|
*
|
|
* @throws SSLException if the incoming ByteBuffer does not contain a
|
|
* well-formed TLS message.
|
|
*/
|
|
private static void twistCertReqMsg(
|
|
ByteBuffer tlsRecord) throws SSLException {
|
|
Objects.requireNonNull(tlsRecord);
|
|
tlsRecord.mark();
|
|
|
|
// Process the TLS record header
|
|
int type = Byte.toUnsignedInt(tlsRecord.get());
|
|
int ver_major = Byte.toUnsignedInt(tlsRecord.get());
|
|
int ver_minor = Byte.toUnsignedInt(tlsRecord.get());
|
|
int recLen = Short.toUnsignedInt(tlsRecord.getShort());
|
|
|
|
// Simple sanity checks
|
|
if (type != 22) {
|
|
throw new SSLException("Not a handshake: Type = " + type);
|
|
} else if (recLen > tlsRecord.remaining()) {
|
|
throw new SSLException("Incomplete record in buffer: " +
|
|
"Record length = " + recLen + ", Remaining = " +
|
|
tlsRecord.remaining());
|
|
}
|
|
|
|
while (tlsRecord.hasRemaining()) {
|
|
// Grab the handshake message header.
|
|
int msgHdr = tlsRecord.getInt();
|
|
int msgType = (msgHdr >> 24) & 0x000000FF;
|
|
int msgLen = msgHdr & 0x00FFFFFF;
|
|
|
|
if (msgType == TLS_HS_CERT_REQ) {
|
|
// Slice the buffer such that it contains the entire
|
|
// handshake message (less the handshake header).
|
|
int bufPos = tlsRecord.position();
|
|
ByteBuffer buf = tlsRecord.slice(bufPos, msgLen);
|
|
|
|
// Replace the signature scheme with an unknown value
|
|
twistSigSchemesCertReq(buf, (short) 0x0000);
|
|
byte[] bufBytes = new byte[buf.limit()];
|
|
buf.get(bufBytes);
|
|
tlsRecord.put(bufPos, bufBytes);
|
|
|
|
break;
|
|
} else {
|
|
// Skip to the next handshake message, if there is one
|
|
tlsRecord.position(tlsRecord.position() + msgLen);
|
|
}
|
|
}
|
|
|
|
tlsRecord.reset();
|
|
}
|
|
|
|
/**
|
|
* Replace the signature schemes in CertificateRequest message with an
|
|
* alternative value. It is assumed that the provided ByteBuffer has its
|
|
* position set at the first byte of the CertificateRequest message body
|
|
* (AFTER the handshake header) and contains the entire CR message. Upon
|
|
* successful completion of this method the ByteBuffer will have its
|
|
* position reset to the initial offset in the buffer.
|
|
* If an exception is thrown the position at the time of the exception
|
|
* will be preserved.
|
|
*
|
|
* @param data the ByteBuffer containing the CertificateRequest bytes
|
|
* @param altSigScheme an alternative signature scheme
|
|
*/
|
|
private static void twistSigSchemesCertReq(ByteBuffer data,
|
|
Short altSigScheme) {
|
|
Objects.requireNonNull(data);
|
|
data.mark();
|
|
|
|
// Jump past the certificate types
|
|
int certTypeLen = Byte.toUnsignedInt(data.get());
|
|
if (certTypeLen != 0) {
|
|
data.position(data.position() + certTypeLen);
|
|
}
|
|
|
|
int sigSchemeLen = Short.toUnsignedInt(data.getShort());
|
|
for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) {
|
|
System.err.println(
|
|
"Use alternative signature scheme: " + altSigScheme);
|
|
data.putShort(data.position(), altSigScheme);
|
|
}
|
|
|
|
data.reset();
|
|
}
|
|
}
|