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