SSLSessionImpl.java revision 12072:6721ff11d592
1/*
2 * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27package sun.security.ssl;
28
29import java.net.*;
30import java.util.Enumeration;
31import java.util.Hashtable;
32import java.util.Vector;
33import java.util.Collection;
34import java.util.Collections;
35import java.util.List;
36import java.util.ArrayList;
37
38import java.security.Principal;
39import java.security.PrivateKey;
40import java.security.SecureRandom;
41import java.security.cert.X509Certificate;
42import java.security.cert.CertificateEncodingException;
43
44import javax.crypto.SecretKey;
45
46import javax.net.ssl.SSLSessionContext;
47import javax.net.ssl.SSLSessionBindingListener;
48import javax.net.ssl.SSLSessionBindingEvent;
49import javax.net.ssl.SSLPeerUnverifiedException;
50import javax.net.ssl.SSLPermission;
51import javax.net.ssl.ExtendedSSLSession;
52import javax.net.ssl.SNIServerName;
53
54import static sun.security.ssl.CipherSuite.KeyExchange.*;
55
56/**
57 * Implements the SSL session interface, and exposes the session context
58 * which is maintained by SSL servers.
59 *
60 * <P> Servers have the ability to manage the sessions associated with
61 * their authentication context(s).  They can do this by enumerating the
62 * IDs of the sessions which are cached, examining those sessions, and then
63 * perhaps invalidating a given session so that it can't be used again.
64 * If servers do not explicitly manage the cache, sessions will linger
65 * until memory is low enough that the runtime environment purges cache
66 * entries automatically to reclaim space.
67 *
68 * <P><em> The only reason this class is not package-private is that
69 * there's no other public way to get at the server session context which
70 * is associated with any given authentication context. </em>
71 *
72 * @author David Brownell
73 */
74final class SSLSessionImpl extends ExtendedSSLSession {
75
76    /*
77     * we only really need a single null session
78     */
79    static final SSLSessionImpl         nullSession = new SSLSessionImpl();
80
81    // compression methods
82    private static final byte           compression_null = 0;
83
84    /*
85     * The state of a single session, as described in section 7.1
86     * of the SSLv3 spec.
87     */
88    private final ProtocolVersion       protocolVersion;
89    private final SessionId             sessionId;
90    private X509Certificate[]   peerCerts;
91    private byte                compressionMethod;
92    private CipherSuite         cipherSuite;
93    private SecretKey           masterSecret;
94
95    /*
96     * Information not part of the SSLv3 protocol spec, but used
97     * to support session management policies.
98     */
99    private final long          creationTime = System.currentTimeMillis();
100    private long                lastUsedTime = 0;
101    private final String        host;
102    private final int           port;
103    private SSLSessionContextImpl       context;
104    private int                 sessionCount;
105    private boolean             invalidated;
106    private X509Certificate[]   localCerts;
107    private PrivateKey          localPrivateKey;
108    private String[]            localSupportedSignAlgs;
109    private String[]            peerSupportedSignAlgs;
110    private List<SNIServerName>    requestedServerNames;
111
112    private int                 negotiatedMaxFragLen;
113    private int                 maximumPacketSize;
114
115    // Principals for non-certificate based cipher suites
116    private Principal peerPrincipal;
117    private Principal localPrincipal;
118
119    /*
120     * Is the session currently re-established with a session-resumption
121     * abbreviated initial handshake?
122     *
123     * Note that currently we only set this variable in client side.
124     */
125    private boolean isSessionResumption = false;
126
127    /*
128     * We count session creations, eventually for statistical data but
129     * also since counters make shorter debugging IDs than the big ones
130     * we use in the protocol for uniqueness-over-time.
131     */
132    private static volatile int counter = 0;
133
134    /*
135     * Use of session caches is globally enabled/disabled.
136     */
137    private static boolean      defaultRejoinable = true;
138
139    /* Class and subclass dynamic debugging support */
140    private static final Debug debug = Debug.getInstance("ssl");
141
142    /*
143     * Create a new non-rejoinable session, using the default (null)
144     * cipher spec.  This constructor returns a session which could
145     * be used either by a client or by a server, as a connection is
146     * first opened and before handshaking begins.
147     */
148    private SSLSessionImpl() {
149        this(ProtocolVersion.NONE, CipherSuite.C_NULL, null,
150            new SessionId(false, null), null, -1);
151    }
152
153    /*
154     * Create a new session, using a given cipher spec.  This will
155     * be rejoinable if session caching is enabled; the constructor
156     * is intended mostly for use by serves.
157     */
158    SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,
159            Collection<SignatureAndHashAlgorithm> algorithms,
160            SecureRandom generator, String host, int port) {
161        this(protocolVersion, cipherSuite, algorithms,
162             new SessionId(defaultRejoinable, generator), host, port);
163    }
164
165    /*
166     * Record a new session, using a given cipher spec and session ID.
167     */
168    SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,
169            Collection<SignatureAndHashAlgorithm> algorithms,
170            SessionId id, String host, int port) {
171        this.protocolVersion = protocolVersion;
172        sessionId = id;
173        peerCerts = null;
174        compressionMethod = compression_null;
175        this.cipherSuite = cipherSuite;
176        masterSecret = null;
177        this.host = host;
178        this.port = port;
179        sessionCount = ++counter;
180        localSupportedSignAlgs =
181            SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
182        negotiatedMaxFragLen = -1;
183
184        if (debug != null && Debug.isOn("session")) {
185            System.out.println("%% Initialized:  " + this);
186        }
187    }
188
189    void setMasterSecret(SecretKey secret) {
190        if (masterSecret == null) {
191            masterSecret = secret;
192        } else {
193            throw new RuntimeException("setMasterSecret() error");
194        }
195    }
196
197    /**
198     * Returns the master secret ... treat with extreme caution!
199     */
200    SecretKey getMasterSecret() {
201        return masterSecret;
202    }
203
204    void setPeerCertificates(X509Certificate[] peer) {
205        if (peerCerts == null) {
206            peerCerts = peer;
207        }
208    }
209
210    void setLocalCertificates(X509Certificate[] local) {
211        localCerts = local;
212    }
213
214    void setLocalPrivateKey(PrivateKey privateKey) {
215        localPrivateKey = privateKey;
216    }
217
218    void setPeerSupportedSignatureAlgorithms(
219            Collection<SignatureAndHashAlgorithm> algorithms) {
220        peerSupportedSignAlgs =
221            SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
222    }
223
224    void setRequestedServerNames(List<SNIServerName> requestedServerNames) {
225        this.requestedServerNames = new ArrayList<>(requestedServerNames);
226    }
227
228    /**
229     * Set the peer principal.
230     */
231    void setPeerPrincipal(Principal principal) {
232        if (peerPrincipal == null) {
233            peerPrincipal = principal;
234        }
235    }
236
237    /**
238     * Set the local principal.
239     */
240    void setLocalPrincipal(Principal principal) {
241        localPrincipal = principal;
242    }
243
244    /**
245     * Returns true iff this session may be resumed ... sessions are
246     * usually resumable.  Security policies may suggest otherwise,
247     * for example sessions that haven't been used for a while (say,
248     * a working day) won't be resumable, and sessions might have a
249     * maximum lifetime in any case.
250     */
251    boolean isRejoinable() {
252        return sessionId != null && sessionId.length() != 0 &&
253            !invalidated && isLocalAuthenticationValid();
254    }
255
256    @Override
257    public synchronized boolean isValid() {
258        return isRejoinable();
259    }
260
261    /**
262     * Check if the authentication used when establishing this session
263     * is still valid. Returns true if no authentication was used
264     */
265    boolean isLocalAuthenticationValid() {
266        if (localPrivateKey != null) {
267            try {
268                // if the private key is no longer valid, getAlgorithm()
269                // should throw an exception
270                // (e.g. Smartcard has been removed from the reader)
271                localPrivateKey.getAlgorithm();
272            } catch (Exception e) {
273                invalidate();
274                return false;
275            }
276        }
277        return true;
278    }
279
280    /**
281     * Returns the ID for this session.  The ID is fixed for the
282     * duration of the session; neither it, nor its value, changes.
283     */
284    @Override
285    public byte[] getId() {
286        return sessionId.getId();
287    }
288
289    /**
290     * For server sessions, this returns the set of sessions which
291     * are currently valid in this process.  For client sessions,
292     * this returns null.
293     */
294    @Override
295    public SSLSessionContext getSessionContext() {
296        /*
297         * An interim security policy until we can do something
298         * more specific in 1.2. Only allow trusted code (code which
299         * can set system properties) to get an
300         * SSLSessionContext. This is to limit the ability of code to
301         * look up specific sessions or enumerate over them. Otherwise,
302         * code can only get session objects from successful SSL
303         * connections which implies that they must have had permission
304         * to make the network connection in the first place.
305         */
306        SecurityManager sm;
307        if ((sm = System.getSecurityManager()) != null) {
308            sm.checkPermission(new SSLPermission("getSSLSessionContext"));
309        }
310
311        return context;
312    }
313
314
315    SessionId getSessionId() {
316        return sessionId;
317    }
318
319
320    /**
321     * Returns the cipher spec in use on this session
322     */
323    CipherSuite getSuite() {
324        return cipherSuite;
325    }
326
327    /**
328     * Resets the cipher spec in use on this session
329     */
330    void setSuite(CipherSuite suite) {
331       cipherSuite = suite;
332
333       if (debug != null && Debug.isOn("session")) {
334           System.out.println("%% Negotiating:  " + this);
335       }
336    }
337
338    /**
339     * Return true if the session is currently re-established with a
340     * session-resumption abbreviated initial handshake.
341     */
342    boolean isSessionResumption() {
343        return isSessionResumption;
344    }
345
346    /**
347     * Resets whether the session is re-established with a session-resumption
348     * abbreviated initial handshake.
349     */
350    void setAsSessionResumption(boolean flag) {
351        isSessionResumption = flag;
352    }
353
354    /**
355     * Returns the name of the cipher suite in use on this session
356     */
357    @Override
358    public String getCipherSuite() {
359        return getSuite().name;
360    }
361
362    ProtocolVersion getProtocolVersion() {
363        return protocolVersion;
364    }
365
366    /**
367     * Returns the standard name of the protocol in use on this session
368     */
369    @Override
370    public String getProtocol() {
371        return getProtocolVersion().name;
372    }
373
374    /**
375     * Returns the compression technique used in this session
376     */
377    byte getCompression() {
378        return compressionMethod;
379    }
380
381    /**
382     * Returns the hashcode for this session
383     */
384    @Override
385    public int hashCode() {
386        return sessionId.hashCode();
387    }
388
389
390    /**
391     * Returns true if sessions have same ids, false otherwise.
392     */
393    @Override
394    public boolean equals(Object obj) {
395
396        if (obj == this) {
397            return true;
398        }
399
400        if (obj instanceof SSLSessionImpl) {
401            SSLSessionImpl sess = (SSLSessionImpl) obj;
402            return (sessionId != null) && (sessionId.equals(
403                        sess.getSessionId()));
404        }
405
406        return false;
407    }
408
409
410    /**
411     * Return the cert chain presented by the peer in the
412     * java.security.cert format.
413     * Note: This method can be used only when using certificate-based
414     * cipher suites; using it with non-certificate-based cipher suites,
415     * such as Kerberos, will throw an SSLPeerUnverifiedException.
416     *
417     * @return array of peer X.509 certs, with the peer's own cert
418     *  first in the chain, and with the "root" CA last.
419     */
420    @Override
421    public java.security.cert.Certificate[] getPeerCertificates()
422            throws SSLPeerUnverifiedException {
423        //
424        // clone to preserve integrity of session ... caller can't
425        // change record of peer identity even by accident, much
426        // less do it intentionally.
427        //
428        if ((cipherSuite.keyExchange == K_KRB5) ||
429            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
430            throw new SSLPeerUnverifiedException("no certificates expected"
431                        + " for Kerberos cipher suites");
432        }
433        if (peerCerts == null) {
434            throw new SSLPeerUnverifiedException("peer not authenticated");
435        }
436        // Certs are immutable objects, therefore we don't clone them.
437        // But do need to clone the array, so that nothing is inserted
438        // into peerCerts.
439        return (java.security.cert.Certificate[])peerCerts.clone();
440    }
441
442    /**
443     * Return the cert chain presented to the peer in the
444     * java.security.cert format.
445     * Note: This method is useful only when using certificate-based
446     * cipher suites.
447     *
448     * @return array of peer X.509 certs, with the peer's own cert
449     *  first in the chain, and with the "root" CA last.
450     */
451    @Override
452    public java.security.cert.Certificate[] getLocalCertificates() {
453        //
454        // clone to preserve integrity of session ... caller can't
455        // change record of peer identity even by accident, much
456        // less do it intentionally.
457        return (localCerts == null ? null :
458            (java.security.cert.Certificate[])localCerts.clone());
459    }
460
461    /**
462     * Return the cert chain presented by the peer in the
463     * javax.security.cert format.
464     * Note: This method can be used only when using certificate-based
465     * cipher suites; using it with non-certificate-based cipher suites,
466     * such as Kerberos, will throw an SSLPeerUnverifiedException.
467     *
468     * @return array of peer X.509 certs, with the peer's own cert
469     *  first in the chain, and with the "root" CA last.
470     *
471     * @deprecated This method returns the deprecated
472     *  {@code javax.security.cert.X509Certificate} type.
473     *  Use {@code getPeerCertificates()} instead.
474     */
475    @Override
476    @Deprecated
477    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
478            throws SSLPeerUnverifiedException {
479        //
480        // clone to preserve integrity of session ... caller can't
481        // change record of peer identity even by accident, much
482        // less do it intentionally.
483        //
484        if ((cipherSuite.keyExchange == K_KRB5) ||
485            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
486            throw new SSLPeerUnverifiedException("no certificates expected"
487                        + " for Kerberos cipher suites");
488        }
489        if (peerCerts == null) {
490            throw new SSLPeerUnverifiedException("peer not authenticated");
491        }
492        javax.security.cert.X509Certificate[] certs;
493        certs = new javax.security.cert.X509Certificate[peerCerts.length];
494        for (int i = 0; i < peerCerts.length; i++) {
495            byte[] der = null;
496            try {
497                der = peerCerts[i].getEncoded();
498                certs[i] = javax.security.cert.X509Certificate.getInstance(der);
499            } catch (CertificateEncodingException e) {
500                throw new SSLPeerUnverifiedException(e.getMessage());
501            } catch (javax.security.cert.CertificateException e) {
502                throw new SSLPeerUnverifiedException(e.getMessage());
503            }
504        }
505
506        return certs;
507    }
508
509    /**
510     * Return the cert chain presented by the peer.
511     * Note: This method can be used only when using certificate-based
512     * cipher suites; using it with non-certificate-based cipher suites,
513     * such as Kerberos, will throw an SSLPeerUnverifiedException.
514     *
515     * @return array of peer X.509 certs, with the peer's own cert
516     *  first in the chain, and with the "root" CA last.
517     */
518    public X509Certificate[] getCertificateChain()
519            throws SSLPeerUnverifiedException {
520        /*
521         * clone to preserve integrity of session ... caller can't
522         * change record of peer identity even by accident, much
523         * less do it intentionally.
524         */
525        if ((cipherSuite.keyExchange == K_KRB5) ||
526            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
527            throw new SSLPeerUnverifiedException("no certificates expected"
528                        + " for Kerberos cipher suites");
529        }
530        if (peerCerts != null) {
531            return peerCerts.clone();
532        } else {
533            throw new SSLPeerUnverifiedException("peer not authenticated");
534        }
535    }
536
537    /**
538     * Returns the identity of the peer which was established as part of
539     * defining the session.
540     *
541     * @return the peer's principal. Returns an X500Principal of the
542     * end-entity certificate for X509-based cipher suites, and
543     * Principal for Kerberos cipher suites.
544     *
545     * @throws SSLPeerUnverifiedException if the peer's identity has not
546     *          been verified
547     */
548    @Override
549    public Principal getPeerPrincipal()
550                throws SSLPeerUnverifiedException
551    {
552        if ((cipherSuite.keyExchange == K_KRB5) ||
553            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
554            if (peerPrincipal == null) {
555                throw new SSLPeerUnverifiedException("peer not authenticated");
556            } else {
557                // Eliminate dependency on KerberosPrincipal
558                return peerPrincipal;
559            }
560        }
561        if (peerCerts == null) {
562            throw new SSLPeerUnverifiedException("peer not authenticated");
563        }
564        return peerCerts[0].getSubjectX500Principal();
565    }
566
567    /**
568     * Returns the principal that was sent to the peer during handshaking.
569     *
570     * @return the principal sent to the peer. Returns an X500Principal
571     * of the end-entity certificate for X509-based cipher suites, and
572     * Principal for Kerberos cipher suites. If no principal was
573     * sent, then null is returned.
574     */
575    @Override
576    public Principal getLocalPrincipal() {
577
578        if ((cipherSuite.keyExchange == K_KRB5) ||
579            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
580                // Eliminate dependency on KerberosPrincipal
581                return (localPrincipal == null ? null : localPrincipal);
582        }
583        return (localCerts == null ? null :
584                localCerts[0].getSubjectX500Principal());
585    }
586
587    /**
588     * Returns the time this session was created.
589     */
590    @Override
591    public long getCreationTime() {
592        return creationTime;
593    }
594
595    /**
596     * Returns the last time this session was used to initialize
597     * a connection.
598     */
599    @Override
600    public long getLastAccessedTime() {
601        return (lastUsedTime != 0) ? lastUsedTime : creationTime;
602    }
603
604    void setLastAccessedTime(long time) {
605        lastUsedTime = time;
606    }
607
608
609    /**
610     * Returns the network address of the session's peer.  This
611     * implementation does not insist that connections between
612     * different ports on the same host must necessarily belong
613     * to different sessions, though that is of course allowed.
614     */
615    public InetAddress getPeerAddress() {
616        try {
617            return InetAddress.getByName(host);
618        } catch (java.net.UnknownHostException e) {
619            return null;
620        }
621    }
622
623    @Override
624    public String getPeerHost() {
625        return host;
626    }
627
628    /**
629     * Need to provide the port info for caching sessions based on
630     * host and port. Accessed by SSLSessionContextImpl
631     */
632    @Override
633    public int getPeerPort() {
634        return port;
635    }
636
637    void setContext(SSLSessionContextImpl ctx) {
638        if (context == null) {
639            context = ctx;
640        }
641    }
642
643    /**
644     * Invalidate a session.  Active connections may still exist, but
645     * no connections will be able to rejoin this session.
646     */
647    @Override
648    synchronized public void invalidate() {
649        //
650        // Can't invalidate the NULL session -- this would be
651        // attempted when we get a handshaking error on a brand
652        // new connection, with no "real" session yet.
653        //
654        if (this == nullSession) {
655            return;
656        }
657        invalidated = true;
658        if (debug != null && Debug.isOn("session")) {
659            System.out.println("%% Invalidated:  " + this);
660        }
661        if (context != null) {
662            context.remove(sessionId);
663            context = null;
664        }
665    }
666
667    /*
668     * Table of application-specific session data indexed by an application
669     * key and the calling security context. This is important since
670     * sessions can be shared across different protection domains.
671     */
672    private Hashtable<SecureKey, Object> table = new Hashtable<>();
673
674    /**
675     * Assigns a session value.  Session change events are given if
676     * appropriate, to any original value as well as the new value.
677     */
678    @Override
679    public void putValue(String key, Object value) {
680        if ((key == null) || (value == null)) {
681            throw new IllegalArgumentException("arguments can not be null");
682        }
683
684        SecureKey secureKey = new SecureKey(key);
685        Object oldValue = table.put(secureKey, value);
686
687        if (oldValue instanceof SSLSessionBindingListener) {
688            SSLSessionBindingEvent e;
689
690            e = new SSLSessionBindingEvent(this, key);
691            ((SSLSessionBindingListener)oldValue).valueUnbound(e);
692        }
693        if (value instanceof SSLSessionBindingListener) {
694            SSLSessionBindingEvent e;
695
696            e = new SSLSessionBindingEvent(this, key);
697            ((SSLSessionBindingListener)value).valueBound(e);
698        }
699    }
700
701
702    /**
703     * Returns the specified session value.
704     */
705    @Override
706    public Object getValue(String key) {
707        if (key == null) {
708            throw new IllegalArgumentException("argument can not be null");
709        }
710
711        SecureKey secureKey = new SecureKey(key);
712        return table.get(secureKey);
713    }
714
715
716    /**
717     * Removes the specified session value, delivering a session changed
718     * event as appropriate.
719     */
720    @Override
721    public void removeValue(String key) {
722        if (key == null) {
723            throw new IllegalArgumentException("argument can not be null");
724        }
725
726        SecureKey secureKey = new SecureKey(key);
727        Object value = table.remove(secureKey);
728
729        if (value instanceof SSLSessionBindingListener) {
730            SSLSessionBindingEvent e;
731
732            e = new SSLSessionBindingEvent(this, key);
733            ((SSLSessionBindingListener)value).valueUnbound(e);
734        }
735    }
736
737
738    /**
739     * Lists the names of the session values.
740     */
741    @Override
742    public String[] getValueNames() {
743        Enumeration<SecureKey> e;
744        Vector<Object> v = new Vector<>();
745        SecureKey key;
746        Object securityCtx = SecureKey.getCurrentSecurityContext();
747
748        for (e = table.keys(); e.hasMoreElements(); ) {
749            key = e.nextElement();
750
751            if (securityCtx.equals(key.getSecurityContext())) {
752                v.addElement(key.getAppKey());
753            }
754        }
755        String[] names = new String[v.size()];
756        v.copyInto(names);
757
758        return names;
759    }
760
761    /**
762     * Use large packet sizes now or follow RFC 2246 packet sizes (2^14)
763     * until changed.
764     *
765     * In the TLS specification (section 6.2.1, RFC2246), it is not
766     * recommended that the plaintext has more than 2^14 bytes.
767     * However, some TLS implementations violate the specification.
768     * This is a workaround for interoperability with these stacks.
769     *
770     * Application could accept large fragments up to 2^15 bytes by
771     * setting the system property jsse.SSLEngine.acceptLargeFragments
772     * to "true".
773     */
774    private boolean acceptLargeFragments =
775        Debug.getBooleanProperty("jsse.SSLEngine.acceptLargeFragments", false);
776
777    /**
778     * Expand the buffer size of both SSL/TLS network packet and
779     * application data.
780     */
781    protected synchronized void expandBufferSizes() {
782        acceptLargeFragments = true;
783    }
784
785    /**
786     * Gets the current size of the largest SSL/TLS packet that is expected
787     * when using this session.
788     */
789    @Override
790    public synchronized int getPacketBufferSize() {
791        // Use the bigger packet size calculated from maximumPacketSize
792        // and negotiatedMaxFragLen.
793        int packetSize = 0;
794        if (negotiatedMaxFragLen > 0) {
795            packetSize = cipherSuite.calculatePacketSize(
796                    negotiatedMaxFragLen, protocolVersion,
797                    protocolVersion.isDTLSProtocol());
798        }
799
800        if (maximumPacketSize > 0) {
801            return (maximumPacketSize > packetSize) ?
802                    maximumPacketSize : packetSize;
803        }
804
805        if (packetSize != 0) {
806           return packetSize;
807        }
808
809        if (protocolVersion.isDTLSProtocol()) {
810            return DTLSRecord.maxRecordSize;
811        } else {
812            return acceptLargeFragments ?
813                    SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
814        }
815    }
816
817    /**
818     * Gets the current size of the largest application data that is
819     * expected when using this session.
820     */
821    @Override
822    public synchronized int getApplicationBufferSize() {
823        // Use the bigger fragment size calculated from maximumPacketSize
824        // and negotiatedMaxFragLen.
825        int fragmentSize = 0;
826        if (maximumPacketSize > 0) {
827            fragmentSize = cipherSuite.calculateFragSize(
828                    maximumPacketSize, protocolVersion,
829                    protocolVersion.isDTLSProtocol());
830        }
831
832        if (negotiatedMaxFragLen > 0) {
833            return (negotiatedMaxFragLen > fragmentSize) ?
834                    negotiatedMaxFragLen : fragmentSize;
835        }
836
837        if (fragmentSize != 0) {
838            return fragmentSize;
839        }
840
841        if (protocolVersion.isDTLSProtocol()) {
842            return Record.maxDataSize;
843        } else {
844            int maxPacketSize = acceptLargeFragments ?
845                        SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
846            return (maxPacketSize - SSLRecord.headerSize);
847        }
848    }
849
850    /**
851     * Sets the negotiated maximum fragment length, as specified by the
852     * max_fragment_length ClientHello extension in RFC 6066.
853     *
854     * @param  negotiatedMaxFragLen
855     *         the negotiated maximum fragment length, or {@code -1} if
856     *         no such length has been negotiated.
857     */
858    synchronized void setNegotiatedMaxFragSize(
859            int negotiatedMaxFragLen) {
860
861        this.negotiatedMaxFragLen = negotiatedMaxFragLen;
862    }
863
864    /**
865     * Get the negotiated maximum fragment length, as specified by the
866     * max_fragment_length ClientHello extension in RFC 6066.
867     *
868     * @return the negotiated maximum fragment length, or {@code -1} if
869     *         no such length has been negotiated.
870     */
871    synchronized int getNegotiatedMaxFragSize() {
872        return negotiatedMaxFragLen;
873    }
874
875    synchronized void setMaximumPacketSize(int maximumPacketSize) {
876        this.maximumPacketSize = maximumPacketSize;
877    }
878
879    synchronized int getMaximumPacketSize() {
880        return maximumPacketSize;
881    }
882
883    /**
884     * Gets an array of supported signature algorithms that the local side is
885     * willing to verify.
886     */
887    @Override
888    public String[] getLocalSupportedSignatureAlgorithms() {
889        if (localSupportedSignAlgs != null) {
890            return localSupportedSignAlgs.clone();
891        }
892
893        return new String[0];
894    }
895
896    /**
897     * Gets an array of supported signature algorithms that the peer is
898     * able to verify.
899     */
900    @Override
901    public String[] getPeerSupportedSignatureAlgorithms() {
902        if (peerSupportedSignAlgs != null) {
903            return peerSupportedSignAlgs.clone();
904        }
905
906        return new String[0];
907    }
908
909    /**
910     * Obtains a <code>List</code> containing all {@link SNIServerName}s
911     * of the requested Server Name Indication (SNI) extension.
912     */
913    @Override
914    public List<SNIServerName> getRequestedServerNames() {
915        if (requestedServerNames != null && !requestedServerNames.isEmpty()) {
916            return Collections.<SNIServerName>unmodifiableList(
917                                                requestedServerNames);
918        }
919
920        return Collections.<SNIServerName>emptyList();
921    }
922
923    /** Returns a string representation of this SSL session */
924    @Override
925    public String toString() {
926        return "[Session-" + sessionCount
927            + ", " + getCipherSuite()
928            + "]";
929    }
930
931    /**
932     * When SSL sessions are finalized, all values bound to
933     * them are removed.
934     */
935    @Override
936    protected void finalize() throws Throwable {
937        String[] names = getValueNames();
938        for (int i = 0; i < names.length; i++) {
939            removeValue(names[i]);
940        }
941    }
942}
943
944
945/**
946 * This "struct" class serves as a Hash Key that combines an
947 * application-specific key and a security context.
948 */
949class SecureKey {
950    private static Object       nullObject = new Object();
951    private Object        appKey;
952    private Object      securityCtx;
953
954    static Object getCurrentSecurityContext() {
955        SecurityManager sm = System.getSecurityManager();
956        Object context = null;
957
958        if (sm != null)
959            context = sm.getSecurityContext();
960        if (context == null)
961            context = nullObject;
962        return context;
963    }
964
965    SecureKey(Object key) {
966        this.appKey = key;
967        this.securityCtx = getCurrentSecurityContext();
968    }
969
970    Object getAppKey() {
971        return appKey;
972    }
973
974    Object getSecurityContext() {
975        return securityCtx;
976    }
977
978    @Override
979    public int hashCode() {
980        return appKey.hashCode() ^ securityCtx.hashCode();
981    }
982
983    @Override
984    public boolean equals(Object o) {
985        return o instanceof SecureKey && ((SecureKey)o).appKey.equals(appKey)
986                        && ((SecureKey)o).securityCtx.equals(securityCtx);
987    }
988}
989