|
16 | 16 |
|
17 | 17 | package org.springframework.boot.web.embedded.netty;
|
18 | 18 |
|
| 19 | +import java.net.Socket; |
19 | 20 | import java.net.URL;
|
| 21 | +import java.security.InvalidAlgorithmParameterException; |
20 | 22 | import java.security.KeyStore;
|
| 23 | +import java.security.KeyStoreException; |
| 24 | +import java.security.NoSuchAlgorithmException; |
| 25 | +import java.security.Principal; |
| 26 | +import java.security.PrivateKey; |
| 27 | +import java.security.Provider; |
| 28 | +import java.security.UnrecoverableKeyException; |
| 29 | +import java.security.cert.X509Certificate; |
21 | 30 | import java.util.Arrays;
|
| 31 | +import java.util.stream.Collectors; |
22 | 32 |
|
| 33 | +import javax.net.ssl.KeyManager; |
23 | 34 | import javax.net.ssl.KeyManagerFactory;
|
| 35 | +import javax.net.ssl.KeyManagerFactorySpi; |
| 36 | +import javax.net.ssl.ManagerFactoryParameters; |
| 37 | +import javax.net.ssl.SSLEngine; |
24 | 38 | import javax.net.ssl.TrustManagerFactory;
|
| 39 | +import javax.net.ssl.X509ExtendedKeyManager; |
25 | 40 |
|
26 | 41 | import io.netty.handler.ssl.ClientAuth;
|
27 | 42 | import io.netty.handler.ssl.SslContextBuilder;
|
|
40 | 55 | *
|
41 | 56 | * @author Brian Clozel
|
42 | 57 | * @author Raheela Aslam
|
| 58 | + * @author Chris Bono |
43 | 59 | * @since 2.0.0
|
44 | 60 | */
|
45 | 61 | public class SslServerCustomizer implements NettyServerCustomizer {
|
@@ -92,8 +108,10 @@ else if (this.ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
|
92 | 108 | protected KeyManagerFactory getKeyManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
93 | 109 | try {
|
94 | 110 | KeyStore keyStore = getKeyStore(ssl, sslStoreProvider);
|
95 |
| - KeyManagerFactory keyManagerFactory = KeyManagerFactory |
96 |
| - .getInstance(KeyManagerFactory.getDefaultAlgorithm()); |
| 111 | + KeyManagerFactory keyManagerFactory = (ssl.getKeyAlias() == null) |
| 112 | + ? KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) |
| 113 | + : ConfigurableAliasKeyManagerFactory.instance(ssl.getKeyAlias(), |
| 114 | + KeyManagerFactory.getDefaultAlgorithm()); |
97 | 115 | char[] keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword().toCharArray() : null;
|
98 | 116 | if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
99 | 117 | keyPassword = ssl.getKeyStorePassword().toCharArray();
|
@@ -161,4 +179,120 @@ private KeyStore loadStore(String type, String provider, String resource, String
|
161 | 179 |
|
162 | 180 | }
|
163 | 181 |
|
| 182 | + /** |
| 183 | + * A {@link KeyManagerFactory} that allows a configurable key alias to be used. Due to |
| 184 | + * the fact that the actual calls to retrieve the key by alias are done at request |
| 185 | + * time the approach is to wrap the actual key managers with a |
| 186 | + * {@link ConfigurableAliasKeyManager}. The actual SPI has to be wrapped as well due |
| 187 | + * to the fact that {@link KeyManagerFactory#getKeyManagers()} is final. |
| 188 | + */ |
| 189 | + private static final class ConfigurableAliasKeyManagerFactory extends KeyManagerFactory { |
| 190 | + |
| 191 | + private static ConfigurableAliasKeyManagerFactory instance(String alias, String algorithm) |
| 192 | + throws NoSuchAlgorithmException { |
| 193 | + KeyManagerFactory originalFactory = KeyManagerFactory.getInstance(algorithm); |
| 194 | + ConfigurableAliasKeyManagerFactorySpi spi = new ConfigurableAliasKeyManagerFactorySpi(originalFactory, |
| 195 | + alias); |
| 196 | + return new ConfigurableAliasKeyManagerFactory(spi, originalFactory.getProvider(), algorithm); |
| 197 | + } |
| 198 | + |
| 199 | + private ConfigurableAliasKeyManagerFactory(ConfigurableAliasKeyManagerFactorySpi spi, Provider provider, |
| 200 | + String algorithm) { |
| 201 | + super(spi, provider, algorithm); |
| 202 | + } |
| 203 | + |
| 204 | + } |
| 205 | + |
| 206 | + private static final class ConfigurableAliasKeyManagerFactorySpi extends KeyManagerFactorySpi { |
| 207 | + |
| 208 | + private KeyManagerFactory originalFactory; |
| 209 | + |
| 210 | + private String alias; |
| 211 | + |
| 212 | + private ConfigurableAliasKeyManagerFactorySpi(KeyManagerFactory originalFactory, String alias) { |
| 213 | + this.originalFactory = originalFactory; |
| 214 | + this.alias = alias; |
| 215 | + } |
| 216 | + |
| 217 | + @Override |
| 218 | + protected void engineInit(KeyStore keyStore, char[] chars) |
| 219 | + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { |
| 220 | + this.originalFactory.init(keyStore, chars); |
| 221 | + } |
| 222 | + |
| 223 | + @Override |
| 224 | + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) |
| 225 | + throws InvalidAlgorithmParameterException { |
| 226 | + throw new InvalidAlgorithmParameterException("Unsupported ManagerFactoryParameters"); |
| 227 | + } |
| 228 | + |
| 229 | + @Override |
| 230 | + protected KeyManager[] engineGetKeyManagers() { |
| 231 | + return Arrays.stream(this.originalFactory.getKeyManagers()).filter(X509ExtendedKeyManager.class::isInstance) |
| 232 | + .map(X509ExtendedKeyManager.class::cast).map(this::wrapKeyManager).collect(Collectors.toList()) |
| 233 | + .toArray(new KeyManager[0]); |
| 234 | + } |
| 235 | + |
| 236 | + private ConfigurableAliasKeyManager wrapKeyManager(X509ExtendedKeyManager km) { |
| 237 | + return new ConfigurableAliasKeyManager(km, this.alias); |
| 238 | + } |
| 239 | + |
| 240 | + } |
| 241 | + |
| 242 | + private static final class ConfigurableAliasKeyManager extends X509ExtendedKeyManager { |
| 243 | + |
| 244 | + private final X509ExtendedKeyManager keyManager; |
| 245 | + |
| 246 | + private final String alias; |
| 247 | + |
| 248 | + private ConfigurableAliasKeyManager(X509ExtendedKeyManager keyManager, String alias) { |
| 249 | + this.keyManager = keyManager; |
| 250 | + this.alias = alias; |
| 251 | + } |
| 252 | + |
| 253 | + @Override |
| 254 | + public String chooseEngineClientAlias(String[] strings, Principal[] principals, SSLEngine sslEngine) { |
| 255 | + return this.keyManager.chooseEngineClientAlias(strings, principals, sslEngine); |
| 256 | + } |
| 257 | + |
| 258 | + @Override |
| 259 | + public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine sslEngine) { |
| 260 | + if (this.alias == null) { |
| 261 | + return this.keyManager.chooseEngineServerAlias(s, principals, sslEngine); |
| 262 | + } |
| 263 | + return this.alias; |
| 264 | + } |
| 265 | + |
| 266 | + @Override |
| 267 | + public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { |
| 268 | + return this.keyManager.chooseClientAlias(keyType, issuers, socket); |
| 269 | + } |
| 270 | + |
| 271 | + @Override |
| 272 | + public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { |
| 273 | + return this.keyManager.chooseServerAlias(keyType, issuers, socket); |
| 274 | + } |
| 275 | + |
| 276 | + @Override |
| 277 | + public X509Certificate[] getCertificateChain(String alias) { |
| 278 | + return this.keyManager.getCertificateChain(alias); |
| 279 | + } |
| 280 | + |
| 281 | + @Override |
| 282 | + public String[] getClientAliases(String keyType, Principal[] issuers) { |
| 283 | + return this.keyManager.getClientAliases(keyType, issuers); |
| 284 | + } |
| 285 | + |
| 286 | + @Override |
| 287 | + public PrivateKey getPrivateKey(String alias) { |
| 288 | + return this.keyManager.getPrivateKey(alias); |
| 289 | + } |
| 290 | + |
| 291 | + @Override |
| 292 | + public String[] getServerAliases(String keyType, Principal[] issuers) { |
| 293 | + return this.keyManager.getServerAliases(keyType, issuers); |
| 294 | + } |
| 295 | + |
| 296 | + } |
| 297 | + |
164 | 298 | }
|
0 commit comments