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}