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 }