1 package org.mortbay.jetty.security;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.nio.ByteBuffer;
8 import java.nio.channels.SelectionKey;
9 import java.nio.channels.SocketChannel;
10 import java.security.KeyStore;
11 import java.security.SecureRandom;
12 import java.security.Security;
13 import java.security.cert.X509Certificate;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.List;
17 import java.util.concurrent.ConcurrentLinkedQueue;
18
19 import javax.net.ssl.KeyManager;
20 import javax.net.ssl.KeyManagerFactory;
21 import javax.net.ssl.SSLContext;
22 import javax.net.ssl.SSLEngine;
23 import javax.net.ssl.SSLException;
24 import javax.net.ssl.SSLPeerUnverifiedException;
25 import javax.net.ssl.SSLSession;
26 import javax.net.ssl.SSLSocket;
27 import javax.net.ssl.TrustManager;
28 import javax.net.ssl.TrustManagerFactory;
29
30 import org.mortbay.io.Buffer;
31 import org.mortbay.io.Connection;
32 import org.mortbay.io.EndPoint;
33 import org.mortbay.io.bio.SocketEndPoint;
34 import org.mortbay.io.nio.NIOBuffer;
35 import org.mortbay.io.nio.SelectChannelEndPoint;
36 import org.mortbay.io.nio.SelectorManager.SelectSet;
37 import org.mortbay.jetty.HttpConnection;
38 import org.mortbay.jetty.HttpParser;
39 import org.mortbay.jetty.HttpSchemes;
40 import org.mortbay.jetty.Request;
41 import org.mortbay.jetty.nio.SelectChannelConnector;
42 import org.mortbay.log.Log;
43 import org.mortbay.resource.Resource;
44
45
46
47
48
49
50
51
52 public class SslSelectChannelConnector extends SelectChannelConnector
53 {
54
55
56
57
58 static final String CACHED_INFO_ATTR=CachedInfo.class.getName();
59
60
61 public static final String DEFAULT_KEYSTORE=System.getProperty("user.home")+File.separator+".keystore";
62
63
64 public static final String KEYPASSWORD_PROPERTY="jetty.ssl.keypassword";
65
66
67 public static final String PASSWORD_PROPERTY="jetty.ssl.password";
68
69
70 private String _excludeCipherSuites[]=null;
71
72
73 private String _keystore=DEFAULT_KEYSTORE;
74 private String _keystoreType="JKS";
75
76
77 private boolean _needClientAuth=false;
78 private boolean _wantClientAuth=false;
79
80 private transient Password _password;
81 private transient Password _keyPassword;
82 private transient Password _trustPassword;
83 private String _protocol="TLS";
84 private String _algorithm="SunX509";
85 private String _provider;
86 private String _secureRandomAlgorithm;
87 private String _sslKeyManagerFactoryAlgorithm=(Security.getProperty("ssl.KeyManagerFactory.algorithm")==null?"SunX509":Security
88 .getProperty("ssl.KeyManagerFactory.algorithm"));
89
90
91 private String _sslTrustManagerFactoryAlgorithm=(Security.getProperty("ssl.TrustManagerFactory.algorithm")==null?"SunX509":Security
92 .getProperty("ssl.TrustManagerFactory.algorithm"));
93
94
95 private String _truststore;
96 private String _truststoreType="JKS";
97 private SSLContext _context;
98
99 private int _packetBufferSize;
100 private int _applicationBufferSize;
101 private ConcurrentLinkedQueue<Buffer> _packetBuffers = new ConcurrentLinkedQueue<Buffer>();
102 private ConcurrentLinkedQueue<Buffer> _applicationBuffers = new ConcurrentLinkedQueue<Buffer>();
103
104
105
106
107
108 public Buffer getBuffer(int size)
109 {
110
111 Buffer buffer;
112 if (size==_applicationBufferSize)
113 {
114 buffer = _applicationBuffers.poll();
115 if (buffer==null)
116 buffer=new NIOBuffer(size,false);
117 }
118 else if (size==_packetBufferSize)
119 {
120 buffer = _packetBuffers.poll();
121 if (buffer==null)
122 buffer=new NIOBuffer(size,getUseDirectBuffers());
123 }
124 else
125 buffer=super.getBuffer(size);
126
127 return buffer;
128 }
129
130
131
132
133
134 public void returnBuffer(Buffer buffer)
135 {
136 buffer.clear();
137 int size=buffer.capacity();
138 ByteBuffer bbuf = ((NIOBuffer)buffer).getByteBuffer();
139 bbuf.position(0);
140 bbuf.limit(size);
141
142 if (size==_applicationBufferSize)
143 _applicationBuffers.add(buffer);
144 else if (size==_packetBufferSize)
145 _packetBuffers.add(buffer);
146 else
147 super.returnBuffer(buffer);
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 private static X509Certificate[] getCertChain(SSLSession sslSession)
167 {
168 try
169 {
170 javax.security.cert.X509Certificate javaxCerts[]=sslSession.getPeerCertificateChain();
171 if (javaxCerts==null||javaxCerts.length==0)
172 return null;
173
174 int length=javaxCerts.length;
175 X509Certificate[] javaCerts=new X509Certificate[length];
176
177 java.security.cert.CertificateFactory cf=java.security.cert.CertificateFactory.getInstance("X.509");
178 for (int i=0; i<length; i++)
179 {
180 byte bytes[]=javaxCerts[i].getEncoded();
181 ByteArrayInputStream stream=new ByteArrayInputStream(bytes);
182 javaCerts[i]=(X509Certificate)cf.generateCertificate(stream);
183 }
184
185 return javaCerts;
186 }
187 catch (SSLPeerUnverifiedException pue)
188 {
189 return null;
190 }
191 catch (Exception e)
192 {
193 Log.warn(Log.EXCEPTION,e);
194 return null;
195 }
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 @Override
223 public void customize(EndPoint endpoint, Request request) throws IOException
224 {
225 super.customize(endpoint,request);
226 request.setScheme(HttpSchemes.HTTPS);
227
228 SslHttpChannelEndPoint sslHttpChannelEndpoint=(SslHttpChannelEndPoint)endpoint;
229 SSLEngine sslEngine=sslHttpChannelEndpoint.getSSLEngine();
230
231 try
232 {
233 SSLSession sslSession=sslEngine.getSession();
234 String cipherSuite=sslSession.getCipherSuite();
235 Integer keySize;
236 X509Certificate[] certs;
237
238 CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR);
239 if (cachedInfo!=null)
240 {
241 keySize=cachedInfo.getKeySize();
242 certs=cachedInfo.getCerts();
243 }
244 else
245 {
246 keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite));
247 certs=getCertChain(sslSession);
248 cachedInfo=new CachedInfo(keySize,certs);
249 sslSession.putValue(CACHED_INFO_ATTR,cachedInfo);
250 }
251
252 if (certs!=null)
253 request.setAttribute("javax.servlet.request.X509Certificate",certs);
254
255 request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
256 request.setAttribute("javax.servlet.request.key_size",keySize);
257 }
258 catch (Exception e)
259 {
260 Log.warn(Log.EXCEPTION,e);
261 }
262 }
263
264
265 public SslSelectChannelConnector()
266 {
267
268
269
270
271
272 }
273
274
275
276
277
278
279 public String[] getCipherSuites()
280 {
281 return getExcludeCipherSuites();
282 }
283
284 public String[] getExcludeCipherSuites()
285 {
286 return _excludeCipherSuites;
287 }
288
289
290
291
292
293
294
295 public void setCipherSuites(String[] cipherSuites)
296 {
297 setExcludeCipherSuites(cipherSuites);
298 }
299
300 public void setExcludeCipherSuites(String[] cipherSuites)
301 {
302 this._excludeCipherSuites=cipherSuites;
303 }
304
305
306 public void setPassword(String password)
307 {
308 _password=Password.getPassword(PASSWORD_PROPERTY,password,null);
309 }
310
311
312 public void setTrustPassword(String password)
313 {
314 _trustPassword=Password.getPassword(PASSWORD_PROPERTY,password,null);
315 }
316
317
318 public void setKeyPassword(String password)
319 {
320 _keyPassword=Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
321 }
322
323
324 public String getAlgorithm()
325 {
326 return (this._algorithm);
327 }
328
329
330 public void setAlgorithm(String algorithm)
331 {
332 this._algorithm=algorithm;
333 }
334
335
336 public String getProtocol()
337 {
338 return _protocol;
339 }
340
341
342 public void setProtocol(String protocol)
343 {
344 _protocol=protocol;
345 }
346
347
348 public void setKeystore(String keystore)
349 {
350 _keystore=keystore;
351 }
352
353
354 public String getKeystore()
355 {
356 return _keystore;
357 }
358
359
360 public String getKeystoreType()
361 {
362 return (_keystoreType);
363 }
364
365
366 public boolean getNeedClientAuth()
367 {
368 return _needClientAuth;
369 }
370
371
372 public boolean getWantClientAuth()
373 {
374 return _wantClientAuth;
375 }
376
377
378
379
380
381
382
383
384 public void setNeedClientAuth(boolean needClientAuth)
385 {
386 _needClientAuth=needClientAuth;
387 }
388
389 public void setWantClientAuth(boolean wantClientAuth)
390 {
391 _wantClientAuth=wantClientAuth;
392 }
393
394
395 public void setKeystoreType(String keystoreType)
396 {
397 _keystoreType=keystoreType;
398 }
399
400
401 public String getProvider()
402 {
403 return _provider;
404 }
405
406 public String getSecureRandomAlgorithm()
407 {
408 return (this._secureRandomAlgorithm);
409 }
410
411
412 public String getSslKeyManagerFactoryAlgorithm()
413 {
414 return (this._sslKeyManagerFactoryAlgorithm);
415 }
416
417
418 public String getSslTrustManagerFactoryAlgorithm()
419 {
420 return (this._sslTrustManagerFactoryAlgorithm);
421 }
422
423
424 public String getTruststore()
425 {
426 return _truststore;
427 }
428
429
430 public String getTruststoreType()
431 {
432 return _truststoreType;
433 }
434
435
436 public void setProvider(String _provider)
437 {
438 this._provider=_provider;
439 }
440
441
442 public void setSecureRandomAlgorithm(String algorithm)
443 {
444 this._secureRandomAlgorithm=algorithm;
445 }
446
447
448 public void setSslKeyManagerFactoryAlgorithm(String algorithm)
449 {
450 this._sslKeyManagerFactoryAlgorithm=algorithm;
451 }
452
453
454 public void setSslTrustManagerFactoryAlgorithm(String algorithm)
455 {
456 this._sslTrustManagerFactoryAlgorithm=algorithm;
457 }
458
459 public void setTruststore(String truststore)
460 {
461 _truststore=truststore;
462 }
463
464 public void setTruststoreType(String truststoreType)
465 {
466 _truststoreType=truststoreType;
467 }
468
469
470
471
472
473
474
475
476
477
478 public boolean isConfidential(Request request)
479 {
480 final int confidentialPort=getConfidentialPort();
481 return confidentialPort==0||confidentialPort==request.getServerPort();
482 }
483
484
485
486
487
488
489
490
491
492
493 public boolean isIntegral(Request request)
494 {
495 final int integralPort=getIntegralPort();
496 return integralPort==0||integralPort==request.getServerPort();
497 }
498
499
500 protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
501 {
502 return new SslHttpChannelEndPoint(this,channel,selectSet,key,createSSLEngine())
503 {
504
505 public boolean isReadyForDispatch()
506 {
507 return super.isReadyForDispatch() && !((HttpConnection)getConnection()).getRequest().isSuspended();
508 }
509 };
510 }
511
512
513 protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
514 {
515 HttpConnection connection=(HttpConnection)super.newConnection(channel,endpoint);
516 ((HttpParser)connection.getParser()).setForceContentBuffer(true);
517 return connection;
518 }
519
520
521 protected SSLEngine createSSLEngine() throws IOException
522 {
523 SSLEngine engine=null;
524 try
525 {
526 engine=_context.createSSLEngine();
527
528 if (_wantClientAuth)
529 engine.setWantClientAuth(_wantClientAuth);
530 if (_needClientAuth)
531 engine.setNeedClientAuth(_needClientAuth);
532
533 if (_excludeCipherSuites!=null&&_excludeCipherSuites.length>0)
534 {
535 List<String> excludedCSList=Arrays.asList(_excludeCipherSuites);
536 String[] enabledCipherSuites=engine.getEnabledCipherSuites();
537 List<String> enabledCSList=new ArrayList<String>(Arrays.asList(enabledCipherSuites));
538
539 for (String cipherName : excludedCSList)
540 {
541 if (enabledCSList.contains(cipherName))
542 {
543 enabledCSList.remove(cipherName);
544 }
545 }
546 enabledCipherSuites=enabledCSList.toArray(new String[enabledCSList.size()]);
547
548 engine.setEnabledCipherSuites(enabledCipherSuites);
549 }
550
551 }
552 catch (Exception e)
553 {
554 Log.warn("Error creating sslEngine -- closing this connector",e);
555 close();
556 throw new IllegalStateException(e);
557 }
558 return engine;
559 }
560
561
562 protected void doStart() throws Exception
563 {
564 _context=createSSLContext();
565
566 SSLEngine engine=createSSLEngine();
567 SSLSession ssl_session=engine.getSession();
568
569 setHeaderBufferSize(ssl_session.getApplicationBufferSize());
570 setRequestBufferSize(ssl_session.getApplicationBufferSize());
571 setResponseBufferSize(ssl_session.getApplicationBufferSize());
572
573 super.doStart();
574 }
575
576 protected SSLContext createSSLContext() throws Exception
577 {
578 if (_truststore==null)
579 {
580 _truststore=_keystore;
581 _truststoreType=_keystoreType;
582 }
583
584 KeyManager[] keyManagers=null;
585 InputStream keystoreInputStream = null;
586 if (_keystore!=null)
587 keystoreInputStream=Resource.newResource(_keystore).getInputStream();
588 KeyStore keyStore=KeyStore.getInstance(_keystoreType);
589 keyStore.load(keystoreInputStream,_password==null?null:_password.toString().toCharArray());
590
591 KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm);
592 keyManagerFactory.init(keyStore,_keyPassword==null?null:_keyPassword.toString().toCharArray());
593 keyManagers=keyManagerFactory.getKeyManagers();
594
595
596 TrustManager[] trustManagers=null;
597 InputStream truststoreInputStream = null;
598 if (_truststore!=null)
599 truststoreInputStream = Resource.newResource(_truststore).getInputStream();
600 KeyStore trustStore=KeyStore.getInstance(_truststoreType);
601 trustStore.load(truststoreInputStream,_trustPassword==null?null:_trustPassword.toString().toCharArray());
602
603 TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
604 trustManagerFactory.init(trustStore);
605 trustManagers=trustManagerFactory.getTrustManagers();
606
607 SecureRandom secureRandom=_secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
608 SSLContext context=_provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol,_provider);
609 context.init(keyManagers,trustManagers,secureRandom);
610 return context;
611 }
612
613
614
615
616
617 private class CachedInfo
618 {
619 private X509Certificate[] _certs;
620 private Integer _keySize;
621
622 CachedInfo(Integer keySize, X509Certificate[] certs)
623 {
624 this._keySize=keySize;
625 this._certs=certs;
626 }
627
628 X509Certificate[] getCerts()
629 {
630 return _certs;
631 }
632
633 Integer getKeySize()
634 {
635 return _keySize;
636 }
637 }
638
639 }