View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package org.jboss.netty.handler.ssl;
18  
19  import org.jboss.netty.buffer.ChannelBuffer;
20  import org.jboss.netty.buffer.ChannelBufferInputStream;
21  
22  import javax.net.ssl.SSLContext;
23  import javax.net.ssl.SSLException;
24  import javax.net.ssl.SSLSessionContext;
25  import javax.net.ssl.TrustManagerFactory;
26  import javax.security.auth.x500.X500Principal;
27  import java.io.File;
28  import java.security.KeyStore;
29  import java.security.cert.CertificateFactory;
30  import java.security.cert.X509Certificate;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.List;
34  
35  /**
36   * A client-side {@link SslContext} which uses JDK's SSL/TLS implementation.
37   */
38  public final class JdkSslClientContext extends JdkSslContext {
39  
40      private final SSLContext ctx;
41      private final List<String> nextProtocols;
42  
43      /**
44       * Creates a new instance.
45       */
46      public JdkSslClientContext() throws SSLException {
47          this(null, null, null, null, null, 0, 0);
48      }
49  
50      /**
51       * Creates a new instance.
52       *
53       * @param certChainFile an X.509 certificate chain file in PEM format.
54       *                      {@code null} to use the system default
55       */
56      public JdkSslClientContext(File certChainFile) throws SSLException {
57          this(certChainFile, null);
58      }
59  
60      /**
61       * Creates a new instance.
62       *
63       * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link javax.net.ssl.TrustManager}s
64       *                            that verifies the certificates sent from servers.
65       *                            {@code null} to use the default.
66       */
67      public JdkSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
68          this(null, trustManagerFactory);
69      }
70  
71      /**
72       * Creates a new instance.
73       *
74       * @param certChainFile an X.509 certificate chain file in PEM format.
75       *                      {@code null} to use the system default
76       * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link javax.net.ssl.TrustManager}s
77       *                            that verifies the certificates sent from servers.
78       *                            {@code null} to use the default.
79       */
80      public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
81          this(null, certChainFile, trustManagerFactory, null, null, 0, 0);
82      }
83  
84      /**
85       * Creates a new instance.
86       *
87       * @param bufPool the buffer pool which will be used by this context.
88       *                {@code null} to use the default buffer pool.
89       * @param certChainFile an X.509 certificate chain file in PEM format.
90       *                      {@code null} to use the system default
91       * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link javax.net.ssl.TrustManager}s
92       *                            that verifies the certificates sent from servers.
93       *                            {@code null} to use the default.
94       * @param ciphers the cipher suites to enable, in the order of preference.
95       *                {@code null} to use the default cipher suites.
96       * @param nextProtocols the application layer protocols to accept, in the order of preference.
97       *                      {@code null} to disable TLS NPN/ALPN extension.
98       * @param sessionCacheSize the size of the cache used for storing SSL session objects.
99       *                         {@code 0} to use the default value.
100      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
101      *                       {@code 0} to use the default value.
102      */
103     public JdkSslClientContext(
104             SslBufferPool bufPool, File certChainFile, TrustManagerFactory trustManagerFactory,
105             Iterable<String> ciphers, Iterable<String> nextProtocols,
106             long sessionCacheSize, long sessionTimeout) throws SSLException {
107 
108         super(bufPool, ciphers);
109 
110         if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
111             if (!JettyNpnSslEngine.isAvailable()) {
112                 throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
113             }
114 
115             List<String> nextProtoList = new ArrayList<String>();
116             for (String p: nextProtocols) {
117                 if (p == null) {
118                     break;
119                 }
120                 nextProtoList.add(p);
121             }
122             this.nextProtocols = Collections.unmodifiableList(nextProtoList);
123         } else {
124             this.nextProtocols = Collections.emptyList();
125         }
126 
127         try {
128             if (certChainFile == null) {
129                 ctx = SSLContext.getInstance(PROTOCOL);
130                 if (trustManagerFactory == null) {
131                     ctx.init(null, null, null);
132                 } else {
133                     trustManagerFactory.init((KeyStore) null);
134                     ctx.init(null, trustManagerFactory.getTrustManagers(), null);
135                 }
136             } else {
137                 KeyStore ks = KeyStore.getInstance("JKS");
138                 ks.load(null, null);
139                 CertificateFactory cf = CertificateFactory.getInstance("X.509");
140 
141                 for (ChannelBuffer buf: PemReader.readCertificates(certChainFile)) {
142                     X509Certificate cert = (X509Certificate) cf.generateCertificate(new ChannelBufferInputStream(buf));
143                     X500Principal principal = cert.getSubjectX500Principal();
144                     ks.setCertificateEntry(principal.getName("RFC2253"), cert);
145                 }
146 
147                 // Set up trust manager factory to use our key store.
148                 if (trustManagerFactory == null) {
149                     trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
150                 }
151                 trustManagerFactory.init(ks);
152 
153                 // Initialize the SSLContext to work with the trust managers.
154                 ctx = SSLContext.getInstance(PROTOCOL);
155                 ctx.init(null, trustManagerFactory.getTrustManagers(), null);
156             }
157 
158             SSLSessionContext sessCtx = ctx.getClientSessionContext();
159             if (sessionCacheSize > 0) {
160                 sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
161             }
162             if (sessionTimeout > 0) {
163                 sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
164             }
165         } catch (Exception e) {
166             throw new SSLException("failed to initialize the server-side SSL context", e);
167         }
168     }
169 
170     @Override
171     public boolean isClient() {
172         return true;
173     }
174 
175     @Override
176     public List<String> nextProtocols() {
177         return nextProtocols;
178     }
179 
180     @Override
181     public SSLContext context() {
182         return ctx;
183     }
184 }