/* * 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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. */ package sun.security.ssl; import java.io.IOException; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Optional; import sun.security.ssl.SupportedGroupsExtension.NamedGroup; import sun.security.ssl.SupportedGroupsExtension.NamedGroupFunctions; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.ssl.X509Authentication.X509Possession; final class SSLKeyExchange implements SSLKeyAgreementGenerator, SSLHandshakeBinding { private final SSLAuthentication authentication; private final SSLKeyAgreement keyAgreement; SSLKeyExchange(X509Authentication authentication, SSLKeyAgreement keyAgreement) { this.authentication = authentication; this.keyAgreement = keyAgreement; } SSLPossession[] createPossessions(HandshakeContext context) { // authentication SSLPossession authPossession = null; if (authentication != null) { authPossession = authentication.createPossession(context); if (authPossession == null) { return new SSLPossession[0]; } else if (context instanceof ServerHandshakeContext) { // The authentication information may be used further for // key agreement parameters negotiation. ServerHandshakeContext shc = (ServerHandshakeContext)context; shc.interimAuthn = authPossession; } } // key agreement SSLPossession kaPossession; if (keyAgreement == T12KeyAgreement.RSA_EXPORT) { // a special case X509Possession x509Possession = (X509Possession)authPossession; if (JsseJce.getRSAKeyLength( x509Possession.popCerts[0].getPublicKey()) > 512) { kaPossession = keyAgreement.createPossession(context); if (kaPossession == null) { return new SSLPossession[0]; } else { return authentication != null ? new SSLPossession[] {authPossession, kaPossession} : new SSLPossession[] {kaPossession}; } } else { return authentication != null ? new SSLPossession[] {authPossession} : new SSLPossession[0]; } } else { kaPossession = keyAgreement.createPossession(context); if (kaPossession == null) { // special cases if (keyAgreement == T12KeyAgreement.RSA || keyAgreement == T12KeyAgreement.ECDH) { return authentication != null ? new SSLPossession[] {authPossession} : new SSLPossession[0]; } else { return new SSLPossession[0]; } } else { return authentication != null ? new SSLPossession[] {authPossession, kaPossession} : new SSLPossession[] {kaPossession}; } } } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext handshakeContext) throws IOException { return keyAgreement.createKeyDerivation(handshakeContext); } @Override public SSLHandshake[] getRelatedHandshakers( HandshakeContext handshakeContext) { SSLHandshake[] auHandshakes; if (authentication != null) { auHandshakes = authentication.getRelatedHandshakers(handshakeContext); } else { auHandshakes = null; } SSLHandshake[] kaHandshakes = keyAgreement.getRelatedHandshakers(handshakeContext); if (auHandshakes == null || auHandshakes.length == 0) { return kaHandshakes; } else if (kaHandshakes == null || kaHandshakes.length == 0) { return auHandshakes; } else { SSLHandshake[] producers = Arrays.copyOf( auHandshakes, auHandshakes.length + kaHandshakes.length); System.arraycopy(kaHandshakes, 0, producers, auHandshakes.length, kaHandshakes.length); return producers; } } @Override public Map.Entry[] getHandshakeProducers( HandshakeContext handshakeContext) { Map.Entry[] auProducers; if (authentication != null) { auProducers = authentication.getHandshakeProducers(handshakeContext); } else { auProducers = null; } Map.Entry[] kaProducers = keyAgreement.getHandshakeProducers(handshakeContext); if (auProducers == null || auProducers.length == 0) { return kaProducers; } else if (kaProducers == null || kaProducers.length == 0) { return auProducers; } else { Map.Entry[] producers = Arrays.copyOf( auProducers, auProducers.length + kaProducers.length); System.arraycopy(kaProducers, 0, producers, auProducers.length, kaProducers.length); return producers; } } @Override public Map.Entry[] getHandshakeConsumers( HandshakeContext handshakeContext) { Map.Entry[] auConsumers; if (authentication != null) { auConsumers = authentication.getHandshakeConsumers(handshakeContext); } else { auConsumers = null; } Map.Entry[] kaConsumers = keyAgreement.getHandshakeConsumers(handshakeContext); if (auConsumers == null || auConsumers.length == 0) { return kaConsumers; } else if (kaConsumers == null || kaConsumers.length == 0) { return auConsumers; } else { Map.Entry[] producers = Arrays.copyOf( auConsumers, auConsumers.length + kaConsumers.length); System.arraycopy(kaConsumers, 0, producers, auConsumers.length, kaConsumers.length); return producers; } } // SSL 3.0 - (D)TLS 1.2 static SSLKeyExchange valueOf( CipherSuite.KeyExchange keyExchange, ProtocolVersion protocolVersion) { if (keyExchange == null || protocolVersion == null) { return null; } switch (keyExchange) { case K_RSA: return SSLKeyExRSA.KE; case K_RSA_EXPORT: return SSLKeyExRSAExport.KE; case K_DHE_DSS: return SSLKeyExDHEDSS.KE; case K_DHE_DSS_EXPORT: return SSLKeyExDHEDSSExport.KE; case K_DHE_RSA: if (protocolVersion.useTLS12PlusSpec()) { // (D)TLS 1.2 return SSLKeyExDHERSAOrPSS.KE; } else { // SSL 3.0, TLS 1.0/1.1 return SSLKeyExDHERSA.KE; } case K_DHE_RSA_EXPORT: return SSLKeyExDHERSAExport.KE; case K_DH_ANON: return SSLKeyExDHANON.KE; case K_DH_ANON_EXPORT: return SSLKeyExDHANONExport.KE; case K_ECDH_ECDSA: return SSLKeyExECDHECDSA.KE; case K_ECDH_RSA: return SSLKeyExECDHRSA.KE; case K_ECDHE_ECDSA: return SSLKeyExECDHEECDSA.KE; case K_ECDHE_RSA: if (protocolVersion.useTLS12PlusSpec()) { // (D)TLS 1.2 return SSLKeyExECDHERSAOrPSS.KE; } else { // SSL 3.0, TLS 1.0/1.1 return SSLKeyExECDHERSA.KE; } case K_ECDH_ANON: return SSLKeyExECDHANON.KE; } return null; } // TLS 1.3 static SSLKeyExchange valueOf(NamedGroup namedGroup) { SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup); if (ka != null) { return new SSLKeyExchange( null, T13KeyAgreement.valueOf(namedGroup)); } return null; } private static class SSLKeyExRSA { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA, T12KeyAgreement.RSA); } private static class SSLKeyExRSAExport { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA, T12KeyAgreement.RSA_EXPORT); } private static class SSLKeyExDHEDSS { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.DSA, T12KeyAgreement.DHE); } private static class SSLKeyExDHEDSSExport { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.DSA, T12KeyAgreement.DHE_EXPORT); } private static class SSLKeyExDHERSA { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA, T12KeyAgreement.DHE); } private static class SSLKeyExDHERSAOrPSS { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA_OR_PSS, T12KeyAgreement.DHE); } private static class SSLKeyExDHERSAExport { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA, T12KeyAgreement.DHE_EXPORT); } private static class SSLKeyExDHANON { private static SSLKeyExchange KE = new SSLKeyExchange( null, T12KeyAgreement.DHE); } private static class SSLKeyExDHANONExport { private static SSLKeyExchange KE = new SSLKeyExchange( null, T12KeyAgreement.DHE_EXPORT); } private static class SSLKeyExECDHECDSA { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.EC, T12KeyAgreement.ECDH); } private static class SSLKeyExECDHRSA { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.EC, T12KeyAgreement.ECDH); } private static class SSLKeyExECDHEECDSA { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.EC, T12KeyAgreement.ECDHE); } private static class SSLKeyExECDHERSA { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA, T12KeyAgreement.ECDHE); } private static class SSLKeyExECDHERSAOrPSS { private static SSLKeyExchange KE = new SSLKeyExchange( X509Authentication.RSA_OR_PSS, T12KeyAgreement.ECDHE); } private static class SSLKeyExECDHANON { private static SSLKeyExchange KE = new SSLKeyExchange( null, T12KeyAgreement.ECDHE); } private enum T12KeyAgreement implements SSLKeyAgreement { RSA ("rsa", null, RSAKeyExchange.kaGenerator), RSA_EXPORT ("rsa_export", RSAKeyExchange.poGenerator, RSAKeyExchange.kaGenerator), DHE ("dhe", DHKeyExchange.poGenerator, DHKeyExchange.kaGenerator), DHE_EXPORT ("dhe_export", DHKeyExchange.poExportableGenerator, DHKeyExchange.kaGenerator), ECDH ("ecdh", null, ECDHKeyExchange.ecdhKAGenerator), ECDHE ("ecdhe", ECDHKeyExchange.poGenerator, ECDHKeyExchange.ecdheKAGenerator); final String name; final SSLPossessionGenerator possessionGenerator; final SSLKeyAgreementGenerator keyAgreementGenerator; T12KeyAgreement(String name, SSLPossessionGenerator possessionGenerator, SSLKeyAgreementGenerator keyAgreementGenerator) { this.name = name; this.possessionGenerator = possessionGenerator; this.keyAgreementGenerator = keyAgreementGenerator; } @Override public SSLPossession createPossession(HandshakeContext context) { if (possessionGenerator != null) { return possessionGenerator.createPossession(context); } return null; } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext context) throws IOException { return keyAgreementGenerator.createKeyDerivation(context); } @Override public SSLHandshake[] getRelatedHandshakers( HandshakeContext handshakeContext) { if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { if (this.possessionGenerator != null) { return new SSLHandshake[] { SSLHandshake.SERVER_KEY_EXCHANGE }; } } return new SSLHandshake[0]; } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Map.Entry[] getHandshakeProducers( HandshakeContext handshakeContext) { if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { return (Map.Entry[])(new Map.Entry[0]); } if (handshakeContext.sslConfig.isClientMode) { switch (this) { case RSA: case RSA_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, RSAClientKeyExchange.rsaHandshakeProducer ) }); case DHE: case DHE_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( SSLHandshake.CLIENT_KEY_EXCHANGE.id, DHClientKeyExchange.dhHandshakeProducer ) }); case ECDH: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, ECDHClientKeyExchange.ecdhHandshakeProducer ) }); case ECDHE: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, ECDHClientKeyExchange.ecdheHandshakeProducer ) }); } } else { switch (this) { case RSA_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.SERVER_KEY_EXCHANGE.id, RSAServerKeyExchange.rsaHandshakeProducer ) }); case DHE: case DHE_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.SERVER_KEY_EXCHANGE.id, DHServerKeyExchange.dhHandshakeProducer ) }); case ECDHE: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.SERVER_KEY_EXCHANGE.id, ECDHServerKeyExchange.ecdheHandshakeProducer ) }); } } return (Map.Entry[])(new Map.Entry[0]); } @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Map.Entry[] getHandshakeConsumers( HandshakeContext handshakeContext) { if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { return (Map.Entry[])(new Map.Entry[0]); } if (handshakeContext.sslConfig.isClientMode) { switch (this) { case RSA_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.SERVER_KEY_EXCHANGE.id, RSAServerKeyExchange.rsaHandshakeConsumer ) }); case DHE: case DHE_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.SERVER_KEY_EXCHANGE.id, DHServerKeyExchange.dhHandshakeConsumer ) }); case ECDHE: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.SERVER_KEY_EXCHANGE.id, ECDHServerKeyExchange.ecdheHandshakeConsumer ) }); } } else { switch (this) { case RSA: case RSA_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, RSAClientKeyExchange.rsaHandshakeConsumer ) }); case DHE: case DHE_EXPORT: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, DHClientKeyExchange.dhHandshakeConsumer ) }); case ECDH: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, ECDHClientKeyExchange.ecdhHandshakeConsumer ) }); case ECDHE: return (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry<>( SSLHandshake.CLIENT_KEY_EXCHANGE.id, ECDHClientKeyExchange.ecdheHandshakeConsumer ) }); } } return (Map.Entry[])(new Map.Entry[0]); } } private static final class T13KeyAgreement implements SSLKeyAgreement { private final NamedGroup namedGroup; static final Map supportedKeyShares = new HashMap<>(); static { for (NamedGroup namedGroup : SupportedGroups.supportedNamedGroups) { supportedKeyShares.put( namedGroup, new T13KeyAgreement(namedGroup)); } } private T13KeyAgreement(NamedGroup namedGroup) { this.namedGroup = namedGroup; } static T13KeyAgreement valueOf(NamedGroup namedGroup) { return supportedKeyShares.get(namedGroup); } @Override public SSLPossession createPossession(HandshakeContext hc) { Optional ngf = namedGroup.getFunctions(); if (ngf.isEmpty()) { return null; } return ngf.get().createPossession(hc.sslContext.getSecureRandom()); } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { Optional ngf = namedGroup.getFunctions(); if (ngf.isEmpty()) { return null; } return ngf.get().createKeyDerivation(hc); } } }