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