< prev index next >
src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2006, 2012, Oracle and/or its affiliates. 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. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2015, 2018, Oracle and/or its affiliates. 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. Oracle designates this
*** 24,135 ****
*/
package sun.security.ssl;
import java.io.IOException;
! import java.util.ArrayList;
! import java.util.Collection;
!
import javax.net.ssl.SSLProtocolException;
! /*
! * [RFC5246] The client uses the "signature_algorithms" extension to
! * indicate to the server which signature/hash algorithm pairs may be
! * used in digital signatures. The "extension_data" field of this
! * extension contains a "supported_signature_algorithms" value.
! *
! * enum {
! * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
! * sha512(6), (255)
! * } HashAlgorithm;
! *
! * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
! * SignatureAlgorithm;
! *
! * struct {
! * HashAlgorithm hash;
! * SignatureAlgorithm signature;
! * } SignatureAndHashAlgorithm;
! *
! * SignatureAndHashAlgorithm
! * supported_signature_algorithms<2..2^16-2>;
*/
! final class SignatureAlgorithmsExtension extends HelloExtension {
! private Collection<SignatureAndHashAlgorithm> algorithms;
! private int algorithmsLen; // length of supported_signature_algorithms
! SignatureAlgorithmsExtension(
! Collection<SignatureAndHashAlgorithm> signAlgs) {
! super(ExtensionType.EXT_SIGNATURE_ALGORITHMS);
! algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs);
! algorithmsLen =
! SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size();
}
! SignatureAlgorithmsExtension(HandshakeInStream s, int len)
! throws IOException {
! super(ExtensionType.EXT_SIGNATURE_ALGORITHMS);
! algorithmsLen = s.getInt16();
! if (algorithmsLen == 0 || algorithmsLen + 2 != len) {
! throw new SSLProtocolException("Invalid " + type + " extension");
}
! algorithms = new ArrayList<SignatureAndHashAlgorithm>();
! int remains = algorithmsLen;
! int sequence = 0;
! while (remains > 1) { // needs at least two bytes
! int hash = s.getInt8(); // hash algorithm
! int signature = s.getInt8(); // signature algorithm
! SignatureAndHashAlgorithm algorithm =
! SignatureAndHashAlgorithm.valueOf(hash, signature, ++sequence);
! algorithms.add(algorithm);
! remains -= 2; // one byte for hash, one byte for signature
}
! if (remains != 0) {
! throw new SSLProtocolException("Invalid server_name extension");
}
}
! Collection<SignatureAndHashAlgorithm> getSignAlgorithms() {
! return algorithms;
}
@Override
! int length() {
! return 6 + algorithmsLen;
}
@Override
! void send(HandshakeOutStream s) throws IOException {
! s.putInt16(type.id);
! s.putInt16(algorithmsLen + 2);
! s.putInt16(algorithmsLen);
! for (SignatureAndHashAlgorithm algorithm : algorithms) {
! s.putInt8(algorithm.getHashValue()); // HashAlgorithm
! s.putInt8(algorithm.getSignatureValue()); // SignatureAlgorithm
}
}
@Override
! public String toString() {
! StringBuilder sb = new StringBuilder();
! boolean opened = false;
! for (SignatureAndHashAlgorithm signAlg : algorithms) {
! if (opened) {
! sb.append(", " + signAlg.getAlgorithmName());
! } else {
! sb.append(signAlg.getAlgorithmName());
! opened = true;
}
}
! return "Extension " + type + ", signature_algorithms: " + sb;
}
- }
--- 24,509 ----
*/
package sun.security.ssl;
import java.io.IOException;
! import java.nio.ByteBuffer;
! import java.text.MessageFormat;
! import java.util.LinkedList;
! import java.util.List;
! import java.util.Locale;
import javax.net.ssl.SSLProtocolException;
+ import sun.security.ssl.SSLExtension.ExtensionConsumer;
+ import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+ import sun.security.ssl.SSLHandshake.HandshakeMessage;
! /**
! * Pack of the "signature_algorithms" extensions [RFC 5246].
*/
! final class SignatureAlgorithmsExtension {
! static final HandshakeProducer chNetworkProducer =
! new CHSignatureSchemesProducer();
! static final ExtensionConsumer chOnLoadConcumer =
! new CHSignatureSchemesConsumer();
! static final HandshakeAbsence chOnLoadAbsence =
! new CHSignatureSchemesAbsence();
! static final HandshakeConsumer chOnTradeConsumer =
! new CHSignatureSchemesUpdate();
!
! static final HandshakeProducer crNetworkProducer =
! new CRSignatureSchemesProducer();
! static final ExtensionConsumer crOnLoadConcumer =
! new CRSignatureSchemesConsumer();
! static final HandshakeAbsence crOnLoadAbsence =
! new CRSignatureSchemesAbsence();
! static final HandshakeConsumer crOnTradeConsumer =
! new CRSignatureSchemesUpdate();
! static final SSLStringize ssStringize =
! new SignatureSchemesStringize();
! /**
! * The "signature_algorithms" extension.
! */
! static final class SignatureSchemesSpec implements SSLExtensionSpec {
! final int[] signatureSchemes;
! SignatureSchemesSpec(List<SignatureScheme> schemes) {
! if (schemes != null) {
! signatureSchemes = new int[schemes.size()];
! int i = 0;
! for (SignatureScheme scheme : schemes) {
! signatureSchemes[i++] = scheme.id;
! }
! } else {
! this.signatureSchemes = new int[0];
! }
! }
! SignatureSchemesSpec(ByteBuffer buffer) throws IOException {
! if (buffer.remaining() < 2) { // 2: the length of the list
! throw new SSLProtocolException(
! "Invalid signature_algorithms: insufficient data");
}
! byte[] algs = Record.getBytes16(buffer);
! if (buffer.hasRemaining()) {
! throw new SSLProtocolException(
! "Invalid signature_algorithms: unknown extra data");
! }
! if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) {
! throw new SSLProtocolException(
! "Invalid signature_algorithms: incomplete data");
}
! int[] schemes = new int[algs.length / 2];
! for (int i = 0, j = 0; i < algs.length;) {
! byte hash = algs[i++];
! byte sign = algs[i++];
! schemes[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF);
! }
! this.signatureSchemes = schemes;
}
! @Override
! public String toString() {
! MessageFormat messageFormat = new MessageFormat(
! "\"signature schemes\": '['{0}']'", Locale.ENGLISH);
!
! if (signatureSchemes == null || signatureSchemes.length == 0) {
! Object[] messageFields = {
! "<no supported signature schemes specified>"
! };
! return messageFormat.format(messageFields);
! } else {
! StringBuilder builder = new StringBuilder(512);
! boolean isFirst = true;
! for (int pv : signatureSchemes) {
! if (isFirst) {
! isFirst = false;
! } else {
! builder.append(", ");
}
+
+ builder.append(SignatureScheme.nameOf(pv));
}
! Object[] messageFields = {
! builder.toString()
! };
!
! return messageFormat.format(messageFields);
! }
! }
}
+ private static final
+ class SignatureSchemesStringize implements SSLStringize {
@Override
! public String toString(ByteBuffer buffer) {
! try {
! return (new SignatureSchemesSpec(buffer)).toString();
! } catch (IOException ioe) {
! // For debug logging only, so please swallow exceptions.
! return ioe.getMessage();
! }
! }
! }
!
! /**
! * Network data producer of a "signature_algorithms" extension in
! * the ClientHello handshake message.
! */
! private static final
! class CHSignatureSchemesProducer implements HandshakeProducer {
! // Prevent instantiation of this class.
! private CHSignatureSchemesProducer() {
! // blank
}
@Override
! public byte[] produce(ConnectionContext context,
! HandshakeMessage message) throws IOException {
! // The producing happens in client side only.
! ClientHandshakeContext chc = (ClientHandshakeContext)context;
!
! // Is it a supported and enabled extension?
! if (!chc.sslConfig.isAvailable(
! SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine(
! "Ignore unavailable signature_algorithms extension");
! }
! return null;
! }
! // Produce the extension.
! if (chc.localSupportedSignAlgs == null) {
! chc.localSupportedSignAlgs =
! SignatureScheme.getSupportedAlgorithms(
! chc.algorithmConstraints, chc.activeProtocols);
! }
!
! int vectorLen = SignatureScheme.sizeInRecord() *
! chc.localSupportedSignAlgs.size();
! byte[] extData = new byte[vectorLen + 2];
! ByteBuffer m = ByteBuffer.wrap(extData);
! Record.putInt16(m, vectorLen);
! for (SignatureScheme ss : chc.localSupportedSignAlgs) {
! Record.putInt16(m, ss.id);
! }
!
! // Update the context.
! chc.handshakeExtensions.put(
! SSLExtension.CH_SIGNATURE_ALGORITHMS,
! new SignatureSchemesSpec(chc.localSupportedSignAlgs));
!
! return extData;
}
}
+ /**
+ * Network data consumer of a "signature_algorithms" extension in
+ * the ClientHello handshake message.
+ */
+ private static final
+ class CHSignatureSchemesConsumer implements ExtensionConsumer {
+ // Prevent instantiation of this class.
+ private CHSignatureSchemesConsumer() {
+ // blank
+ }
+
@Override
! public void consume(ConnectionContext context,
! HandshakeMessage message, ByteBuffer buffer) throws IOException {
! // The comsuming happens in server side only.
! ServerHandshakeContext shc = (ServerHandshakeContext)context;
!
! // Is it a supported and enabled extension?
! if (!shc.sslConfig.isAvailable(
! SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine(
! "Ignore unavailable signature_algorithms extension");
}
+ return; // ignore the extension
}
! // Parse the extension.
! SignatureSchemesSpec spec;
! try {
! spec = new SignatureSchemesSpec(buffer);
! } catch (IOException ioe) {
! shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
! return; // fatal() always throws, make the compiler happy.
}
+ // Update the context.
+ shc.handshakeExtensions.put(
+ SSLExtension.CH_SIGNATURE_ALGORITHMS, spec);
+
+ // No impact on session resumption.
+ }
+ }
+
+ /**
+ * After session creation consuming of a "signature_algorithms"
+ * extension in the ClientHello handshake message.
+ */
+ private static final class CHSignatureSchemesUpdate
+ implements HandshakeConsumer {
+ // Prevent instantiation of this class.
+ private CHSignatureSchemesUpdate() {
+ // blank
+ }
+
+ @Override
+ public void consume(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The comsuming happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ SignatureSchemesSpec spec =
+ (SignatureSchemesSpec)shc.handshakeExtensions.get(
+ SSLExtension.CH_SIGNATURE_ALGORITHMS);
+ if (spec == null) {
+ // Ignore, no "signature_algorithms" extension requested.
+ return;
+ }
+
+ // update the context
+ List<SignatureScheme> shemes =
+ SignatureScheme.getSupportedAlgorithms(
+ shc.algorithmConstraints, shc.negotiatedProtocol,
+ spec.signatureSchemes);
+ shc.peerRequestedSignatureSchemes = shemes;
+
+ // If no "signature_algorithms_cert" extension is present, then
+ // the "signature_algorithms" extension also applies to
+ // signatures appearing in certificates.
+ SignatureSchemesSpec certSpec =
+ (SignatureSchemesSpec)shc.handshakeExtensions.get(
+ SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+ if (certSpec == null) {
+ shc.peerRequestedCertSignSchemes = shemes;
+ }
+
+ shc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+
+ if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
+ if (shc.sslConfig.clientAuthType !=
+ ClientAuthType.CLIENT_AUTH_NONE) {
+ shc.handshakeProducers.putIfAbsent(
+ SSLHandshake.CERTIFICATE_REQUEST.id,
+ SSLHandshake.CERTIFICATE_REQUEST);
+ }
+ shc.handshakeProducers.put(
+ SSLHandshake.CERTIFICATE.id,
+ SSLHandshake.CERTIFICATE);
+ shc.handshakeProducers.putIfAbsent(
+ SSLHandshake.CERTIFICATE_VERIFY.id,
+ SSLHandshake.CERTIFICATE_VERIFY);
+ }
+ }
+ }
+
+ /**
+ * The absence processing if a "signature_algorithms" extension is
+ * not present in the ClientHello handshake message.
+ */
+ private static final
+ class CHSignatureSchemesAbsence implements HandshakeAbsence {
+ @Override
+ public void absent(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The comsuming happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ // This is a mandatory extension for certificate authentication
+ // in TLS 1.3.
+ //
+ // We may support the server authentication other than X.509
+ // certificate later.
+ if (shc.negotiatedProtocol.useTLS13PlusSpec()) {
+ shc.conContext.fatal(Alert.MISSING_EXTENSION,
+ "No mandatory signature_algorithms extension in the " +
+ "received CertificateRequest handshake message");
+ }
+ }
+ }
+
+ /**
+ * Network data producer of a "signature_algorithms" extension in
+ * the CertificateRequest handshake message.
+ */
+ private static final
+ class CRSignatureSchemesProducer implements HandshakeProducer {
+ // Prevent instantiation of this class.
+ private CRSignatureSchemesProducer() {
+ // blank
+ }
+
+ @Override
+ public byte[] produce(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The producing happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ //
+ // Note that this is a mandatory extension for CertificateRequest
+ // handshake message in TLS 1.3.
+ if (!shc.sslConfig.isAvailable(
+ SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
+ shc.conContext.fatal(Alert.MISSING_EXTENSION,
+ "No available signature_algorithms extension " +
+ "for client certificate authentication");
+ return null; // make the compiler happy
+ }
+
+ // Produce the extension.
+ if (shc.localSupportedSignAlgs == null) {
+ shc.localSupportedSignAlgs =
+ SignatureScheme.getSupportedAlgorithms(
+ shc.algorithmConstraints, shc.activeProtocols);
+ }
+
+ int vectorLen = SignatureScheme.sizeInRecord() *
+ shc.localSupportedSignAlgs.size();
+ byte[] extData = new byte[vectorLen + 2];
+ ByteBuffer m = ByteBuffer.wrap(extData);
+ Record.putInt16(m, vectorLen);
+ for (SignatureScheme ss : shc.localSupportedSignAlgs) {
+ Record.putInt16(m, ss.id);
+ }
+
+ // Update the context.
+ shc.handshakeExtensions.put(
+ SSLExtension.CR_SIGNATURE_ALGORITHMS,
+ new SignatureSchemesSpec(shc.localSupportedSignAlgs));
+
+ return extData;
+ }
+ }
+
+ /**
+ * Network data consumer of a "signature_algorithms" extension in
+ * the CertificateRequest handshake message.
+ */
+ private static final
+ class CRSignatureSchemesConsumer implements ExtensionConsumer {
+ // Prevent instantiation of this class.
+ private CRSignatureSchemesConsumer() {
+ // blank
+ }
+ @Override
+ public void consume(ConnectionContext context,
+ HandshakeMessage message, ByteBuffer buffer) throws IOException {
+ // The comsuming happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ //
+ // Note that this is a mandatory extension for CertificateRequest
+ // handshake message in TLS 1.3.
+ if (!chc.sslConfig.isAvailable(
+ SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
+ chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+ "No available signature_algorithms extension " +
+ "for client certificate authentication");
+ return; // make the compiler happy
+ }
+
+ // Parse the extension.
+ SignatureSchemesSpec spec;
+ try {
+ spec = new SignatureSchemesSpec(buffer);
+ } catch (IOException ioe) {
+ chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+ return; // fatal() always throws, make the compiler happy.
+ }
+
+ List<SignatureScheme> knownSignatureSchemes = new LinkedList<>();
+ for (int id : spec.signatureSchemes) {
+ SignatureScheme ss = SignatureScheme.valueOf(id);
+ if (ss != null) {
+ knownSignatureSchemes.add(ss);
+ }
+ }
+
+ // Update the context.
+ // chc.peerRequestedSignatureSchemes = knownSignatureSchemes;
+ chc.handshakeExtensions.put(
+ SSLExtension.CR_SIGNATURE_ALGORITHMS, spec);
+
+ // No impact on session resumption.
+ }
+ }
+
+ /**
+ * After session creation consuming of a "signature_algorithms"
+ * extension in the CertificateRequest handshake message.
+ */
+ private static final class CRSignatureSchemesUpdate
+ implements HandshakeConsumer {
+ // Prevent instantiation of this class.
+ private CRSignatureSchemesUpdate() {
+ // blank
+ }
+
+ @Override
+ public void consume(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The comsuming happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ SignatureSchemesSpec spec =
+ (SignatureSchemesSpec)chc.handshakeExtensions.get(
+ SSLExtension.CR_SIGNATURE_ALGORITHMS);
+ if (spec == null) {
+ // Ignore, no "signature_algorithms" extension requested.
+ return;
+ }
+
+ // update the context
+ List<SignatureScheme> shemes =
+ SignatureScheme.getSupportedAlgorithms(
+ chc.algorithmConstraints, chc.negotiatedProtocol,
+ spec.signatureSchemes);
+ chc.peerRequestedSignatureSchemes = shemes;
+
+ // If no "signature_algorithms_cert" extension is present, then
+ // the "signature_algorithms" extension also applies to
+ // signatures appearing in certificates.
+ SignatureSchemesSpec certSpec =
+ (SignatureSchemesSpec)chc.handshakeExtensions.get(
+ SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+ if (certSpec == null) {
+ chc.peerRequestedCertSignSchemes = shemes;
+ }
+
+ chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+ }
+ }
+
+ /**
+ * The absence processing if a "signature_algorithms" extension is
+ * not present in the CertificateRequest handshake message.
+ */
+ private static final
+ class CRSignatureSchemesAbsence implements HandshakeAbsence {
+ @Override
+ public void absent(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The comsuming happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ // This is a mandatory extension for CertificateRequest handshake
+ // message in TLS 1.3.
+ chc.conContext.fatal(Alert.MISSING_EXTENSION,
+ "No mandatory signature_algorithms extension in the " +
+ "received CertificateRequest handshake message");
+ }
+ }
+ }
< prev index next >