1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.crypto.Cipher;
23 import javax.crypto.EncryptedPrivateKeyInfo;
24 import javax.crypto.NoSuchPaddingException;
25 import javax.crypto.SecretKey;
26 import javax.crypto.SecretKeyFactory;
27 import javax.crypto.spec.PBEKeySpec;
28 import javax.net.ssl.KeyManagerFactory;
29 import javax.net.ssl.SSLContext;
30 import javax.net.ssl.SSLException;
31 import javax.net.ssl.SSLSessionContext;
32 import java.io.File;
33 import java.io.IOException;
34 import java.security.InvalidAlgorithmParameterException;
35 import java.security.InvalidKeyException;
36 import java.security.KeyFactory;
37 import java.security.KeyStore;
38 import java.security.NoSuchAlgorithmException;
39 import java.security.PrivateKey;
40 import java.security.Security;
41 import java.security.cert.Certificate;
42 import java.security.cert.CertificateFactory;
43 import java.security.spec.InvalidKeySpecException;
44 import java.security.spec.PKCS8EncodedKeySpec;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48
49
50
51
52 public final class JdkSslServerContext extends JdkSslContext {
53
54 private final SSLContext ctx;
55 private final List<String> nextProtocols;
56
57
58
59
60
61
62
63 public JdkSslServerContext(File certChainFile, File keyFile) throws SSLException {
64 this(certChainFile, keyFile, null);
65 }
66
67
68
69
70
71
72
73
74
75 public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
76 this(null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public JdkSslServerContext(
98 SslBufferPool bufPool,
99 File certChainFile, File keyFile, String keyPassword,
100 Iterable<String> ciphers, Iterable<String> nextProtocols,
101 long sessionCacheSize, long sessionTimeout) throws SSLException {
102
103 super(bufPool, ciphers);
104
105 if (certChainFile == null) {
106 throw new NullPointerException("certChainFile");
107 }
108 if (keyFile == null) {
109 throw new NullPointerException("keyFile");
110 }
111
112 if (keyPassword == null) {
113 keyPassword = "";
114 }
115
116 if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
117 if (!JettyNpnSslEngine.isAvailable()) {
118 throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
119 }
120
121 List<String> list = new ArrayList<String>();
122 for (String p: nextProtocols) {
123 if (p == null) {
124 break;
125 }
126 list.add(p);
127 }
128
129 this.nextProtocols = Collections.unmodifiableList(list);
130 } else {
131 this.nextProtocols = Collections.emptyList();
132 }
133
134 String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
135 if (algorithm == null) {
136 algorithm = "SunX509";
137 }
138
139 try {
140 KeyStore ks = KeyStore.getInstance("JKS");
141 ks.load(null, null);
142 CertificateFactory cf = CertificateFactory.getInstance("X.509");
143 KeyFactory rsaKF = KeyFactory.getInstance("RSA");
144 KeyFactory dsaKF = KeyFactory.getInstance("DSA");
145
146 ChannelBuffer encodedKeyBuf = PemReader.readPrivateKey(keyFile);
147 byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
148 encodedKeyBuf.readBytes(encodedKey);
149
150 char[] keyPasswordChars = keyPassword.toCharArray();
151 PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey);
152
153 PrivateKey key;
154 try {
155 key = rsaKF.generatePrivate(encodedKeySpec);
156 } catch (InvalidKeySpecException ignore) {
157 key = dsaKF.generatePrivate(encodedKeySpec);
158 }
159
160 List<Certificate> certChain = new ArrayList<Certificate>();
161 for (ChannelBuffer buf: PemReader.readCertificates(certChainFile)) {
162 certChain.add(cf.generateCertificate(new ChannelBufferInputStream(buf)));
163 }
164
165 ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[certChain.size()]));
166
167
168 KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
169 kmf.init(ks, keyPasswordChars);
170
171
172 ctx = SSLContext.getInstance(PROTOCOL);
173 ctx.init(kmf.getKeyManagers(), null, null);
174
175 SSLSessionContext sessCtx = ctx.getServerSessionContext();
176 if (sessionCacheSize > 0) {
177 sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
178 }
179 if (sessionTimeout > 0) {
180 sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
181 }
182 } catch (Exception e) {
183 throw new SSLException("failed to initialize the server-side SSL context", e);
184 }
185 }
186
187 @Override
188 public boolean isClient() {
189 return false;
190 }
191
192 @Override
193 public List<String> nextProtocols() {
194 return nextProtocols;
195 }
196
197 @Override
198 public SSLContext context() {
199 return ctx;
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 private static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
219 throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
220 InvalidKeyException, InvalidAlgorithmParameterException {
221
222 if (password == null || password.length == 0) {
223 return new PKCS8EncodedKeySpec(key);
224 }
225
226 EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
227 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
228 PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
229 SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
230
231 Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
232 cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
233
234 return encryptedPrivateKeyInfo.getKeySpec(cipher);
235 }
236 }