/core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/DefaultCasSslContext.java

https://github.com/frett/cas · Java · 232 lines · 160 code · 35 blank · 37 comment · 4 complexity · 307e14d3be3674f8cf2d15726d070944 MD5 · raw file

  1. package org.apereo.cas.authentication;
  2. import org.apereo.cas.util.CollectionUtils;
  3. import lombok.Getter;
  4. import lombok.extern.slf4j.Slf4j;
  5. import lombok.val;
  6. import org.apache.http.ssl.SSLContexts;
  7. import org.springframework.core.io.Resource;
  8. import javax.net.ssl.KeyManager;
  9. import javax.net.ssl.KeyManagerFactory;
  10. import javax.net.ssl.SSLContext;
  11. import javax.net.ssl.TrustManager;
  12. import javax.net.ssl.TrustManagerFactory;
  13. import javax.net.ssl.X509KeyManager;
  14. import javax.net.ssl.X509TrustManager;
  15. import java.io.IOException;
  16. import java.net.Socket;
  17. import java.security.KeyStore;
  18. import java.security.Principal;
  19. import java.security.PrivateKey;
  20. import java.security.cert.Certificate;
  21. import java.security.cert.CertificateException;
  22. import java.security.cert.X509Certificate;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Collection;
  26. import java.util.List;
  27. import java.util.Objects;
  28. import java.util.stream.Collectors;
  29. /**
  30. * This is {@link DefaultCasSslContext}.
  31. *
  32. * @author Misagh Moayyed
  33. * @since 5.2.0
  34. */
  35. @Getter
  36. public class DefaultCasSslContext {
  37. private static final String ALG_NAME_PKIX = "PKIX";
  38. private final SSLContext sslContext;
  39. public DefaultCasSslContext(final Resource trustStoreFile, final String trustStorePassword, final String trustStoreType) throws IOException {
  40. try {
  41. this.sslContext = initialize(trustStoreFile, trustStorePassword, trustStoreType);
  42. } catch (final Exception e) {
  43. throw new IOException(e.getMessage(), e);
  44. }
  45. }
  46. private static SSLContext initialize(final Resource trustStoreFile, final String trustStorePassword, final String trustStoreType) throws Exception {
  47. val casTrustStore = KeyStore.getInstance(trustStoreType);
  48. val trustStorePasswordCharArray = trustStorePassword.toCharArray();
  49. try (val casStream = trustStoreFile.getInputStream()) {
  50. casTrustStore.load(casStream, trustStorePasswordCharArray);
  51. }
  52. val defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
  53. val customKeyManager = getKeyManager(ALG_NAME_PKIX, casTrustStore, trustStorePasswordCharArray);
  54. val jvmKeyManager = getKeyManager(defaultAlgorithm, null, null);
  55. val defaultTrustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
  56. val customTrustManager = getTrustManager(ALG_NAME_PKIX, casTrustStore);
  57. val jvmTrustManagers = getTrustManager(defaultTrustAlgorithm, null);
  58. final KeyManager[] keyManagers = {
  59. new CompositeX509KeyManager(CollectionUtils.wrapList(jvmKeyManager, customKeyManager))
  60. };
  61. val allManagers = new ArrayList<X509TrustManager>(customTrustManager);
  62. allManagers.addAll(jvmTrustManagers);
  63. val trustManagers = new TrustManager[]{new CompositeX509TrustManager(allManagers)};
  64. val sslContext = SSLContexts.custom().setProtocol("SSL").build();
  65. sslContext.init(keyManagers, trustManagers, null);
  66. return sslContext;
  67. }
  68. /**
  69. * Gets key manager.
  70. *
  71. * @param algorithm the algorithm
  72. * @param keystore the keystore
  73. * @param password the password
  74. * @return the key manager
  75. * @throws Exception the exception
  76. */
  77. private static X509KeyManager getKeyManager(final String algorithm, final KeyStore keystore,
  78. final char[] password) throws Exception {
  79. val factory = KeyManagerFactory.getInstance(algorithm);
  80. factory.init(keystore, password);
  81. return (X509KeyManager) factory.getKeyManagers()[0];
  82. }
  83. /**
  84. * Gets trust manager.
  85. *
  86. * @param algorithm the algorithm
  87. * @param keystore the keystore
  88. * @return the trust manager
  89. * @throws Exception the exception
  90. */
  91. private static Collection<X509TrustManager> getTrustManager(final String algorithm, final KeyStore keystore) throws Exception {
  92. val factory = TrustManagerFactory.getInstance(algorithm);
  93. factory.init(keystore);
  94. return Arrays.stream(factory.getTrustManagers())
  95. .filter(e -> e instanceof X509TrustManager)
  96. .map(X509TrustManager.class::cast)
  97. .collect(Collectors.toList());
  98. }
  99. private static class CompositeX509KeyManager implements X509KeyManager {
  100. private final List<X509KeyManager> keyManagers;
  101. /**
  102. * Represents an ordered list of {@link X509KeyManager}s with most-preferred managers first.
  103. *
  104. * @param keyManagers list of key managers
  105. */
  106. CompositeX509KeyManager(final List<X509KeyManager> keyManagers) {
  107. this.keyManagers = keyManagers;
  108. }
  109. @Override
  110. public String chooseClientAlias(final String[] keyType, final Principal[] issuers, final Socket socket) {
  111. return this.keyManagers.stream().map(keyManager -> keyManager.chooseClientAlias(keyType, issuers, socket))
  112. .filter(Objects::nonNull).findFirst().orElse(null);
  113. }
  114. @Override
  115. public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) {
  116. return this.keyManagers.stream().map(keyManager -> keyManager.chooseServerAlias(keyType, issuers, socket))
  117. .filter(Objects::nonNull).findFirst().orElse(null);
  118. }
  119. @Override
  120. public PrivateKey getPrivateKey(final String alias) {
  121. return this.keyManagers.stream().map(keyManager -> keyManager.getPrivateKey(alias))
  122. .filter(Objects::nonNull).findFirst().orElse(null);
  123. }
  124. @Override
  125. public X509Certificate[] getCertificateChain(final String alias) {
  126. return this.keyManagers.stream().map(keyManager -> keyManager.getCertificateChain(alias))
  127. .filter(chain -> chain != null && chain.length > 0)
  128. .findFirst().orElse(null);
  129. }
  130. @Override
  131. public String[] getClientAliases(final String keyType, final Principal[] issuers) {
  132. val aliases = new ArrayList<String>();
  133. this.keyManagers.forEach(keyManager -> aliases.addAll(CollectionUtils.wrapList(keyManager.getClientAliases(keyType, issuers))));
  134. return aliases.toArray(new String[]{});
  135. }
  136. @Override
  137. public String[] getServerAliases(final String keyType, final Principal[] issuers) {
  138. val aliases = new ArrayList<String>();
  139. this.keyManagers.forEach(keyManager -> aliases.addAll(CollectionUtils.wrapList(keyManager.getServerAliases(keyType, issuers))));
  140. return aliases.toArray(new String[]{});
  141. }
  142. }
  143. /**
  144. * Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the
  145. * composed managers trusts a certificate chain, then it is trusted by the composite manager.
  146. */
  147. @Slf4j
  148. private static class CompositeX509TrustManager implements X509TrustManager {
  149. private final List<X509TrustManager> trustManagers;
  150. /**
  151. * Instantiates a new Composite x 509 trust manager.
  152. *
  153. * @param trustManagers the trust managers
  154. */
  155. CompositeX509TrustManager(final List<X509TrustManager> trustManagers) {
  156. this.trustManagers = trustManagers;
  157. }
  158. @Override
  159. public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
  160. val trusted = this.trustManagers.stream().anyMatch(trustManager -> {
  161. try {
  162. trustManager.checkClientTrusted(chain, authType);
  163. return true;
  164. } catch (final CertificateException e) {
  165. val msg = "Unable to trust the client certificates [%s] for auth type [%s]: [%s]";
  166. LOGGER.debug(String.format(msg, Arrays.stream(chain).map(Certificate::toString).collect(Collectors.toSet()), authType, e.getMessage()), e);
  167. return false;
  168. }
  169. });
  170. if (!trusted) {
  171. throw new CertificateException("None of the TrustManagers can trust this client certificate chain");
  172. }
  173. }
  174. @Override
  175. public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
  176. val trusted = this.trustManagers.stream().anyMatch(trustManager -> {
  177. try {
  178. trustManager.checkServerTrusted(chain, authType);
  179. return true;
  180. } catch (final CertificateException e) {
  181. val msg = "Unable to trust the server certificates [%s] for auth type [%s]: [%s]";
  182. LOGGER.debug(String.format(msg, Arrays.stream(chain).map(Certificate::toString).collect(Collectors.toSet()), authType, e.getMessage()), e);
  183. return false;
  184. }
  185. });
  186. if (!trusted) {
  187. throw new CertificateException("None of the TrustManagers trust this server certificate chain");
  188. }
  189. }
  190. @Override
  191. public X509Certificate[] getAcceptedIssuers() {
  192. val certificates = new ArrayList<X509Certificate>();
  193. this.trustManagers.forEach(trustManager -> certificates.addAll(CollectionUtils.wrapList(trustManager.getAcceptedIssuers())));
  194. return certificates.toArray(new X509Certificate[0]);
  195. }
  196. }
  197. }