This turns SessionStore into AbstractSessionContext, a skeletal implementation of the SessionContext interface, fleshes out some more of the Session class (itself a skeletal implementation of the SSLSession interface), and uses generic collection types in PrivateCredentials.

2006-06-28  Casey Marshall  <[EMAIL PROTECTED]>

        * gnu/javax/net/ssl/AbstractSessionContext.java: renamed from
        `SessionStore.' Implement SessionContext.
        * gnu/javax/net/ssl/PrivateCredentials.java: genericize
        collections.
        * gnu/javax/net/ssl/Session.java (packetBufferSize): new field.
        (values): genericize.
        (random): make transient.
        (truncatedMac, context): new fields.
        (getLocalPrincipal, getPacketBufferSize, getPeerPrincipal)
        (getSessionContext): implement.
        (isTruncatedMac): new method.
        (repair, privateData, setPrivateData): new abstract methods.
        (PrivateData.serialVersionUID): new constant.
        * gnu/javax/net/ssl/SessionStore.java: renamed to
        `AbstractSessionContext.'

Committed.

### Eclipse Workspace Patch 1.0
#P classpath-ssl-nio
Index: gnu/javax/net/ssl/Session.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/javax/net/ssl/Attic/Session.java,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 Session.java
--- gnu/javax/net/ssl/Session.java      3 Jun 2006 07:46:44 -0000       1.1.2.1
+++ gnu/javax/net/ssl/Session.java      28 Jun 2006 23:15:40 -0000
@@ -46,15 +46,20 @@
 
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Iterator;
+import java.util.Set;
 
+import javax.crypto.SealedObject;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
 import javax.security.cert.X509Certificate;
 
 /**
  * A concrete implementation of the [EMAIL PROTECTED] SSLSession} interface. 
This
- * class is provided to allow pluggable [EMAIL PROTECTED] SessionStore}
+ * class is provided to allow pluggable [EMAIL PROTECTED] 
AbstractSessionContext}
  * implementations.
  */
 public abstract class Session implements SSLSession, Serializable
@@ -62,6 +67,9 @@
   protected final long creationTime;
   protected long lastAccessedTime;
   protected int applicationBufferSize;
+  
+  // Default to 2^14 + 5 -- the maximum size for a record.
+  protected int packetBufferSize = 16389;
   protected ID sessionId;
   protected Certificate[] localCerts;
   protected Certificate[] peerCerts;
@@ -69,127 +77,207 @@
   protected String peerHost;
   protected int peerPort;
   protected boolean peerVerified;
-  protected HashMap values;
+  protected HashMap<String,Object> values;
   protected boolean valid;
-  protected SecureRandom random;
+  protected boolean truncatedMac = false;
+  transient protected SecureRandom random;
+  transient protected SSLSessionContext context;
 
-  protected Session ()
+  protected Session()
   {
-    creationTime = System.currentTimeMillis ();
-    values = new HashMap ();
+    creationTime = System.currentTimeMillis();
+    values = new HashMap<String, Object>();
   }
 
-  public void access ()
+  public void access()
   {
     lastAccessedTime = System.currentTimeMillis ();
   }
 
-  public int getApplicationBufferSize ()
+  public int getApplicationBufferSize()
   {
     return applicationBufferSize;
   }
 
-  public String getCipherSuite ()
+  public String getCipherSuite()
   {
     return null;
   }
 
-  public long getCreationTime ()
+  public long getCreationTime()
   {
     return creationTime;
   }
 
-  public byte[] getId ()
+  public byte[] getId()
   {
-    return sessionId.id ();
+    return sessionId.id();
   }
 
-  public ID id ()
+  public ID id()
   {
     return sessionId;
   }
 
-  public long getLastAccessedTime ()
+  public long getLastAccessedTime()
   {
     return lastAccessedTime;
   }
 
-  public Certificate[] getLocalCertificates ()
+  public Certificate[] getLocalCertificates()
   {
     if (localCerts == null)
       return null;
-    return (Certificate[]) localCerts.clone ();
+    return (Certificate[]) localCerts.clone();
   }
 
-  public Certificate[] getPeerCertificates () throws SSLPeerUnverifiedException
+  public Principal getLocalPrincipal()
+  {
+    if (localCerts != null)
+      {
+        if (localCerts[0] instanceof java.security.cert.X509Certificate)
+          return ((java.security.cert.X509Certificate) 
localCerts[0]).getSubjectDN();
+      }
+    return null;
+  }
+  
+  public int getPacketBufferSize()
+  {
+    return packetBufferSize;
+  }
+  
+  public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException
   {
     if (!peerVerified)
-      throw new SSLPeerUnverifiedException ("peer not verified");
+      throw new SSLPeerUnverifiedException("peer not verified");
     if (peerCerts == null)
       return null;
-    return (Certificate[]) peerCerts.clone ();
+    return (Certificate[]) peerCerts.clone();
   }
 
-  public X509Certificate[] getPeerCertificateChain () throws 
SSLPeerUnverifiedException
+  public X509Certificate[] getPeerCertificateChain()
+    throws SSLPeerUnverifiedException
   {
     if (!peerVerified)
-      throw new SSLPeerUnverifiedException ("peer not verified");
+      throw new SSLPeerUnverifiedException("peer not verified");
     if (peerCertChain == null)
       return null;
-    return (X509Certificate[]) peerCertChain.clone ();
+    return (X509Certificate[]) peerCertChain.clone();
   }
   
-  public String getPeerHost ()
+  public String getPeerHost()
   {
     return peerHost;
   }
   
-  public int getPeerPort ()
+  public int getPeerPort()
   {
     return peerPort;
   }
-
-  public Principal getPeerPrincipal () throws SSLPeerUnverifiedException
+  
+  public Principal getPeerPrincipal() throws SSLPeerUnverifiedException
   {
-    return null;
+    if (!peerVerified)
+      throw new SSLPeerUnverifiedException("peer not verified");
+    if (peerCertChain == null)
+      return null;
+    return peerCertChain[0].getSubjectDN();
   }
   
-  public String[] getValueNames ()
+  public SSLSessionContext getSessionContext()
   {
-    HashMap values = this.values;
-    String[] s = new String[values.size ()];
-    int i = 0;
-    for (Iterator it = values.keySet ().iterator (); it.hasNext () && i < 
s.length; )
-      s[i++] = (String) it.next ();
-    return s;
+    return context;
   }
   
-  public Object getValue (String name)
+  public String[] getValueNames()
   {
-    return values.get (name);
+    Set<String> keys = this.values.keySet();
+    return keys.toArray(new String[keys.size()]);
   }
   
-  public void invalidate ()
+  public Object getValue(String name)
+  {
+    return values.get(name);
+  }
+  
+  public void invalidate()
   {
     valid = false;
   }
   
-  public boolean isValid ()
+  public boolean isValid()
   {
     return valid;
   }
   
-  public void putValue (String name, Object value)
+  public void putValue(String name, Object value)
+  {
+    values.put(name, value);
+    try
+      {
+        if (value instanceof SSLSessionBindingListener)
+          ((SSLSessionBindingListener) value).valueBound
+            (new SSLSessionBindingEvent(this, name));
+      }
+    catch (Exception x)
+      {
+      }
+  }
+  
+  public void removeValue(String name)
   {
-    values.put (name, value);
+    Object value = values.remove(name);
+    try
+      {
+        if (value instanceof SSLSessionBindingListener)
+          ((SSLSessionBindingListener) value).valueUnbound
+            (new SSLSessionBindingEvent(this, name));
+      }
+    catch (Exception x)
+      {
+      }   
   }
   
-  public void removeValue (String name)
+  public final boolean isTruncatedMac()
   {
-    values.remove (name);
+    return truncatedMac;
   }
 
-  public abstract void prepare (char[] password);
+  /**
+   * Prepare this session for serialization. Private data will be encrypted
+   * with the given password, and this object will then be ready to be
+   * serialized.
+   * 
+   * @param password The password to protect this session with.
+   * @throws SSLException If encrypting this session's private data fails.
+   */
+  public abstract void prepare (char[] password) throws SSLException;
+  
+  /**
+   * Repair this session's private data after deserialization. This method
+   * will decrypt this session's private data, and prepare the session for
+   * use in new SSL connections.
+   * 
+   * @param password The password to decrypt the private data with.
+   * @throws SSLException
+   */
+  public abstract void repair(char[] password) throws SSLException;
+  
+  /**
+   * Get the private data of this session. This method may only be called
+   * after first calling [EMAIL PROTECTED] #prepare(char[])}.
+   * 
+   * @return The sealed private data.
+   * @throws SSLException If the private data have not been sealed.
+   */
+  public abstract SealedObject privateData() throws SSLException;
+  
+  /**
+   * Set the private data of this session.
+   * @param data
+   * @throws SSLException
+   */
+  public abstract void setPrivateData(SealedObject data) throws SSLException;
 
   // Inner classes.
   // -------------------------------------------------------------------------
@@ -203,6 +291,7 @@
     // Fields.
     // -----------------------------------------------------------------------
 
+    static final long serialVersionUID = 7887036954666565936L;
     /** The ID itself. */
     private final byte[] id;
 
Index: gnu/javax/net/ssl/PrivateCredentials.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/gnu/javax/net/ssl/PrivateCredentials.java,v
retrieving revision 1.1.4.1
diff -u -r1.1.4.1 PrivateCredentials.java
--- gnu/javax/net/ssl/PrivateCredentials.java   2 Mar 2006 09:33:53 -0000       
1.1.4.1
+++ gnu/javax/net/ssl/PrivateCredentials.java   28 Jun 2006 23:15:40 -0000
@@ -51,6 +51,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.Security;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
@@ -95,16 +96,16 @@
   public static final String BEGIN_RSA = "-----BEGIN RSA PRIVATE KEY";
   public static final String END_RSA   = "-----END RSA PRIVATE KEY";
 
-  private List privateKeys;
-  private List certChains;
+  private List<PrivateKey> privateKeys;
+  private List<X509Certificate[]> certChains;
 
   // Constructor.
   // -------------------------------------------------------------------------
 
   public PrivateCredentials()
   {
-    privateKeys = new LinkedList();
-    certChains = new LinkedList();
+    privateKeys = new LinkedList<PrivateKey>();
+    certChains = new LinkedList<X509Certificate[]>();
   }
 
   // Instance methods.
@@ -115,7 +116,7 @@
            IOException, NoSuchAlgorithmException, WrongPaddingException
   {
     CertificateFactory cf = CertificateFactory.getInstance("X.509");
-    Collection certs = cf.generateCertificates(certChain);
+    Collection<? extends Certificate> certs = 
cf.generateCertificates(certChain);
     X509Certificate[] chain = (X509Certificate[]) certs.toArray(new 
X509Certificate[0]);
 
     String alg = null;
@@ -199,11 +200,12 @@
           (BigInteger) der.read().getValue(),  // d mod (q-1)
           (BigInteger) der.read().getValue()); // coefficient
       }
+
     privateKeys.add(kf.generatePrivate(spec));
     certChains.add(chain);
   }
 
-  public List getPrivateKeys()
+  public List<PrivateKey> getPrivateKeys()
   {
     if (isDestroyed())
       {
@@ -212,7 +214,7 @@
     return privateKeys;
   }
 
-  public List getCertChains()
+  public List<X509Certificate[]> getCertChains()
   {
     return certChains;
   }
Index: gnu/javax/net/ssl/AbstractSessionContext.java
===================================================================
RCS file: gnu/javax/net/ssl/AbstractSessionContext.java
diff -N gnu/javax/net/ssl/AbstractSessionContext.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ gnu/javax/net/ssl/AbstractSessionContext.java       1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,258 @@
+/* AbstractSessionContext -- stores SSL sessions, possibly persistently.
+   Copyright (C) 2006  Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version.  */
+
+
+package gnu.javax.net.ssl;
+
+import gnu.java.security.Requires;
+
+import gnu.javax.net.ssl.provider.SimpleSessionContext;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPermission;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * A skeletal implementation of [EMAIL PROTECTED] SSLSessionContext}. This 
class may
+ * be subclassed to add extended functionality to session contexts, such
+ * as by storing sessions in files on disk, or by sharing contexts
+ * across different JVM instances.
+ * 
+ * <p>In order to securely store sessions, along with private key data,
+ * the abstract methods [EMAIL PROTECTED] [EMAIL PROTECTED] #load(char[])} and 
[EMAIL PROTECTED] #store(char[])}
+ * come into play. When storing sessions, a session context implementation
+ * must pass this password to the [EMAIL PROTECTED] Session#prepare(char[])} 
method,
+ * before either writing the [EMAIL PROTECTED] java.io.Serializable} session 
to the
+ * underlying store, or getting the opaque [EMAIL PROTECTED] 
Session#privateData()}
+ * class from the session, and storing that.
+ * 
+ * <p>As a simple example, that writes sessions to some object output
+ * stream:
+ * 
+ * <pre>
+  char[] password = ...;
+  ObjectOutputStream out = ...;
+  ...
+  for (Session s : this)
+    {
+      s.prepare(password);
+      out.writeObject(s);
+    }</pre>
+ * 
+ * <p>The reverse must be done when deserializing sessions, by using the
+ * [EMAIL PROTECTED] Session#repair(char[])} method, possibly by first calling
+ * [EMAIL PROTECTED] Session#setPrivateData(java.io.Serializable)} with the 
read,
+ * opaque private data type. Thus an example of reading may be:
+ * 
+ * <pre>
+  char[] password = ...;
+  ObjectInputStream in = ...;
+  ...
+  while (hasMoreSessions(in))
+    {
+      Session s = (Session) in.readObject();
+      s.repair(password);
+      addToThisStore(s);
+    }</pre>
+ * 
+ * @author Casey Marshall ([EMAIL PROTECTED])
+ */
+public abstract class AbstractSessionContext implements SSLSessionContext
+{
+  protected long timeout;
+  private static Class<? extends AbstractSessionContext> 
+    implClass = SimpleSessionContext.class;
+
+  /**
+   * Create a new instance of a session context, according to the configured
+   * implementation class.
+   * 
+   * @return The new session context.
+   * @throws SSLException If an error occurs in creating the instance.
+   */
+  public static SSLSessionContext newInstance () throws SSLException
+  {
+    try
+      {
+        return implClass.newInstance();
+      }
+    catch (IllegalAccessException iae)
+      {
+        throw new SSLException(iae);
+      }
+    catch (InstantiationException ie)
+      {
+        throw new SSLException(ie);
+      }
+  }
+
+  /**
+   * Reconfigure this instance to use a different session context
+   * implementation.
+   * 
+   * <p><strong>Note:</strong> this method requires that the caller have
+   * [EMAIL PROTECTED] SSLPermission} with target
+   * <code>gnu.javax.net.ssl.AbstractSessionContext</code> and action
+   * <code>setImplClass</code>.
+   * 
+   * @param clazz The new implementation class.
+   * @throws SecurityException If the caller does not have permission to
+   *  change the session context.
+   */
+  @Requires(permissionClass = SSLPermission.class,
+            target = "gnu.javax.net.ssl.AbstractSessionContext",
+            action = "setImplClass")
+  public static synchronized void setImplClass
+    (Class<? extends AbstractSessionContext> clazz)
+    throws SecurityException
+  {
+    SecurityManager sm = System.getSecurityManager ();
+    if (sm != null)
+      sm.checkPermission(new 
SSLPermission("gnu.javax.net.ssl.AbstractSessionContext",
+                                           "setImplClass"));
+    implClass = clazz;
+  }
+
+  /**
+   * @param timeout The initial session timeout.
+   */
+  protected AbstractSessionContext (final int timeout)
+  {
+    setSessionTimeout(timeout);
+  }
+
+  /**
+   * Fetch a saved session by its ID. This method will (possibly)
+   * deserialize and return the SSL session with that ID, or null if
+   * the requested session does not exist, or has expired.
+   *
+   * <p>Subclasses implementing this class <strong>must not</strong>
+   * perform any blocking operations in this method. If any blocking
+   * behavior is required, it must be done in the [EMAIL PROTECTED] 
load(char[])}
+   * method.
+   *
+   * @param sessionId The ID of the session to get.
+   * @return The found session, or null if no such session was found,
+   * or if that session has expired.
+   */
+  public final SSLSession getSession (byte[] sessionId)
+  {
+    Session s = implGet (sessionId);
+    if (System.currentTimeMillis () - s.getLastAccessedTime () > timeout)
+      {
+        remove (sessionId);
+        return null;
+      }
+    return s;
+  }
+  
+  /**
+   * To be implemented by subclasses. Subclasses do not need to check
+   * timeouts in this method.
+   * 
+   * @param sessionId The session ID.
+   * @return The session, or <code>null</code> if the requested session
+   *  was not found.
+   */
+  protected abstract Session implGet (byte[] sessionId);
+
+  public int getSessionTimeout()
+  {
+    return (int) (timeout / 1000);
+  }
+  
+  /**
+   * Load this session store from the underlying media, if supported
+   * by the implementation.
+   *
+   * @param password The password that protects the sensitive data in
+   * this store.
+   * @throws SessionStoreException If reading this store fails, such
+   * as when an I/O exception occurs, or if the password is incorrect.
+   */
+  public abstract void load (char[] password) throws SessionStoreException;
+
+  /**
+   * Add a new session to the store. The underlying implementation
+   * will add the session to its store, possibly overwriting any
+   * existing session with the same ID.
+   *
+   * <p>Subclasses implementing this class <strong>must not</strong>
+   * perform any blocking operations in this method. If any blocking
+   * behavior is required, it must be done in the [EMAIL PROTECTED]
+   * #store(char[])} method.
+   *
+   * @param session The session to add.
+   * @throws NullPointerException If the argument is null.
+   */
+  public abstract void put (Session session);
+
+  /**
+   * Remove a session from this store.
+   *
+   * <p>Subclasses implementing this class <strong>must not</strong>
+   * perform any blocking operations in this method. If any blocking
+   * behavior is required, it must be done in the [EMAIL PROTECTED]
+   * #store(char[])} method.
+   *
+   * @param sessionId The ID of the session to remove.
+   */
+  public abstract void remove (byte[] sessionId);
+
+  /**
+   * 
+   */
+  public final void setSessionTimeout(int seconds)
+  {
+    if (timeout < 0)
+      throw new IllegalArgumentException("timeout may not be negative");
+    this.timeout = (long) seconds * 1000;
+  }
+  
+  /**
+   * Commit this session store to the underlying media. For session
+   * store implementations that support saving sessions across
+   * invocations of the JVM, this method will save any sessions that
+   * have not expired to some persistent media, so they may be loaded
+   * and used again later.
+   *
+   * @param password The password that will protect the sensitive data
+   * in this store.
+   */
+  public abstract void store (char[] password) throws SessionStoreException;
+}

Attachment: PGP.sig
Description: This is a digitally signed message part

Reply via email to