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