SSLSessionImpl.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27package sun.security.ssl;
28
29import java.net.*;
30import java.util.Enumeration;
31import java.util.Hashtable;
32import java.util.Vector;
33import java.util.Collection;
34import java.util.Collections;
35import java.util.List;
36import java.util.ArrayList;
37
38import java.security.Principal;
39import java.security.PrivateKey;
40import java.security.SecureRandom;
41import java.security.cert.X509Certificate;
42import java.security.cert.CertificateEncodingException;
43
44import javax.crypto.SecretKey;
45
46import javax.net.ssl.SSLSessionContext;
47import javax.net.ssl.SSLSessionBindingListener;
48import javax.net.ssl.SSLSessionBindingEvent;
49import javax.net.ssl.SSLPeerUnverifiedException;
50import javax.net.ssl.SSLPermission;
51import javax.net.ssl.ExtendedSSLSession;
52import javax.net.ssl.SNIServerName;
53
54import static sun.security.ssl.CipherSuite.KeyExchange.*;
55
56/**
57 * Implements the SSL session interface, and exposes the session context
58 * which is maintained by SSL servers.
59 *
60 * <P> Servers have the ability to manage the sessions associated with
61 * their authentication context(s).  They can do this by enumerating the
62 * IDs of the sessions which are cached, examining those sessions, and then
63 * perhaps invalidating a given session so that it can't be used again.
64 * If servers do not explicitly manage the cache, sessions will linger
65 * until memory is low enough that the runtime environment purges cache
66 * entries automatically to reclaim space.
67 *
68 * <P><em> The only reason this class is not package-private is that
69 * there's no other public way to get at the server session context which
70 * is associated with any given authentication context. </em>
71 *
72 * @author David Brownell
73 */
74final class SSLSessionImpl extends ExtendedSSLSession {
75
76    /*
77     * we only really need a single null session
78     */
79    static final SSLSessionImpl         nullSession = new SSLSessionImpl();
80
81    // compression methods
82    private static final byte           compression_null = 0;
83
84    /*
85     * The state of a single session, as described in section 7.1
86     * of the SSLv3 spec.
87     */
88    private final ProtocolVersion       protocolVersion;
89    private final SessionId             sessionId;
90    private X509Certificate[]   peerCerts;
91    private byte                compressionMethod;
92    private CipherSuite         cipherSuite;
93    private SecretKey           masterSecret;
94
95    /*
96     * Information not part of the SSLv3 protocol spec, but used
97     * to support session management policies.
98     */
99    private final long          creationTime = System.currentTimeMillis();
100    private long                lastUsedTime = 0;
101    private final String        host;
102    private final int           port;
103    private SSLSessionContextImpl       context;
104    private int                 sessionCount;
105    private boolean             invalidated;
106    private X509Certificate[]   localCerts;
107    private PrivateKey          localPrivateKey;
108    private String[]            localSupportedSignAlgs;
109    private String[]            peerSupportedSignAlgs;
110    private List<SNIServerName>    requestedServerNames;
111    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 = 0;
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     * When SSL sessions are finalized, all values bound to
965     * them are removed.
966     */
967    @Override
968    protected void finalize() throws Throwable {
969        String[] names = getValueNames();
970        for (int i = 0; i < names.length; i++) {
971            removeValue(names[i]);
972        }
973    }
974}
975
976
977/**
978 * This "struct" class serves as a Hash Key that combines an
979 * application-specific key and a security context.
980 */
981class SecureKey {
982    private static Object       nullObject = new Object();
983    private Object        appKey;
984    private Object      securityCtx;
985
986    static Object getCurrentSecurityContext() {
987        SecurityManager sm = System.getSecurityManager();
988        Object context = null;
989
990        if (sm != null)
991            context = sm.getSecurityContext();
992        if (context == null)
993            context = nullObject;
994        return context;
995    }
996
997    SecureKey(Object key) {
998        this.appKey = key;
999        this.securityCtx = getCurrentSecurityContext();
1000    }
1001
1002    Object getAppKey() {
1003        return appKey;
1004    }
1005
1006    Object getSecurityContext() {
1007        return securityCtx;
1008    }
1009
1010    @Override
1011    public int hashCode() {
1012        return appKey.hashCode() ^ securityCtx.hashCode();
1013    }
1014
1015    @Override
1016    public boolean equals(Object o) {
1017        return o instanceof SecureKey && ((SecureKey)o).appKey.equals(appKey)
1018                        && ((SecureKey)o).securityCtx.equals(securityCtx);
1019    }
1020}
1021