Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/KrbClientKeyExchange.java
38830 views
/*1* Copyright (c) 2020, Azul Systems, Inc. All rights reserved.2* Copyright (c) 2020, Red Hat, Inc.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation. Oracle designates this8* particular file as subject to the "Classpath" exception as provided9* by Oracle in the LICENSE file that accompanied this code.10*11* This code is distributed in the hope that it will be useful, but WITHOUT12* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or13* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License14* version 2 for more details (a copy is included in the LICENSE file that15* accompanied this code).16*17* You should have received a copy of the GNU General Public License version18* 2 along with this work; if not, write to the Free Software Foundation,19* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.20*21* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA22* or visit www.oracle.com if you need additional information or have any23* questions.24*/2526package sun.security.ssl;2728import java.io.IOException;29import java.lang.reflect.InvocationTargetException;30import java.nio.ByteBuffer;31import java.security.AccessControlContext;32import java.security.AccessController;33import java.security.Principal;34import java.security.PrivilegedAction;35import java.text.MessageFormat;36import java.util.Locale;37import javax.crypto.SecretKey;38import javax.net.ssl.SNIHostName;39import javax.net.ssl.StandardConstants;4041import sun.misc.HexDumpEncoder;42import sun.security.ssl.KrbKeyExchange.KrbPremasterSecret;43import sun.security.ssl.SSLHandshake.HandshakeMessage;4445/**46* Pack of the "ClientKeyExchange" handshake message.47*/48final class KrbClientKeyExchange {49static final SSLConsumer krbHandshakeConsumer =50new KrbClientKeyExchangeConsumer();51static final HandshakeProducer krbHandshakeProducer =52new KrbClientKeyExchangeProducer();5354/**55* The KRB5 ClientKeyExchange handshake message (CLIENT -> SERVER).56* It holds the Kerberos ticket and the encrypted pre-master secret57* encrypted with the session key sealed in the ticket.58*59* From RFC 2712:60*61* struct62* {63* opaque Ticket;64* opaque authenticator; // optional, ignored65* opaque EncryptedPreMasterSecret; // encrypted with the session key66* // sealed in the ticket67* } KerberosWrapper;68*69*/70private static final71class KrbClientKeyExchangeMessage extends HandshakeMessage {7273private static final String KRB5_CLASS_NAME =74"sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl";7576private static final Class<?> krb5Class = AccessController.doPrivileged(77new PrivilegedAction<Class<?>>() {78@Override79public Class<?> run() {80try {81return Class.forName(KRB5_CLASS_NAME, true, null);82} catch (ClassNotFoundException cnf) {83return null;84}85}86});8788private static KrbClientKeyExchangeHelper newKrb5Instance() {89if (krb5Class != null) {90try {91return (KrbClientKeyExchangeHelper)krb5Class.92getDeclaredConstructor().newInstance();93} catch (InstantiationException | IllegalAccessException |94NoSuchMethodException | InvocationTargetException e) {95throw new AssertionError(e);96}97}98return null;99}100101private final KrbClientKeyExchangeHelper krb5Helper;102103private KrbClientKeyExchangeMessage(HandshakeContext context) {104super(context);105if ((krb5Helper = newKrb5Instance()) == null)106throw new IllegalStateException("Kerberos is unavailable");107}108109KrbClientKeyExchangeMessage(HandshakeContext context,110byte[] preMaster, String serverName,111AccessControlContext acc) throws IOException {112this(context);113krb5Helper.init(preMaster, serverName, acc);114}115116KrbClientKeyExchangeMessage(HandshakeContext context,117ByteBuffer message, Object serverKeys,118AccessControlContext acc) throws IOException {119this(context);120byte[] encodedTicket = Record.getBytes16(message);121if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {122SSLLogger.fine("encoded Kerberos service ticket",123encodedTicket);124}125// Read and ignore the authenticator126Record.getBytes16(message);127byte[] encryptedPreMasterSecret = Record.getBytes16(message);128if (encryptedPreMasterSecret != null && SSLLogger.isOn &&129SSLLogger.isOn("ssl,handshake")) {130SSLLogger.fine("encrypted Kerberos pre-master secret",131encryptedPreMasterSecret);132}133krb5Helper.init(encodedTicket, encryptedPreMasterSecret,134serverKeys, acc);135}136137@Override138SSLHandshake handshakeType() {139return SSLHandshake.CLIENT_KEY_EXCHANGE;140}141142@Override143int messageLength() {144return (6 + krb5Helper.getEncodedTicket().length +145krb5Helper.getEncryptedPreMasterSecret().length);146}147148@Override149void send(HandshakeOutStream hos) throws IOException {150hos.putBytes16(krb5Helper.getEncodedTicket());151hos.putBytes16(null); // XXX no authenticator152hos.putBytes16(krb5Helper.getEncryptedPreMasterSecret());153}154155byte[] getPlainPreMasterSecret() {156return krb5Helper.getPlainPreMasterSecret();157}158159Principal getPeerPrincipal() {160return krb5Helper.getPeerPrincipal();161}162163Principal getLocalPrincipal() {164return krb5Helper.getLocalPrincipal();165}166167@Override168public String toString() {169MessageFormat messageFormat = new MessageFormat(170"\"KRB5 ClientKeyExchange\": '{'\n" +171" \"ticket\": '{'\n" +172"{0}\n" +173" '}'\n" +174" \"pre-master\": '{'\n" +175" \"plain\": '{'\n" +176"{1}\n" +177" '}'\n" +178" \"encrypted\": '{'\n" +179"{2}\n" +180" '}'\n" +181" '}'\n" +182"'}'",183Locale.ENGLISH);184185HexDumpEncoder hexEncoder = new HexDumpEncoder();186Object[] messageFields = {187Utilities.indent(188hexEncoder.encodeBuffer(189krb5Helper.getEncodedTicket()), " "),190Utilities.indent(191hexEncoder.encodeBuffer(192krb5Helper.getPlainPreMasterSecret()), " "),193Utilities.indent(194hexEncoder.encodeBuffer(195krb5Helper.getEncryptedPreMasterSecret()), " "),196};197return messageFormat.format(messageFields);198}199}200201/**202* The KRB5 "ClientKeyExchange" handshake message producer.203*/204private static final205class KrbClientKeyExchangeProducer implements HandshakeProducer {206// Prevent instantiation of this class.207private KrbClientKeyExchangeProducer() {208// blank209}210211@Override212public byte[] produce(ConnectionContext context,213HandshakeMessage message) throws IOException {214// This happens in client side only.215ClientHandshakeContext chc = (ClientHandshakeContext)context;216217KrbClientKeyExchangeMessage kerberosMsg = null;218String hostName = null;219if (chc.negotiatedServerName != null) {220if (chc.negotiatedServerName.getType() ==221StandardConstants.SNI_HOST_NAME) {222SNIHostName sniHostName = null;223if (chc.negotiatedServerName instanceof SNIHostName) {224sniHostName = (SNIHostName) chc.negotiatedServerName;225} else {226try {227sniHostName = new SNIHostName(228chc.negotiatedServerName.getEncoded());229} catch (IllegalArgumentException iae) {230// unlikely to happen, just in case ...231}232}233if (sniHostName != null)234hostName = sniHostName.getAsciiName();235}236} else {237hostName = chc.handshakeSession.getPeerHost();238}239try {240KrbPremasterSecret premasterSecret =241KrbPremasterSecret.createPremasterSecret(242chc.negotiatedProtocol,243chc.sslContext.getSecureRandom());244kerberosMsg = new KrbClientKeyExchangeMessage(chc,245premasterSecret.preMaster, hostName,246chc.conContext.acc);247chc.handshakePossessions.add(premasterSecret);248} catch (IOException e) {249if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {250SSLLogger.fine(251"Error generating KRB premaster secret." +252" Hostname: " + hostName + " - Negotiated" +253" server name: " + chc.negotiatedServerName);254}255throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,256"Cannot generate KRB premaster secret", e);257}258if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {259SSLLogger.fine(260"Produced KRB5 ClientKeyExchange handshake message",261kerberosMsg);262}263264// Record the principals involved in the exchange265chc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());266chc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());267268// Output the handshake message.269kerberosMsg.write(chc.handshakeOutput);270chc.handshakeOutput.flush();271272// update the states273SSLKeyExchange ke = SSLKeyExchange.valueOf(274chc.negotiatedCipherSuite.keyExchange,275chc.negotiatedProtocol);276if (ke == null) {277// unlikely278throw chc.conContext.fatal(Alert.INTERNAL_ERROR,279"Not supported key exchange type");280} else {281SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);282SecretKey masterSecret =283masterKD.deriveKey("MasterSecret", null);284285chc.handshakeSession.setMasterSecret(masterSecret);286287SSLTrafficKeyDerivation kd =288SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);289if (kd == null) {290// unlikely291throw chc.conContext.fatal(Alert.INTERNAL_ERROR,292"Not supported key derivation: " +293chc.negotiatedProtocol);294} else {295chc.handshakeKeyDerivation =296kd.createKeyDerivation(chc, masterSecret);297}298}299300// The handshake message has been delivered.301return null;302}303}304305/**306* The KRB5 "ClientKeyExchange" handshake message consumer.307*/308private static final309class KrbClientKeyExchangeConsumer implements SSLConsumer {310// Prevent instantiation of this class.311private KrbClientKeyExchangeConsumer() {312// blank313}314315@Override316public void consume(ConnectionContext context,317ByteBuffer message) throws IOException {318// The consuming happens in server side only.319ServerHandshakeContext shc = (ServerHandshakeContext)context;320321Object serviceCreds = null;322for (SSLPossession possession : shc.handshakePossessions) {323if (possession instanceof KrbKeyExchange.KrbServiceCreds) {324serviceCreds = ((KrbKeyExchange.KrbServiceCreds) possession)325.serviceCreds;326break;327}328}329if (serviceCreds == null) { // unlikely330throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,331"No Kerberos service credentials for KRB Client Key Exchange");332}333334KrbClientKeyExchangeMessage kerberosMsg =335new KrbClientKeyExchangeMessage(shc,336message, serviceCreds, shc.conContext.acc);337KrbPremasterSecret premasterSecret = KrbPremasterSecret.decode(338shc.negotiatedProtocol,339ProtocolVersion.valueOf(shc.clientHelloVersion),340kerberosMsg.getPlainPreMasterSecret(),341shc.sslContext.getSecureRandom());342shc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());343shc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());344shc.handshakeCredentials.add(premasterSecret);345if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {346SSLLogger.fine(347"Consuming KRB5 ClientKeyExchange handshake message",348kerberosMsg);349}350351// update the states352SSLKeyExchange ke = SSLKeyExchange.valueOf(353shc.negotiatedCipherSuite.keyExchange,354shc.negotiatedProtocol);355if (ke == null) { // unlikely356throw shc.conContext.fatal(Alert.INTERNAL_ERROR,357"Not supported key exchange type");358} else {359SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);360SecretKey masterSecret =361masterKD.deriveKey("MasterSecret", null);362363// update the states364shc.handshakeSession.setMasterSecret(masterSecret);365SSLTrafficKeyDerivation kd =366SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);367if (kd == null) { // unlikely368throw shc.conContext.fatal(Alert.INTERNAL_ERROR,369"Not supported key derivation: " +370shc.negotiatedProtocol);371} else {372shc.handshakeKeyDerivation =373kd.createKeyDerivation(shc, masterSecret);374}375}376}377}378}379380381