< prev index next >
src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -23,116 +23,562 @@
* questions.
*/
package sun.security.ssl;
-import sun.security.action.GetPropertyAction;
-
-import java.io.File;
-import java.io.FilePermission;
import java.io.IOException;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.SecureRandom;
-import java.util.*;
-
-/**
- * Models a service that provides support for a particular client key exchange
- * mode. Currently used to implement Kerberos-related cipher suites.
- *
- * @since 9
- */
-public interface ClientKeyExchangeService {
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import sun.security.ssl.DHKeyExchange.DHEPossession;
+import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.ssl.X509Authentication.X509Possession;
+
+final class SSLKeyExchange implements SSLKeyAgreement {
+ private final SSLAuthentication authentication;
+ private final SSLKeyAgreement keyAgreement;
+
+ SSLKeyExchange(X509Authentication authentication,
+ SSLKeyAgreement keyAgreement) {
+ this.authentication = authentication;
+ this.keyAgreement = keyAgreement;
+ }
- static class Loader {
- private static final Map<String,ClientKeyExchangeService>
- providers = new HashMap<>();
+ 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;
+ }
+ }
- static {
- String path = GetPropertyAction.privilegedGetProperty("java.home");
- ServiceLoader<ClientKeyExchangeService> sc =
- AccessController.doPrivileged(
- (PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
- () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
- null,
- new FilePermission(new File(path, "-").toString(), "read"));
- Iterator<ClientKeyExchangeService> iter = sc.iterator();
- while (iter.hasNext()) {
- ClientKeyExchangeService cs = iter.next();
- for (String ex: cs.supported()) {
- providers.put(ex, cs);
+ // 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 SSLPossession createPossession(HandshakeContext handshakeContext) {
+ // Please call createPossessions() so that the SSLAuthentication
+ // is counted.
+ throw new UnsupportedOperationException(
+ "SSLKeyExchange.createPossessions() should be used instead");
}
- public static ClientKeyExchangeService find(String ex) {
- return Loader.providers.get(ex);
+ @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;
+ }
- /**
- * Returns the supported key exchange modes by this provider.
- * @return the supported key exchange modes
- */
- String[] supported();
+ SSLHandshake[] kaHandshakes =
+ keyAgreement.getRelatedHandshakers(handshakeContext);
- /**
- * Returns a generalized credential object on the server side. The server
- * side can use the info to determine if a cipher suite can be enabled.
- * @param acc the AccessControlContext of the SSL session
- * @return the credential object
- */
- Object getServiceCreds(AccessControlContext acc);
+ 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;
+ }
+ }
- /**
- * Returns the host name for a service principal. The info can be used in
- * SNI or host name verifier.
- * @param principal the principal of a service
- * @return the string formed host name
- */
- String getServiceHostName(Principal principal);
+ @Override
+ public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
+ HandshakeContext handshakeContext) {
+ Map.Entry<Byte, HandshakeProducer>[] auProducers;
+ if (authentication != null) {
+ auProducers =
+ authentication.getHandshakeProducers(handshakeContext);
+ } else {
+ auProducers = null;
+ }
- /**
- * Returns whether the specified principal is related to the current
- * SSLSession. The info can be used to verify a SSL resume.
- * @param isClient if true called from client side, otherwise from server
- * @param acc the AccessControlContext of the SSL session
- * @param p the specified principal
- * @return true if related
- */
- boolean isRelated(boolean isClient, AccessControlContext acc, Principal p);
+ Map.Entry<Byte, HandshakeProducer>[] kaProducers =
+ keyAgreement.getHandshakeProducers(handshakeContext);
- /**
- * Creates the ClientKeyExchange object on the client side.
- * @param serverName the intented peer name
- * @param acc the AccessControlContext of the SSL session
- * @param protocolVersion the TLS protocol version
- * @param rand the SecureRandom that will used to generate the premaster
- * @return the new Exchanger object
- * @throws IOException if there is an error
- */
- ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc,
- ProtocolVersion protocolVersion, SecureRandom rand) throws IOException;
+ if (auProducers == null || auProducers.length == 0) {
+ return kaProducers;
+ } else if (kaProducers == null || kaProducers.length == 0) {
+ return auProducers;
+ } else {
+ Map.Entry<Byte, HandshakeProducer>[] producers = Arrays.copyOf(
+ auProducers, auProducers.length + kaProducers.length);
+ System.arraycopy(kaProducers, 0,
+ producers, auProducers.length, kaProducers.length);
+ return producers;
+ }
+ }
- /**
- * Create the ClientKeyExchange on the server side.
- * @param protocolVersion the protocol version
- * @param clientVersion the input protocol version
- * @param rand a SecureRandom object used to generate premaster
- * (if the server has to create one)
- * @param encodedTicket the ticket from client
- * @param encrypted the encrypted premaster secret from client
- * @param acc the AccessControlContext of the SSL session
- * @param ServiceCreds the service side credentials object as retrived from
- * {@link #getServiceCreds}
- * @return the new Exchanger object
- * @throws IOException if there is an error
- */
- ClientKeyExchange createServerExchange(
- ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
- SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
- AccessControlContext acc, Object ServiceCreds) throws IOException;
+ @Override
+ public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
+ HandshakeContext handshakeContext) {
+ Map.Entry<Byte, SSLConsumer>[] auConsumers;
+ if (authentication != null) {
+ auConsumers =
+ authentication.getHandshakeConsumers(handshakeContext);
+ } else {
+ auConsumers = null;
+ }
+
+ Map.Entry<Byte, SSLConsumer>[] kaConsumers =
+ keyAgreement.getHandshakeConsumers(handshakeContext);
+
+ if (auConsumers == null || auConsumers.length == 0) {
+ return kaConsumers;
+ } else if (kaConsumers == null || kaConsumers.length == 0) {
+ return auConsumers;
+ } else {
+ Map.Entry<Byte, SSLConsumer>[] 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) {
+ if (keyExchange == 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:
+ 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:
+ 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 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 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<Byte, HandshakeProducer>[] getHandshakeProducers(
+ HandshakeContext handshakeContext) {
+ if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+ return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
+ }
+
+ if (handshakeContext.sslConfig.isClientMode) {
+ switch (this) {
+ case RSA:
+ case RSA_EXPORT:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ RSAClientKeyExchange.rsaHandshakeProducer
+ )
+ });
+
+ case DHE:
+ case DHE_EXPORT:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<Byte, HandshakeProducer>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ DHClientKeyExchange.dhHandshakeProducer
+ )
+ });
+
+ case ECDH:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ ECDHClientKeyExchange.ecdhHandshakeProducer
+ )
+ });
+
+ case ECDHE:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ ECDHClientKeyExchange.ecdheHandshakeProducer
+ )
+ });
+ }
+ } else {
+ switch (this) {
+ case RSA_EXPORT:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.SERVER_KEY_EXCHANGE.id,
+ RSAServerKeyExchange.rsaHandshakeProducer
+ )
+ });
+
+ case DHE:
+ case DHE_EXPORT:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.SERVER_KEY_EXCHANGE.id,
+ DHServerKeyExchange.dhHandshakeProducer
+ )
+ });
+
+ case ECDHE:
+ return (Map.Entry<Byte,
+ HandshakeProducer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.SERVER_KEY_EXCHANGE.id,
+ ECDHServerKeyExchange.ecdheHandshakeProducer
+ )
+ });
+ }
+ }
+
+ return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
+ HandshakeContext handshakeContext) {
+ if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+ return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
+ }
+
+ if (handshakeContext.sslConfig.isClientMode) {
+ switch (this) {
+ case RSA_EXPORT:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.SERVER_KEY_EXCHANGE.id,
+ RSAServerKeyExchange.rsaHandshakeConsumer
+ )
+ });
+
+ case DHE:
+ case DHE_EXPORT:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.SERVER_KEY_EXCHANGE.id,
+ DHServerKeyExchange.dhHandshakeConsumer
+ )
+ });
+
+ case ECDHE:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.SERVER_KEY_EXCHANGE.id,
+ ECDHServerKeyExchange.ecdheHandshakeConsumer
+ )
+ });
+ }
+ } else {
+ switch (this) {
+ case RSA:
+ case RSA_EXPORT:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ RSAClientKeyExchange.rsaHandshakeConsumer
+ )
+ });
+
+ case DHE:
+ case DHE_EXPORT:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ DHClientKeyExchange.dhHandshakeConsumer
+ )
+ });
+
+ case ECDH:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ ECDHClientKeyExchange.ecdhHandshakeConsumer
+ )
+ });
+
+ case ECDHE:
+ return (Map.Entry<Byte,
+ SSLConsumer>[])(new Map.Entry[] {
+ new SimpleImmutableEntry<>(
+ SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+ ECDHClientKeyExchange.ecdheHandshakeConsumer
+ )
+ });
+ }
+ }
+
+ return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
+ }
+ }
+
+ private static final class T13KeyAgreement implements SSLKeyAgreement {
+ private final NamedGroup namedGroup;
+ static final Map<NamedGroup, T13KeyAgreement>
+ 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) {
+ if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+ return new ECDHEPossession(
+ namedGroup, hc.sslContext.getSecureRandom());
+ } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+ return new DHEPossession(
+ namedGroup, hc.sslContext.getSecureRandom());
+ }
+
+ return null;
+ }
+
+ @Override
+ public SSLKeyDerivation createKeyDerivation(
+ HandshakeContext hc) throws IOException {
+ if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+ return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
+ } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+ return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
+ }
+
+ return null;
+ }
+ }
}
< prev index next >