832 lines · 26.6 KB · Java
RawDownload
/* * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */  package sun.security.ssl; import java.io.*;import java.net.*;import java.util.Enumeration;import java.util.Hashtable;import java.util.Vector;import java.util.Arrays;import java.util.Collection; import java.security.Principal;import java.security.PrivateKey;import java.security.SecureRandom;import java.security.cert.X509Certificate;import java.security.cert.CertificateEncodingException; import javax.crypto.SecretKey; import javax.net.ssl.SSLSession;import javax.net.ssl.SSLSessionContext;import javax.net.ssl.SSLSessionBindingListener;import javax.net.ssl.SSLSessionBindingEvent;import javax.net.ssl.SSLPeerUnverifiedException;import javax.net.ssl.SSLSession;import javax.net.ssl.SSLPermission;import javax.net.ssl.SSLException;import javax.net.ssl.ExtendedSSLSession; import javax.security.auth.x500.X500Principal; import static sun.security.ssl.CipherSuite.*;import static sun.security.ssl.CipherSuite.KeyExchange.*; /** * Implements the SSL session interface, and exposes the session context * which is maintained by SSL servers. * * <P> Servers have the ability to manage the sessions associated with * their authentication context(s).  They can do this by enumerating the * IDs of the sessions which are cached, examining those sessions, and then * perhaps invalidating a given session so that it can't be used again. * If servers do not explicitly manage the cache, sessions will linger * until memory is low enough that the runtime environment purges cache * entries automatically to reclaim space. * * <P><em> The only reason this class is not package-private is that * there's no other public way to get at the server session context which * is associated with any given authentication context. </em> * * @author David Brownell */final class SSLSessionImpl extends ExtendedSSLSession {     /*     * we only really need a single null session     */    static final SSLSessionImpl         nullSession = new SSLSessionImpl();     // compression methods    private static final byte           compression_null = 0;     /*     * The state of a single session, as described in section 7.1     * of the SSLv3 spec.     */    private final ProtocolVersion       protocolVersion;    private final SessionId             sessionId;    private X509Certificate[]   peerCerts;    private byte                compressionMethod;    private CipherSuite         cipherSuite;    private SecretKey           masterSecret;     /*     * Information not part of the SSLv3 protocol spec, but used     * to support session management policies.     */    private final long          creationTime = System.currentTimeMillis();    private long                lastUsedTime = 0;    private final String        host;    private final int           port;    private SSLSessionContextImpl       context;    private int                 sessionCount;    private boolean             invalidated;    private X509Certificate[]   localCerts;    private PrivateKey          localPrivateKey;    private String[]            localSupportedSignAlgs;    private String[]            peerSupportedSignAlgs;     // Principals for non-certificate based cipher suites    private Principal peerPrincipal;    private Principal localPrincipal;     /*     * We count session creations, eventually for statistical data but     * also since counters make shorter debugging IDs than the big ones     * we use in the protocol for uniqueness-over-time.     */    private static volatile int counter = 0;     /*     * Use of session caches is globally enabled/disabled.     */    private static boolean      defaultRejoinable = true;     /* Class and subclass dynamic debugging support */    private static final Debug debug = Debug.getInstance("ssl");     /*     * Create a new non-rejoinable session, using the default (null)     * cipher spec.  This constructor returns a session which could     * be used either by a client or by a server, as a connection is     * first opened and before handshaking begins.     */    private SSLSessionImpl() {        this(ProtocolVersion.NONE, CipherSuite.C_NULL, null,            new SessionId(false, null), null, -1);    }     /*     * Create a new session, using a given cipher spec.  This will     * be rejoinable if session caching is enabled; the constructor     * is intended mostly for use by serves.     */    SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,            Collection<SignatureAndHashAlgorithm> algorithms,            SecureRandom generator, String host, int port) {        this(protocolVersion, cipherSuite, algorithms,             new SessionId(defaultRejoinable, generator), host, port);    }     /*     * Record a new session, using a given cipher spec and session ID.     */    SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,            Collection<SignatureAndHashAlgorithm> algorithms,            SessionId id, String host, int port) {        this.protocolVersion = protocolVersion;        sessionId = id;        peerCerts = null;        compressionMethod = compression_null;        this.cipherSuite = cipherSuite;        masterSecret = null;        this.host = host;        this.port = port;        sessionCount = ++counter;        localSupportedSignAlgs =            SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);         if (debug != null && Debug.isOn("session")) {            System.out.println("%% Initialized:  " + this);        }    }     void setMasterSecret(SecretKey secret) {        if (masterSecret == null) {            masterSecret = secret;        } else {            throw new RuntimeException("setMasterSecret() error");        }    }     /**     * Returns the master secret ... treat with extreme caution!     */    SecretKey getMasterSecret() {        return masterSecret;    }     void setPeerCertificates(X509Certificate[] peer) {        if (peerCerts == null) {            peerCerts = peer;        }    }     void setLocalCertificates(X509Certificate[] local) {        localCerts = local;    }     void setLocalPrivateKey(PrivateKey privateKey) {        localPrivateKey = privateKey;    }     void setPeerSupportedSignatureAlgorithms(            Collection<SignatureAndHashAlgorithm> algorithms) {        peerSupportedSignAlgs =            SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);    }     /**     * Set the peer principal.     */    void setPeerPrincipal(Principal principal) {        if (peerPrincipal == null) {            peerPrincipal = principal;        }    }     /**     * Set the local principal.     */    void setLocalPrincipal(Principal principal) {        localPrincipal = principal;    }     /**     * Returns true iff this session may be resumed ... sessions are     * usually resumable.  Security policies may suggest otherwise,     * for example sessions that haven't been used for a while (say,     * a working day) won't be resumable, and sessions might have a     * maximum lifetime in any case.     */    boolean isRejoinable() {        return sessionId != null && sessionId.length() != 0 &&            !invalidated && isLocalAuthenticationValid();    }     public synchronized boolean isValid() {        return isRejoinable();    }     /**     * Check if the authentication used when establishing this session     * is still valid. Returns true if no authentication was used     */    boolean isLocalAuthenticationValid() {        if (localPrivateKey != null) {            try {                // if the private key is no longer valid, getAlgorithm()                // should throw an exception                // (e.g. Smartcard has been removed from the reader)                localPrivateKey.getAlgorithm();            } catch (Exception e) {                invalidate();                return false;            }        }        return true;    }     /**     * Returns the ID for this session.  The ID is fixed for the     * duration of the session; neither it, nor its value, changes.     */    public byte[] getId() {        return sessionId.getId();    }     /**     * For server sessions, this returns the set of sessions which     * are currently valid in this process.  For client sessions,     * this returns null.     */    public SSLSessionContext getSessionContext() {        /*         * An interim security policy until we can do something         * more specific in 1.2. Only allow trusted code (code which         * can set system properties) to get an         * SSLSessionContext. This is to limit the ability of code to         * look up specific sessions or enumerate over them. Otherwise,         * code can only get session objects from successful SSL         * connections which implies that they must have had permission         * to make the network connection in the first place.         */        SecurityManager sm;        if ((sm = System.getSecurityManager()) != null) {            sm.checkPermission(new SSLPermission("getSSLSessionContext"));        }         return context;    }      SessionId getSessionId() {        return sessionId;    }      /**     * Returns the cipher spec in use on this session     */    CipherSuite getSuite() {        return cipherSuite;    }     /**     * Resets the cipher spec in use on this session     */    void setSuite(CipherSuite suite) {       cipherSuite = suite;        if (debug != null && Debug.isOn("session")) {           System.out.println("%% Negotiating:  " + this);       }    }     /**     * Returns the name of the cipher suite in use on this session     */    public String getCipherSuite() {        return getSuite().name;    }     ProtocolVersion getProtocolVersion() {        return protocolVersion;    }     /**     * Returns the standard name of the protocol in use on this session     */    public String getProtocol() {        return getProtocolVersion().name;    }     /**     * Returns the compression technique used in this session     */    byte getCompression() {        return compressionMethod;    }     /**     * Returns the hashcode for this session     */    public int hashCode() {        return sessionId.hashCode();    }      /**     * Returns true if sessions have same ids, false otherwise.     */    public boolean equals(Object obj) {         if (obj == this) {            return true;        }         if (obj instanceof SSLSessionImpl) {            SSLSessionImpl sess = (SSLSessionImpl) obj;            return (sessionId != null) && (sessionId.equals(                        sess.getSessionId()));        }         return false;    }      /**     * Return the cert chain presented by the peer in the     * java.security.cert format.     * Note: This method can be used only when using certificate-based     * cipher suites; using it with non-certificate-based cipher suites,     * such as Kerberos, will throw an SSLPeerUnverifiedException.     *     * @return array of peer X.509 certs, with the peer's own cert     *  first in the chain, and with the "root" CA last.     */    public java.security.cert.Certificate[] getPeerCertificates()            throws SSLPeerUnverifiedException {        //        // clone to preserve integrity of session ... caller can't        // change record of peer identity even by accident, much        // less do it intentionally.        //        if ((cipherSuite.keyExchange == K_KRB5) ||            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {            throw new SSLPeerUnverifiedException("no certificates expected"                        + " for Kerberos cipher suites");        }        if (peerCerts == null) {            throw new SSLPeerUnverifiedException("peer not authenticated");        }        // Certs are immutable objects, therefore we don't clone them.        // But do need to clone the array, so that nothing is inserted        // into peerCerts.        return (java.security.cert.Certificate[])peerCerts.clone();    }     /**     * Return the cert chain presented to the peer in the     * java.security.cert format.     * Note: This method is useful only when using certificate-based     * cipher suites.     *     * @return array of peer X.509 certs, with the peer's own cert     *  first in the chain, and with the "root" CA last.     */    public java.security.cert.Certificate[] getLocalCertificates() {        //        // clone to preserve integrity of session ... caller can't        // change record of peer identity even by accident, much        // less do it intentionally.        return (localCerts == null ? null :            (java.security.cert.Certificate[])localCerts.clone());    }     /**     * Return the cert chain presented by the peer in the     * javax.security.cert format.     * Note: This method can be used only when using certificate-based     * cipher suites; using it with non-certificate-based cipher suites,     * such as Kerberos, will throw an SSLPeerUnverifiedException.     *     * @return array of peer X.509 certs, with the peer's own cert     *  first in the chain, and with the "root" CA last.     */    public javax.security.cert.X509Certificate[] getPeerCertificateChain()            throws SSLPeerUnverifiedException {        //        // clone to preserve integrity of session ... caller can't        // change record of peer identity even by accident, much        // less do it intentionally.        //        if ((cipherSuite.keyExchange == K_KRB5) ||            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {            throw new SSLPeerUnverifiedException("no certificates expected"                        + " for Kerberos cipher suites");        }        if (peerCerts == null) {            throw new SSLPeerUnverifiedException("peer not authenticated");        }        javax.security.cert.X509Certificate[] certs;        certs = new javax.security.cert.X509Certificate[peerCerts.length];        for (int i = 0; i < peerCerts.length; i++) {            byte[] der = null;            try {                der = peerCerts[i].getEncoded();                certs[i] = javax.security.cert.X509Certificate.getInstance(der);            } catch (CertificateEncodingException e) {                throw new SSLPeerUnverifiedException(e.getMessage());            } catch (javax.security.cert.CertificateException e) {                throw new SSLPeerUnverifiedException(e.getMessage());            }        }         return certs;    }     /**     * Return the cert chain presented by the peer.     * Note: This method can be used only when using certificate-based     * cipher suites; using it with non-certificate-based cipher suites,     * such as Kerberos, will throw an SSLPeerUnverifiedException.     *     * @return array of peer X.509 certs, with the peer's own cert     *  first in the chain, and with the "root" CA last.     */    public X509Certificate[] getCertificateChain()            throws SSLPeerUnverifiedException {        /*         * clone to preserve integrity of session ... caller can't         * change record of peer identity even by accident, much         * less do it intentionally.         */        if ((cipherSuite.keyExchange == K_KRB5) ||            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {            throw new SSLPeerUnverifiedException("no certificates expected"                        + " for Kerberos cipher suites");        }        if (peerCerts != null) {            return peerCerts.clone();        } else {            throw new SSLPeerUnverifiedException("peer not authenticated");        }    }     /**     * Returns the identity of the peer which was established as part of     * defining the session.     *     * @return the peer's principal. Returns an X500Principal of the     * end-entity certificate for X509-based cipher suites, and     * Principal for Kerberos cipher suites.     *

Lines 1–500 of 832.