001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.web.embedded.netty; 018 019import java.net.URL; 020import java.security.KeyStore; 021import java.util.Arrays; 022 023import javax.net.ssl.KeyManagerFactory; 024import javax.net.ssl.TrustManagerFactory; 025 026import io.netty.handler.ssl.ClientAuth; 027import io.netty.handler.ssl.SslContextBuilder; 028import reactor.netty.http.server.HttpServer; 029import reactor.netty.tcp.SslProvider; 030 031import org.springframework.boot.web.server.Http2; 032import org.springframework.boot.web.server.Ssl; 033import org.springframework.boot.web.server.SslStoreProvider; 034import org.springframework.util.ResourceUtils; 035 036/** 037 * {@link NettyServerCustomizer} that configures SSL for the given Reactor Netty server 038 * instance. 039 * 040 * @author Brian Clozel 041 */ 042public class SslServerCustomizer implements NettyServerCustomizer { 043 044 private final Ssl ssl; 045 046 private final Http2 http2; 047 048 private final SslStoreProvider sslStoreProvider; 049 050 public SslServerCustomizer(Ssl ssl, Http2 http2, SslStoreProvider sslStoreProvider) { 051 this.ssl = ssl; 052 this.http2 = http2; 053 this.sslStoreProvider = sslStoreProvider; 054 } 055 056 @Override 057 public HttpServer apply(HttpServer server) { 058 try { 059 return server.secure((contextSpec) -> { 060 SslProvider.DefaultConfigurationSpec spec = contextSpec 061 .sslContext(getContextBuilder()); 062 if (this.http2 != null && this.http2.isEnabled()) { 063 spec.defaultConfiguration(SslProvider.DefaultConfigurationType.H2); 064 } 065 }); 066 } 067 catch (Exception ex) { 068 throw new IllegalStateException(ex); 069 } 070 } 071 072 protected SslContextBuilder getContextBuilder() { 073 SslContextBuilder builder = SslContextBuilder 074 .forServer(getKeyManagerFactory(this.ssl, this.sslStoreProvider)) 075 .trustManager(getTrustManagerFactory(this.ssl, this.sslStoreProvider)); 076 if (this.ssl.getEnabledProtocols() != null) { 077 builder.protocols(this.ssl.getEnabledProtocols()); 078 } 079 if (this.ssl.getCiphers() != null) { 080 builder.ciphers(Arrays.asList(this.ssl.getCiphers())); 081 } 082 if (this.ssl.getClientAuth() == Ssl.ClientAuth.NEED) { 083 builder.clientAuth(ClientAuth.REQUIRE); 084 } 085 else if (this.ssl.getClientAuth() == Ssl.ClientAuth.WANT) { 086 builder.clientAuth(ClientAuth.OPTIONAL); 087 } 088 return builder; 089 } 090 091 protected KeyManagerFactory getKeyManagerFactory(Ssl ssl, 092 SslStoreProvider sslStoreProvider) { 093 try { 094 KeyStore keyStore = getKeyStore(ssl, sslStoreProvider); 095 KeyManagerFactory keyManagerFactory = KeyManagerFactory 096 .getInstance(KeyManagerFactory.getDefaultAlgorithm()); 097 char[] keyPassword = (ssl.getKeyPassword() != null) 098 ? ssl.getKeyPassword().toCharArray() : null; 099 if (keyPassword == null && ssl.getKeyStorePassword() != null) { 100 keyPassword = ssl.getKeyStorePassword().toCharArray(); 101 } 102 keyManagerFactory.init(keyStore, keyPassword); 103 return keyManagerFactory; 104 } 105 catch (Exception ex) { 106 throw new IllegalStateException(ex); 107 } 108 } 109 110 private KeyStore getKeyStore(Ssl ssl, SslStoreProvider sslStoreProvider) 111 throws Exception { 112 if (sslStoreProvider != null) { 113 return sslStoreProvider.getKeyStore(); 114 } 115 return loadKeyStore(ssl.getKeyStoreType(), ssl.getKeyStoreProvider(), 116 ssl.getKeyStore(), ssl.getKeyStorePassword()); 117 } 118 119 protected TrustManagerFactory getTrustManagerFactory(Ssl ssl, 120 SslStoreProvider sslStoreProvider) { 121 try { 122 KeyStore store = getTrustStore(ssl, sslStoreProvider); 123 TrustManagerFactory trustManagerFactory = TrustManagerFactory 124 .getInstance(TrustManagerFactory.getDefaultAlgorithm()); 125 trustManagerFactory.init(store); 126 return trustManagerFactory; 127 } 128 catch (Exception ex) { 129 throw new IllegalStateException(ex); 130 } 131 } 132 133 private KeyStore getTrustStore(Ssl ssl, SslStoreProvider sslStoreProvider) 134 throws Exception { 135 if (sslStoreProvider != null) { 136 return sslStoreProvider.getTrustStore(); 137 } 138 return loadKeyStore(ssl.getTrustStoreType(), ssl.getTrustStoreProvider(), 139 ssl.getTrustStore(), ssl.getTrustStorePassword()); 140 } 141 142 private KeyStore loadKeyStore(String type, String provider, String resource, 143 String password) throws Exception { 144 type = (type != null) ? type : "JKS"; 145 if (resource == null) { 146 return null; 147 } 148 KeyStore store = (provider != null) ? KeyStore.getInstance(type, provider) 149 : KeyStore.getInstance(type); 150 URL url = ResourceUtils.getURL(resource); 151 store.load(url.openStream(), (password != null) ? password.toCharArray() : null); 152 return store; 153 } 154 155}