Path: blob/master/test/jdk/sun/security/ssl/SignatureScheme/SigSchemePropOrdering.java
41152 views
/*1* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425//26// SunJSSE does not support dynamic system properties, no way to re-use27// system properties in samevm/agentvm mode.28//2930/*31* @test32* @bug 825586733* @summary SignatureScheme JSSE property does not preserve ordering in handshake messages34* @library /javax/net/ssl/templates35* @run main/othervm SigSchemePropOrdering36*/3738import java.nio.ByteBuffer;39import java.util.*;40import java.util.AbstractMap.SimpleImmutableEntry;41import javax.net.ssl.SSLEngine;42import javax.net.ssl.SSLException;4344public class SigSchemePropOrdering extends SSLEngineTemplate {4546// Helper map to correlate integral SignatureScheme identifiers to47// their IANA string name counterparts.48static final Map<Integer, String> sigSchemeMap = Map.ofEntries(49new SimpleImmutableEntry(0x0401, "rsa_pkcs1_sha256"),50new SimpleImmutableEntry(0x0501, "rsa_pkcs1_sha384"),51new SimpleImmutableEntry(0x0601, "rsa_pkcs1_sha512"),52new SimpleImmutableEntry(0x0403, "ecdsa_secp256r1_sha256"),53new SimpleImmutableEntry(0x0503, "ecdsa_secp384r1_sha384"),54new SimpleImmutableEntry(0x0603, "ecdsa_secp521r1_sha512"),55new SimpleImmutableEntry(0x0804, "rsa_pss_rsae_sha256"),56new SimpleImmutableEntry(0x0805, "rsa_pss_rsae_sha384"),57new SimpleImmutableEntry(0x0806, "rsa_pss_rsae_sha512"),58new SimpleImmutableEntry(0x0807, "ed25519"),59new SimpleImmutableEntry(0x0808, "ed448"),60new SimpleImmutableEntry(0x0809, "rsa_pss_pss_sha256"),61new SimpleImmutableEntry(0x080a, "rsa_pss_pss_sha384"),62new SimpleImmutableEntry(0x080b, "rsa_pss_pss_sha512"),63new SimpleImmutableEntry(0x0101, "rsa_md5"),64new SimpleImmutableEntry(0x0201, "rsa_pkcs1_sha1"),65new SimpleImmutableEntry(0x0202, "dsa_sha1"),66new SimpleImmutableEntry(0x0203, "ecdsa_sha1"),67new SimpleImmutableEntry(0x0301, "rsa_sha224"),68new SimpleImmutableEntry(0x0302, "dsa_sha224"),69new SimpleImmutableEntry(0x0303, "ecdsa_sha224"),70new SimpleImmutableEntry(0x0402, "rsa_pkcs1_sha256"));7172// Other useful TLS definitions for these tests73private static final int TLS_HS_CLI_HELLO = 1;74private static final int TLS_HS_CERT_REQ = 13;75private static final int HELLO_EXT_SIG_ALGS = 13;7677private static final String SIG_SCHEME_STR =78"rsa_pkcs1_sha256,rsa_pss_rsae_sha256,rsa_pss_pss_sha256," +79"ed448,ed25519,ecdsa_secp256r1_sha256";8081SigSchemePropOrdering() throws Exception {82super();83}8485public static void main(String[] args) throws Exception {86System.setProperty("javax.net.debug", "ssl:handshake");87System.setProperty("jdk.tls.client.SignatureSchemes", SIG_SCHEME_STR);88System.setProperty("jdk.tls.server.SignatureSchemes", SIG_SCHEME_STR);89new SigSchemePropOrdering().run();90}9192@Override93protected SSLEngine configureClientEngine(SSLEngine clientEngine) {94clientEngine.setUseClientMode(true);95clientEngine.setEnabledProtocols(new String[] { "TLSv1.2" });96return clientEngine;97}9899@Override100protected SSLEngine configureServerEngine(SSLEngine serverEngine) {101serverEngine.setUseClientMode(false);102serverEngine.setWantClientAuth(true);103return serverEngine;104}105106private void run() throws Exception {107// Start the handshake. Check the ClientHello's signature_algorithms108// extension and make sure the ordering matches what we specified109// in the property above.110List<String> expectedSS = Arrays.asList(SIG_SCHEME_STR.split(","));111112clientEngine.wrap(clientOut, cTOs);113cTOs.flip();114115List<String> actualSS = getSigSchemesCliHello(116extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO));117118// Make sure the ordering is correct119if (!expectedSS.equals(actualSS)) {120System.out.println("FAIL: Mismatch between property ordering " +121"and ClientHello message");122System.out.print("Expected SigSchemes: ");123expectedSS.forEach(ss -> System.out.print(ss + " "));124System.out.println();125System.out.print("Actual SigSchemes: ");126actualSS.forEach(ss -> System.out.print(ss + " "));127System.out.println();128throw new RuntimeException(129"FAIL: Expected and Actual values differ.");130}131132// Consume the ClientHello and get the server flight of handshake133// messages. We expect that it will be one TLS record containing134// multiple handshake messages, one of which is a CertificateRequest.135serverEngine.unwrap(cTOs, serverIn);136runDelegatedTasks(serverEngine);137138// Wrap the server flight139serverEngine.wrap(serverOut, sTOc);140sTOc.flip();141142actualSS = getSigSchemesCertReq(143extractHandshakeMsg(sTOc, TLS_HS_CERT_REQ));144145// Make sure the ordering is correct146if (!expectedSS.equals(actualSS)) {147System.out.println("FAIL: Mismatch between property ordering " +148"and CertificateRequest message");149System.out.print("Expected SigSchemes: ");150expectedSS.forEach(ss -> System.out.print(ss + " "));151System.out.println();152System.out.print("Actual SigSchemes: ");153actualSS.forEach(ss -> System.out.print(ss + " "));154System.out.println();155throw new RuntimeException(156"FAIL: Expected and Actual values differ.");157}158}159160/**161* Given a TLS record containing one or more handshake messages, return162* the specific handshake message as a ByteBuffer (a slice of the record)163*164* @param tlsRecord a ByteBuffer containing a TLS record. It is assumed165* that the position of the ByteBuffer is on the first byte of the TLS166* record header.167* @param hsMsgId the message identifier for the handshake message being168* sought.169*170* @return a ByteBuffer containing the TLS handshake message. The position171* of the returned ByteBuffer will be on the first byte of the TLS172* handshake message data, immediately following the handshake header.173* If the message is not found, null will be returned.174*175* @throws SSLException if the incoming ByteBuffer does not contain a176* well-formed TLS message.177*/178private static ByteBuffer extractHandshakeMsg(ByteBuffer tlsRecord,179int hsMsgId) throws SSLException {180Objects.requireNonNull(tlsRecord);181tlsRecord.mark();182183// Process the TLS record header184int type = Byte.toUnsignedInt(tlsRecord.get());185int ver_major = Byte.toUnsignedInt(tlsRecord.get());186int ver_minor = Byte.toUnsignedInt(tlsRecord.get());187int recLen = Short.toUnsignedInt(tlsRecord.getShort());188189// Simple sanity checks190if (type != 22) {191throw new SSLException("Not a handshake: Type = " + type);192} else if (recLen > tlsRecord.remaining()) {193throw new SSLException("Incomplete record in buffer: " +194"Record length = " + recLen + ", Remaining = " +195tlsRecord.remaining());196}197198while (tlsRecord.hasRemaining()) {199// Grab the handshake message header.200int msgHdr = tlsRecord.getInt();201int msgType = (msgHdr >> 24) & 0x000000FF;202int msgLen = msgHdr & 0x00FFFFFF;203204if (msgType == hsMsgId) {205// Slice the buffer such that it contains the entire206// handshake message (less the handshake header).207ByteBuffer buf = tlsRecord.slice(tlsRecord.position(), msgLen);208tlsRecord.reset();209return buf;210} else {211// Skip to the next handshake message, if there is one212tlsRecord.position(tlsRecord.position() + msgLen);213}214}215216tlsRecord.reset();217return null;218}219220221/**222* Parses the ClientHello message and extracts from it a list of223* SignatureScheme values in string form. It is assumed that the provided224* ByteBuffer has its position set at the first byte of the ClientHello225* message body (AFTER the handshake header) and contains the entire226* hello message. Upon successful completion of this method the ByteBuffer227* will have its position reset to the initial offset in the buffer.228* If an exception is thrown the position at the time of the exception229* will be preserved.230*231* @param data the ByteBuffer containing the ClientHello bytes232*233* @returns A List of the signature schemes in string form. If no234* signature_algorithms extension is present in the client hello then235* an empty list will be returned.236*/237private static List<String> getSigSchemesCliHello(ByteBuffer data) {238Objects.requireNonNull(data);239data.mark();240241// Skip over the protocol version and client random242data.position(data.position() + 34);243244// Jump past the session ID (if there is one)245int sessLen = Byte.toUnsignedInt(data.get());246if (sessLen != 0) {247data.position(data.position() + sessLen);248}249250// Jump past the cipher suites251int csLen = Short.toUnsignedInt(data.getShort());252if (csLen != 0) {253data.position(data.position() + csLen);254}255256// ...and the compression257int compLen = Byte.toUnsignedInt(data.get());258if (compLen != 0) {259data.position(data.position() + compLen);260}261262// Now for the fun part. Go through the extensions and look263// for the two status request exts.264List<String> extSigAlgs = new ArrayList();265int extsLen = Short.toUnsignedInt(data.getShort());266while (data.hasRemaining()) {267int extType = Short.toUnsignedInt(data.getShort());268int extLen = Short.toUnsignedInt(data.getShort());269if (extType == HELLO_EXT_SIG_ALGS) {270// Start processing signature algorithms271int sigSchemeLen = Short.toUnsignedInt(data.getShort());272for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) {273String schemeName = sigSchemeMap.get(274Short.toUnsignedInt(data.getShort()));275if (schemeName != null) {276extSigAlgs.add(schemeName);277}278}279} else {280// Not the extension we're looking for. Skip past the281// extension data282data.position(data.position() + extLen);283}284}285286// We should be at the end of the ClientHello287data.reset();288return extSigAlgs;289}290291/**292* Parses the CertificateRequest message and extracts from it a list of293* SignatureScheme values in string form. It is assumed that the provided294* ByteBuffer has its position set at the first byte of the295* CertificateRequest message body (AFTER the handshake header) and296* contains the entire CR message. Upon successful completion of this297* method the ByteBuffer will have its position reset to the initial298* offset in the buffer.299* If an exception is thrown the position at the time of the exception300* will be preserved.301*302* @param data the ByteBuffer containing the CertificateRequest bytes303*304* @returns A List of the signature schemes in string form. If no305* signature_algorithms extension is present in the CertificateRequest306* then an empty list will be returned.307*/308private static List<String> getSigSchemesCertReq(ByteBuffer data) {309Objects.requireNonNull(data);310data.mark();311312// Jump past the certificate types313int certTypeLen = Byte.toUnsignedInt(data.get());314if (certTypeLen != 0) {315data.position(data.position() + certTypeLen);316}317318// Collect the SignatureAndHashAlgorithms319List<String> extSigAlgs = new ArrayList();320int sigSchemeLen = Short.toUnsignedInt(data.getShort());321for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) {322String schemeName = sigSchemeMap.get(323Short.toUnsignedInt(data.getShort()));324if (schemeName != null) {325extSigAlgs.add(schemeName);326}327}328329data.reset();330return extSigAlgs;331}332}333334335