CompositeX509ExtendedKeyManager.java

  1. /*
  2.  * Copyright 2019-2021 the original author or authors.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */

  16. package nl.altindag.ssl.keymanager;

  17. import javax.net.ssl.SSLEngine;
  18. import javax.net.ssl.X509ExtendedKeyManager;
  19. import java.net.InetSocketAddress;
  20. import java.net.Socket;
  21. import java.net.URI;
  22. import java.security.Principal;
  23. import java.security.PrivateKey;
  24. import java.security.cert.X509Certificate;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.Collections;
  28. import java.util.HashMap;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.Optional;

  32. /**
  33.  * Represents an ordered list of {@link X509ExtendedKeyManager} with most-preferred managers first.
  34.  *
  35.  * This is necessary because of the fine-print on {@link javax.net.ssl.SSLContext#init}:
  36.  * Only the first instance of a particular key and/or key manager implementation type in the
  37.  * array is used. (For example, only the first javax.net.ssl.X509KeyManager in the array will be used.)
  38.  * The KeyManager can be build from one or more of any combination provided within the {@link nl.altindag.ssl.util.KeyManagerUtils.KeyManagerBuilder KeyManagerUtils.KeyManagerBuilder}.
  39.  * <br><br>
  40.  * This includes:
  41.  * <pre>
  42.  *     - Any amount of custom KeyManagers
  43.  *     - Any amount of custom Identities
  44.  * </pre>
  45.  *
  46.  * <p>
  47.  * <strong>NOTE:</strong>
  48.  * Please don't use this class directly as it is part of the internal API. Class name and methods can be changed any time.
  49.  * Instead use the {@link nl.altindag.ssl.util.KeyManagerUtils KeyManagerUtils} which provides the same functionality
  50.  * while it has a stable API because it is part of the public API.
  51.  * </p>
  52.  *
  53.  * @see <a href="http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm">
  54.  *     http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm
  55.  *     </a>
  56.  * @see <a href="http://codyaray.com/2013/04/java-ssl-with-multiple-keystores">
  57.  *     http://codyaray.com/2013/04/java-ssl-with-multiple-keystores
  58.  *     </a>
  59.  *
  60.  * @author Cody Ray
  61.  * @author Hakan Altinda
  62.  */
  63. public final class CompositeX509ExtendedKeyManager extends X509ExtendedKeyManager {

  64.     private final List<X509ExtendedKeyManager> keyManagers;
  65.     private final Map<String, List<URI>> preferredClientAliasToHost;

  66.     /**
  67.      * Creates a new {@link CompositeX509ExtendedKeyManager}.
  68.      *
  69.      * @param keyManagers the {@link X509ExtendedKeyManager}, ordered with the most-preferred managers first.
  70.      */
  71.     public CompositeX509ExtendedKeyManager(List<? extends X509ExtendedKeyManager> keyManagers) {
  72.         this(keyManagers, Collections.emptyMap());
  73.     }

  74.     /**
  75.      * Creates a new {@link CompositeX509ExtendedKeyManager}.
  76.      *
  77.      * @param keyManagers                the {@link X509ExtendedKeyManager}, ordered with the most-preferred managers first.
  78.      * @param preferredClientAliasToHost the preferred client alias to be used for the given host
  79.      */
  80.     public CompositeX509ExtendedKeyManager(List<? extends X509ExtendedKeyManager> keyManagers,
  81.                                            Map<String, List<URI>> preferredClientAliasToHost) {
  82.         this.keyManagers = Collections.unmodifiableList(keyManagers);
  83.         this.preferredClientAliasToHost = new HashMap<>(preferredClientAliasToHost);
  84.     }

  85.     /**
  86.      * Chooses the first non-null client alias returned from the delegate
  87.      * {@link X509ExtendedKeyManager}, or {@code null} if there are no matches.
  88.      */
  89.     @Override
  90.     public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
  91.         Optional<String> preferredAlias = Optional.empty();
  92.         if (!preferredClientAliasToHost.isEmpty() && socket != null && socket.getRemoteSocketAddress() instanceof InetSocketAddress) {
  93.             InetSocketAddress address = (InetSocketAddress) socket.getRemoteSocketAddress();
  94.             preferredAlias = getPreferredClientAlias(address.getHostName(), address.getPort());
  95.         }

  96.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  97.             String alias = keyManager.chooseClientAlias(keyType, issuers, socket);
  98.             if (alias != null) {
  99.                 if (preferredAlias.isPresent()) {
  100.                     if (preferredAlias.get().equals(alias)) {
  101.                         return alias;
  102.                     }
  103.                 } else {
  104.                     return alias;
  105.                 }
  106.             }
  107.         }
  108.         return null;
  109.     }

  110.     /**
  111.      * Chooses the first non-null client alias returned from the delegate
  112.      * {@link X509ExtendedKeyManager}, or {@code null} if there are no matches.
  113.      */
  114.     @Override
  115.     public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine sslEngine) {
  116.         Optional<String> preferredAlias = Optional.empty();
  117.         if (!preferredClientAliasToHost.isEmpty() && sslEngine != null) {
  118.             preferredAlias = getPreferredClientAlias(sslEngine.getPeerHost(), sslEngine.getPeerPort());
  119.         }

  120.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  121.             String alias = keyManager.chooseEngineClientAlias(keyTypes, issuers, sslEngine);
  122.             if (alias != null) {
  123.                 if (preferredAlias.isPresent()) {
  124.                     if (preferredAlias.get().equals(alias)) {
  125.                         return alias;
  126.                     }
  127.                 } else {
  128.                     return alias;
  129.                 }
  130.             }
  131.         }
  132.         return null;
  133.     }

  134.     private Optional<String> getPreferredClientAlias(String peerHost, int peerPort) {
  135.         return preferredClientAliasToHost.entrySet().stream()
  136.                 .filter(entry -> entry.getValue().stream().anyMatch(uri -> uri.getHost().equals(peerHost)))
  137.                 .filter(entry -> entry.getValue().stream().anyMatch(uri -> uri.getPort() == peerPort))
  138.                 .findFirst()
  139.                 .map(Map.Entry::getKey);
  140.     }

  141.     /**
  142.      * Chooses the first non-null server alias returned from the delegate
  143.      * {@link X509ExtendedKeyManager}, or {@code null} if there are no matches.
  144.      */
  145.     @Override
  146.     public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
  147.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  148.             String alias = keyManager.chooseServerAlias(keyType, issuers, socket);
  149.             if (alias != null) {
  150.                 return alias;
  151.             }
  152.         }
  153.         return null;
  154.     }

  155.     /**
  156.      * Chooses the first non-null server alias returned from the delegate
  157.      * {@link X509ExtendedKeyManager}, or {@code null} if there are no matches.
  158.      */
  159.     @Override
  160.     public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine sslEngine) {
  161.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  162.             String alias = keyManager.chooseEngineServerAlias(keyType, issuers, sslEngine);
  163.             if (alias != null) {
  164.                 return alias;
  165.             }
  166.         }
  167.         return null;
  168.     }

  169.     /**
  170.      * Returns the first non-null private key associated with the
  171.      * given alias, or {@code null} if the alias can't be found.
  172.      */
  173.     @Override
  174.     public PrivateKey getPrivateKey(String alias) {
  175.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  176.             PrivateKey privateKey = keyManager.getPrivateKey(alias);
  177.             if (privateKey != null) {
  178.                 return privateKey;
  179.             }
  180.         }
  181.         return null;
  182.     }

  183.     /**
  184.      * Returns the first non-null certificate chain associated with the
  185.      * given alias, or {@code null} if the alias can't be found.
  186.      */
  187.     @Override
  188.     public X509Certificate[] getCertificateChain(String alias) {
  189.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  190.             X509Certificate[] chain = keyManager.getCertificateChain(alias);
  191.             if (chain != null && chain.length > 0) {
  192.                 return chain;
  193.             }
  194.         }
  195.         return null;
  196.     }

  197.     /**
  198.      * Get all matching aliases for authenticating the client side of a
  199.      * secure socket, or {@code null} if there are no matches.
  200.      */
  201.     @Override
  202.     public String[] getClientAliases(String keyType, Principal[] issuers) {
  203.         List<String> clientAliases = new ArrayList<>();
  204.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  205.             Optional.ofNullable(keyManager.getClientAliases(keyType, issuers))
  206.                     .ifPresent(aliases -> clientAliases.addAll(Arrays.asList(aliases)));
  207.         }
  208.         return emptyToNull(clientAliases.toArray(new String[]{}));
  209.     }

  210.     /**
  211.      * Get all matching aliases for authenticating the server side of a
  212.      * secure socket, or {@code null} if there are no matches.
  213.      */
  214.     @Override
  215.     public String[] getServerAliases(String keyType, Principal[] issuers) {
  216.         List<String> serverAliases = new ArrayList<>();
  217.         for (X509ExtendedKeyManager keyManager : keyManagers) {
  218.             Optional.ofNullable(keyManager.getServerAliases(keyType, issuers))
  219.                     .ifPresent(aliases -> serverAliases.addAll(Arrays.asList(aliases)));
  220.         }
  221.         return emptyToNull(serverAliases.toArray(new String[]{}));
  222.     }

  223.     private <T> T[] emptyToNull(T[] arr) {
  224.         return (arr.length == 0) ? null : arr;
  225.     }

  226.     public int size() {
  227.         return keyManagers.size();
  228.     }

  229.     public List<X509ExtendedKeyManager> getKeyManagers() {
  230.         return keyManagers;
  231.     }

  232.     public Map<String, List<URI>> getPreferredClientAliasToHosts() {
  233.         return preferredClientAliasToHost;
  234.     }

  235. }