001/*
002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.9/src/java/org/apache/commons/ssl/SSL.java $
003 * $Revision: 121 $
004 * $Date: 2007-11-13 21:26:57 -0800 (Tue, 13 Nov 2007) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements.  See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership.  The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License.  You may obtain a copy of the License at
014 *
015 *   http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied.  See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation.  For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032package org.apache.commons.ssl;
033
034import javax.net.ssl.SSLContext;
035import javax.net.ssl.SSLServerSocket;
036import javax.net.ssl.SSLServerSocketFactory;
037import javax.net.ssl.SSLSocket;
038import javax.net.ssl.SSLSocketFactory;
039import java.io.File;
040import java.io.IOException;
041import java.net.InetAddress;
042import java.net.ServerSocket;
043import java.net.Socket;
044import java.net.UnknownHostException;
045import java.security.GeneralSecurityException;
046import java.security.KeyManagementException;
047import java.security.KeyStoreException;
048import java.security.NoSuchAlgorithmException;
049import java.security.cert.CertificateException;
050import java.security.cert.X509Certificate;
051import java.util.ArrayList;
052import java.util.Arrays;
053import java.util.Collection;
054import java.util.Collections;
055import java.util.HashSet;
056import java.util.Iterator;
057import java.util.LinkedList;
058import java.util.List;
059import java.util.Properties;
060import java.util.SortedSet;
061import java.util.TreeSet;
062
063/**
064 * Not thread-safe.  (But who would ever share this thing across multiple
065 * threads???)
066 *
067 * @author Credit Union Central of British Columbia
068 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
069 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
070 * @since May 1, 2006
071 */
072public class SSL {
073    private final static String[] KNOWN_PROTOCOLS =
074        {"TLSv1", "SSLv3", "SSLv2", "SSLv2Hello"};
075
076    // SUPPORTED_CIPHERS_ARRAY is initialized in the static constructor.
077    private final static String[] SUPPORTED_CIPHERS;
078
079    public final static SortedSet KNOWN_PROTOCOLS_SET;
080    public final static SortedSet SUPPORTED_CIPHERS_SET;
081
082    // RC4
083    public final static String SSL_RSA_WITH_RC4_128_SHA = "SSL_RSA_WITH_RC4_128_SHA";
084
085    // 3DES
086    public final static String SSL_RSA_WITH_3DES_EDE_CBC_SHA = "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
087    public final static String SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA = "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA";
088    public final static String SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA = "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA";
089
090    // AES-128
091    public final static String TLS_RSA_WITH_AES_128_CBC_SHA = "TLS_RSA_WITH_AES_128_CBC_SHA";
092    public final static String TLS_DHE_RSA_WITH_AES_128_CBC_SHA = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA";
093    public final static String TLS_DHE_DSS_WITH_AES_128_CBC_SHA = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA";
094
095    // AES-256
096    public final static String TLS_RSA_WITH_AES_256_CBC_SHA = "TLS_RSA_WITH_AES_256_CBC_SHA";
097    public final static String TLS_DHE_RSA_WITH_AES_256_CBC_SHA = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA";
098    public final static String TLS_DHE_DSS_WITH_AES_256_CBC_SHA = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA";
099
100    static {
101        TreeSet ts = new TreeSet(Collections.reverseOrder());
102        ts.addAll(Arrays.asList(KNOWN_PROTOCOLS));
103        KNOWN_PROTOCOLS_SET = Collections.unmodifiableSortedSet(ts);
104
105        // SSLSocketFactory.getDefault() sometimes blocks on FileInputStream
106        // reads of "/dev/random" (Linux only?).  You might find you system
107        // stuck here.  Move the mouse around a little!
108        SSLSocketFactory s = (SSLSocketFactory) SSLSocketFactory.getDefault();
109        ts = new TreeSet();
110        SUPPORTED_CIPHERS = s.getSupportedCipherSuites();
111        Arrays.sort(SUPPORTED_CIPHERS);
112        ts.addAll(Arrays.asList(SUPPORTED_CIPHERS));
113        SUPPORTED_CIPHERS_SET = Collections.unmodifiableSortedSet(ts);
114    }
115
116    private Object sslContext = null;
117    private int initCount = 0;
118    private SSLSocketFactory socketFactory = null;
119    private SSLServerSocketFactory serverSocketFactory = null;
120    private HostnameVerifier hostnameVerifier = HostnameVerifier.DEFAULT;
121    private boolean checkHostname = true;
122    private final ArrayList allowedNames = new ArrayList();
123    private boolean checkCRL = true;
124    private boolean checkExpiry = true;
125    private boolean useClientMode = false;
126    private boolean useClientModeDefault = true;
127    private int soTimeout = 24 * 60 * 60 * 1000; // default: one day
128    private int connectTimeout = 60 * 60 * 1000; // default: one hour
129    private TrustChain trustChain = null;
130    private KeyMaterial keyMaterial = null;
131    private String[] enabledCiphers = null;
132    private String[] enabledProtocols = null;
133    private String defaultProtocol = "TLS";
134    private X509Certificate[] currentServerChain;
135    private X509Certificate[] currentClientChain;
136    private boolean wantClientAuth = true;
137    private boolean needClientAuth = false;
138    private SSLWrapperFactory sslWrapperFactory = SSLWrapperFactory.NO_WRAP;
139
140    protected final boolean usingSystemProperties;
141
142    public SSL()
143        throws GeneralSecurityException, IOException {
144        boolean usingSysProps = false;
145        Properties props = System.getProperties();
146        boolean ksSet = props.containsKey("javax.net.ssl.keyStore");
147        boolean tsSet = props.containsKey("javax.net.ssl.trustStore");
148        if (ksSet) {
149            String path = System.getProperty("javax.net.ssl.keyStore");
150            String pwd = System.getProperty("javax.net.ssl.keyStorePassword");
151            pwd = pwd != null ? pwd : ""; // JSSE default is "".
152            File f = new File(path);
153            if (f.exists()) {
154                KeyMaterial km = new KeyMaterial(path, pwd.toCharArray());
155                setKeyMaterial(km);
156                usingSysProps = true;
157            }
158        }
159        boolean trustMaterialSet = false;
160        if (tsSet) {
161            String path = System.getProperty("javax.net.ssl.trustStore");
162            String pwd = System.getProperty("javax.net.ssl.trustStorePassword");
163            boolean pwdWasNull = pwd == null;
164            pwd = pwdWasNull ? "" : pwd; // JSSE default is "".
165            File f = new File(path);
166            if (f.exists()) {
167                TrustMaterial tm;
168                try {
169                    tm = new TrustMaterial(path, pwd.toCharArray());
170                }
171                catch (GeneralSecurityException gse) {
172                    // Probably a bad password.  If we're using the default password,
173                    // let's try and survive this setback.
174                    if (pwdWasNull) {
175                        tm = new TrustMaterial(path);
176                    } else {
177                        throw gse;
178                    }
179                }
180
181                setTrustMaterial(tm);
182                usingSysProps = true;
183                trustMaterialSet = true;
184            }
185        }
186
187        /*
188            No default trust material was set.  We'll use the JSSE standard way
189            where we test for "JSSE_CACERTS" first, and then fall back on
190            "CACERTS".  We could just leave TrustMaterial null, but then our
191            setCheckCRL() and setCheckExpiry() features won't work.  We need a
192            non-null TrustMaterial object in order to intercept and decorate
193            the JVM's default TrustManager.
194          */
195        if (!trustMaterialSet) {
196            setTrustMaterial(TrustMaterial.DEFAULT);
197        }
198        this.usingSystemProperties = usingSysProps;
199
200        // By default we only use the strong ciphers (128 bit and higher).
201        // Consumers can call "useDefaultJavaCiphers()" to get the 40 and 56 bit
202        // ciphers back that Java normally has turned on.
203        useStrongCiphers();
204        dirtyAndReloadIfYoung();
205    }
206
207    private void dirty() {
208        this.sslContext = null;
209        this.socketFactory = null;
210        this.serverSocketFactory = null;
211    }
212
213    private void dirtyAndReloadIfYoung()
214        throws NoSuchAlgorithmException, KeyStoreException,
215        KeyManagementException, IOException, CertificateException {
216        dirty();
217        if (initCount >= 0 && initCount <= 5) {
218            // The first five init's we do early (before any sockets are
219            // created) in the hope that will trigger any explosions nice
220            // and early, with the correct exception type.
221
222            // After the first five init's, we revert to a regular
223            // dirty / init pattern, and the "init" happens very late:
224            // just before the socket is created.  If badness happens, a
225            // wrapping RuntimeException will be thrown.
226            init();
227        }
228    }
229
230    public SSLContext getSSLContext()
231        throws GeneralSecurityException, IOException
232
233    {
234        Object obj = getSSLContextAsObject();
235        if (JavaImpl.isJava13()) {
236            try {
237                return (SSLContext) obj;
238            }
239            catch (ClassCastException cce) {
240                throw new ClassCastException("When using Java13 SSL, you must call SSL.getSSLContextAsObject() - " + cce);
241            }
242        }
243        return (SSLContext) obj;
244    }
245
246    /**
247     * @return com.sun.net.ssl.SSLContext or javax.net.ssl.SSLContext depending
248     *         on the JSSE implementation we're using.
249     * @throws GeneralSecurityException problem creating SSLContext
250     * @throws IOException              problem creating SSLContext
251     */
252    public Object getSSLContextAsObject()
253        throws GeneralSecurityException, IOException
254
255    {
256        if (sslContext == null) {
257            init();
258        }
259        return sslContext;
260    }
261
262    public void addTrustMaterial(TrustChain trustChain)
263        throws NoSuchAlgorithmException, KeyStoreException,
264        KeyManagementException, IOException, CertificateException {
265        if (this.trustChain == null || trustChain == TrustMaterial.TRUST_ALL) {
266            this.trustChain = trustChain;
267        } else {
268            this.trustChain.addTrustMaterial(trustChain);
269        }
270        dirtyAndReloadIfYoung();
271    }
272
273    public void setTrustMaterial(TrustChain trustChain)
274        throws NoSuchAlgorithmException, KeyStoreException,
275        KeyManagementException, IOException, CertificateException {
276        this.trustChain = trustChain;
277        dirtyAndReloadIfYoung();
278    }
279
280    public void setKeyMaterial(KeyMaterial keyMaterial)
281        throws NoSuchAlgorithmException, KeyStoreException,
282        KeyManagementException, IOException, CertificateException {
283        this.keyMaterial = keyMaterial;
284        dirtyAndReloadIfYoung();
285    }
286
287    public X509Certificate[] getAssociatedCertificateChain() {
288        if (keyMaterial != null) {
289            return keyMaterial.getAssociatedCertificateChain();
290        } else {
291            return null;
292        }
293    }
294
295    public String[] getEnabledCiphers() {
296        return enabledCiphers != null ? enabledCiphers : getDefaultCipherSuites();
297    }
298
299    public void useDefaultJavaCiphers() {
300        this.enabledCiphers = null;
301    }
302
303    public void useStrongCiphers() {
304        LinkedList list = new LinkedList();
305        addCipher(list, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, false);
306        addCipher(list, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, false);
307        addCipher(list, SSL_RSA_WITH_3DES_EDE_CBC_SHA, false);
308        addCipher(list, SSL_RSA_WITH_RC4_128_SHA, false);
309        addCipher(list, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, false);
310        addCipher(list, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, false);
311        addCipher(list, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, false);
312        addCipher(list, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, false);
313        addCipher(list, TLS_RSA_WITH_AES_128_CBC_SHA, false);
314        addCipher(list, TLS_RSA_WITH_AES_256_CBC_SHA, false);
315        String[] strongCiphers = new String[list.size()];
316        list.toArray(strongCiphers);
317        String[] currentCiphers = getEnabledCiphers();
318        // Current ciphers must be default or something.  Odd that it's null,
319        // though.
320        if (currentCiphers == null) {
321            setEnabledCiphers(strongCiphers);
322        }
323
324        Arrays.sort(strongCiphers);
325        Arrays.sort(currentCiphers);
326        // Let's only call "setEnabledCiphers" if our array is actually different
327        // than what's already set.
328        if (!Arrays.equals(strongCiphers, currentCiphers)) {
329            setEnabledCiphers(strongCiphers);
330        }
331    }
332
333    public void setEnabledCiphers(String[] ciphers) {
334        HashSet desired = new HashSet(Arrays.asList(ciphers));
335        desired.removeAll(SUPPORTED_CIPHERS_SET);
336        if (!desired.isEmpty()) {
337            throw new IllegalArgumentException("following ciphers not supported: " + desired);
338        }
339        this.enabledCiphers = ciphers;
340    }
341
342    public String[] getEnabledProtocols() {
343        return enabledProtocols != null ? enabledProtocols : KNOWN_PROTOCOLS;
344    }
345
346    public void setEnabledProtocols(String[] protocols) {
347        HashSet desired = new HashSet(Arrays.asList(protocols));
348        desired.removeAll(KNOWN_PROTOCOLS_SET);
349        if (!desired.isEmpty()) {
350            throw new IllegalArgumentException("following protocols not supported: " + desired);
351        }
352        this.enabledProtocols = protocols;
353    }
354
355    public String getDefaultProtocol() {
356        return defaultProtocol;
357    }
358
359    public void setDefaultProtocol(String protocol) {
360        this.defaultProtocol = protocol;
361        dirty();
362    }
363
364    public boolean getCheckHostname() {
365        return checkHostname;
366    }
367
368    /**
369     * @return String[] array of alternate "allowed names" to try against a
370     *         server's x509 CN field if the host/ip we used didn't match.
371     *         Returns an empty list if there are no "allowedNames" currently
372     *         set.
373     */
374    public List getAllowedNames() {
375        return Collections.unmodifiableList(allowedNames);
376    }
377
378    /**
379     * Offers a secure way to use virtual-hosting and SSL in some situations:
380     * for example you want to connect to "bar.com" but you know in advance
381     * that the SSL Certificate on that server only contains "CN=foo.com".  If
382     * you setAllowedNames( new String[] { "foo.com" } ) on your SSLClient in
383     * advance, you can connect securely, while still using "bar.com" as the
384     * host.
385     * <p/>
386     * Here's a code example using "cucbc.com" to connect, but anticipating
387     * "www.cucbc.com" in the server's certificate:
388     * <pre>
389     * SSLClient client = new SSLClient();
390     * client.setAllowedNames( new String[] { "www.cucbc.com" } );
391     * Socket s = client.createSocket( "cucbc.com", 443 );
392     * </pre>
393     * <p/>
394     * This technique is also useful if you don't want to use DNS, and want to
395     * connect using the IP address.
396     *
397     * @param allowedNames Collection of alternate "allowed names" to try against
398     *                     a server's x509 CN field if the host/ip we used didn't
399     *                     match.  Set to null to force strict matching against
400     *                     host/ip passed into createSocket().  Null is the
401     *                     default value.  Must be set in advance, before
402     *                     createSocket() is called.
403     */
404    public void addAllowedNames(Collection allowedNames) {
405        this.allowedNames.addAll(allowedNames);
406    }
407
408    public void addAllowedName(String allowedName) {
409        this.allowedNames.add(allowedName);
410    }
411
412    public void clearAllowedNames() {
413        this.allowedNames.clear();
414    }
415
416    public void setCheckHostname(boolean checkHostname) {
417        this.checkHostname = checkHostname;
418    }
419
420    public void setHostnameVerifier(HostnameVerifier verifier) {
421        if (verifier == null) {
422            verifier = HostnameVerifier.DEFAULT;
423        }
424        this.hostnameVerifier = verifier;
425    }
426
427    public HostnameVerifier getHostnameVerifier() {
428        return hostnameVerifier;
429    }
430
431    public boolean getCheckCRL() {
432        return checkCRL;
433    }
434
435    public void setCheckCRL(boolean checkCRL) {
436        this.checkCRL = checkCRL;
437    }
438
439    public boolean getCheckExpiry() {
440        return checkExpiry;
441    }
442
443    public void setCheckExpiry(boolean checkExpiry) {
444        this.checkExpiry = checkExpiry;
445    }
446
447    public void setSoTimeout(int soTimeout) {
448        if (soTimeout < 0) {
449            throw new IllegalArgumentException("soTimeout must not be negative");
450        }
451        this.soTimeout = soTimeout;
452    }
453
454    public int getSoTimeout() {
455        return soTimeout;
456    }
457
458    public void setConnectTimeout(int connectTimeout) {
459        if (connectTimeout < 0) {
460            throw new IllegalArgumentException("connectTimeout must not be negative");
461        }
462        this.connectTimeout = connectTimeout;
463    }
464
465    public void setUseClientMode(boolean useClientMode) {
466        this.useClientModeDefault = false;
467        this.useClientMode = useClientMode;
468    }
469
470    public boolean getUseClientModeDefault() {
471        return useClientModeDefault;
472    }
473
474    public boolean getUseClientMode() {
475        return useClientMode;
476    }
477
478    public void setWantClientAuth(boolean wantClientAuth) {
479        this.wantClientAuth = wantClientAuth;
480    }
481
482    public void setNeedClientAuth(boolean needClientAuth) {
483        this.needClientAuth = needClientAuth;
484    }
485
486    public boolean getWantClientAuth() {
487        return wantClientAuth;
488    }
489
490    public boolean getNeedClientAuth() {
491        return needClientAuth;
492    }
493
494    public SSLWrapperFactory getSSLWrapperFactory() {
495        return this.sslWrapperFactory;
496    }
497
498    public void setSSLWrapperFactory(SSLWrapperFactory wf) {
499        this.sslWrapperFactory = wf;
500    }
501
502    private void initThrowRuntime() {
503        try {
504            init();
505        }
506        catch (GeneralSecurityException gse) {
507            throw JavaImpl.newRuntimeException(gse);
508        }
509        catch (IOException ioe) {
510            throw JavaImpl.newRuntimeException(ioe);
511        }
512    }
513
514    private void init()
515        throws NoSuchAlgorithmException, KeyStoreException,
516        KeyManagementException, IOException, CertificateException {
517        socketFactory = null;
518        serverSocketFactory = null;
519        this.sslContext = JavaImpl.init(this, trustChain, keyMaterial);
520        initCount++;
521    }
522
523    public void doPreConnectSocketStuff(SSLSocket s)
524        throws IOException {
525        if (!useClientModeDefault) {
526            s.setUseClientMode(useClientMode);
527        }
528        if (soTimeout > 0) {
529            s.setSoTimeout(soTimeout);
530        }
531        if (enabledProtocols != null) {
532            JavaImpl.setEnabledProtocols(s, enabledProtocols);
533        }
534        if (enabledCiphers != null) {
535            s.setEnabledCipherSuites(enabledCiphers);
536        }
537    }
538
539    public void doPostConnectSocketStuff(SSLSocket s, String host)
540        throws IOException {
541        if (checkHostname) {
542            int size = allowedNames.size() + 1;
543            String[] hosts = new String[size];
544            // hosts[ 0 ] MUST ALWAYS be the host given to createSocket().
545            hosts[0] = host;
546            int i = 1;
547            for (Iterator it = allowedNames.iterator(); it.hasNext(); i++) {
548                hosts[i] = (String) it.next();
549            }
550            hostnameVerifier.check(hosts, s);
551        }
552    }
553
554    public SSLSocket createSocket() throws IOException {
555        return sslWrapperFactory.wrap(JavaImpl.createSocket(this));
556    }
557
558    /**
559     * Attempts to get a new socket connection to the given host within the
560     * given time limit.
561     *
562     * @param remoteHost the host name/IP
563     * @param remotePort the port on the host
564     * @param localHost  the local host name/IP to bind the socket to
565     * @param localPort  the port on the local machine
566     * @param timeout    the connection timeout (0==infinite)
567     * @return Socket a new socket
568     * @throws IOException          if an I/O error occurs while creating the socket
569     * @throws UnknownHostException if the IP address of the host cannot be
570     *                              determined
571     */
572    public Socket createSocket(String remoteHost, int remotePort,
573                               InetAddress localHost, int localPort,
574                               int timeout)
575        throws IOException {
576        // Only use our factory-wide connectTimeout if this method was passed
577        // in a timeout of 0 (infinite).
578        int factoryTimeout = getConnectTimeout();
579        int connectTimeout = timeout == 0 ? factoryTimeout : timeout;
580        SSLSocket s = JavaImpl.createSocket(this, remoteHost, remotePort,
581            localHost, localPort,
582            connectTimeout);
583        return sslWrapperFactory.wrap(s);
584    }
585
586    public Socket createSocket(Socket s, String remoteHost, int remotePort,
587                               boolean autoClose)
588        throws IOException {
589        SSLSocketFactory sf = getSSLSocketFactory();
590        s = sf.createSocket(s, remoteHost, remotePort, autoClose);
591        doPreConnectSocketStuff((SSLSocket) s);
592        doPostConnectSocketStuff((SSLSocket) s, remoteHost);
593        return sslWrapperFactory.wrap((SSLSocket) s);
594    }
595
596    public ServerSocket createServerSocket() throws IOException {
597        SSLServerSocket ss = JavaImpl.createServerSocket(this);
598        return getSSLWrapperFactory().wrap(ss, this);
599    }
600
601    /**
602     * Attempts to get a new socket connection to the given host within the
603     * given time limit.
604     *
605     * @param localHost the local host name/IP to bind against (null == ANY)
606     * @param port      the port to listen on
607     * @param backlog   number of connections allowed to queue up for accept().
608     * @return SSLServerSocket a new server socket
609     * @throws IOException if an I/O error occurs while creating thesocket
610     */
611    public ServerSocket createServerSocket(int port, int backlog,
612                                           InetAddress localHost)
613        throws IOException {
614        SSLServerSocketFactory f = getSSLServerSocketFactory();
615        ServerSocket ss = f.createServerSocket(port, backlog, localHost);
616        SSLServerSocket s = (SSLServerSocket) ss;
617        doPreConnectServerSocketStuff(s);
618        return getSSLWrapperFactory().wrap(s, this);
619    }
620
621    public void doPreConnectServerSocketStuff(SSLServerSocket s)
622        throws IOException {
623        if (soTimeout > 0) {
624            s.setSoTimeout(soTimeout);
625        }
626        if (enabledProtocols != null) {
627            JavaImpl.setEnabledProtocols(s, enabledProtocols);
628        }
629        if (enabledCiphers != null) {
630            s.setEnabledCipherSuites(enabledCiphers);
631        }
632
633        /*
634          setNeedClientAuth( false ) has an annoying side effect:  it seems to
635          reset setWantClient( true ) back to to false.  So I do things this
636          way to make sure setting things "true" happens after setting things
637          "false" - giving "true" priority.
638          */
639        if (!wantClientAuth) {
640            JavaImpl.setWantClientAuth(s, wantClientAuth);
641        }
642        if (!needClientAuth) {
643            s.setNeedClientAuth(needClientAuth);
644        }
645        if (wantClientAuth) {
646            JavaImpl.setWantClientAuth(s, wantClientAuth);
647        }
648        if (needClientAuth) {
649            s.setNeedClientAuth(needClientAuth);
650        }
651    }
652
653    public SSLSocketFactory getSSLSocketFactory() {
654        if (sslContext == null) {
655            initThrowRuntime();
656        }
657        if (socketFactory == null) {
658            socketFactory = JavaImpl.getSSLSocketFactory(sslContext);
659        }
660        return socketFactory;
661    }
662
663    public SSLServerSocketFactory getSSLServerSocketFactory() {
664        if (sslContext == null) {
665            initThrowRuntime();
666        }
667        if (serverSocketFactory == null) {
668            serverSocketFactory = JavaImpl.getSSLServerSocketFactory(sslContext);
669        }
670        return serverSocketFactory;
671    }
672
673    public int getConnectTimeout() {
674        return connectTimeout;
675    }
676
677    public String[] getDefaultCipherSuites() {
678        return getSSLSocketFactory().getDefaultCipherSuites();
679    }
680
681    public String[] getSupportedCipherSuites() {
682        String[] s = new String[SUPPORTED_CIPHERS.length];
683        System.arraycopy(SUPPORTED_CIPHERS, 0, s, 0, s.length);
684        return s;
685    }
686
687    public TrustChain getTrustChain() {
688        return trustChain;
689    }
690
691    public void setCurrentServerChain(X509Certificate[] chain) {
692        this.currentServerChain = chain;
693    }
694
695    public void setCurrentClientChain(X509Certificate[] chain) {
696        this.currentClientChain = chain;
697    }
698
699    public X509Certificate[] getCurrentServerChain() {
700        return currentServerChain;
701    }
702
703    public X509Certificate[] getCurrentClientChain() {
704        return currentClientChain;
705    }
706
707    public static void main(String[] args) {
708        for (int i = 0; i < SUPPORTED_CIPHERS.length; i++) {
709            System.out.println(SUPPORTED_CIPHERS[i]);
710        }
711        System.out.println();
712        System.out.println("----------------------------------------------");
713        addCipher(null, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, true);
714        addCipher(null, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, true);
715        addCipher(null, SSL_RSA_WITH_3DES_EDE_CBC_SHA, true);
716        addCipher(null, SSL_RSA_WITH_RC4_128_SHA, true);
717        addCipher(null, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, true);
718        addCipher(null, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, true);
719        addCipher(null, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, true);
720        addCipher(null, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, true);
721        addCipher(null, TLS_RSA_WITH_AES_128_CBC_SHA, true);
722        addCipher(null, TLS_RSA_WITH_AES_256_CBC_SHA, true);
723    }
724
725    private static void addCipher(List l, String c, boolean printOnStandardOut) {
726        boolean supported = false;
727        if (c != null && SUPPORTED_CIPHERS_SET.contains(c)) {
728            if (l != null) {
729                l.add(c);
730            }
731            supported = true;
732        }
733        if (printOnStandardOut) {
734            System.out.println(c + ":\t" + supported);
735        }
736    }
737
738
739}