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 }