KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > web > tomcat > tc6 > session > SessionIDGenerator


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.web.tomcat.tc6.session;
23
24 import java.util.Collection JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Random JavaDoc;
27 import java.security.MessageDigest JavaDoc;
28 import java.security.NoSuchAlgorithmException JavaDoc;
29 import java.security.SecureRandom JavaDoc;
30
31 import org.jboss.logging.Logger;
32
33 /**
34  * Unique session id generator
35  *
36  * @author Ben Wang
37  */

38 public class SessionIDGenerator
39 {
40    protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
41
protected final static String JavaDoc SESSION_ID_HASH_ALGORITHM = "MD5";
42    protected final static String JavaDoc SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
43    protected final static String JavaDoc SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
44    protected Logger log = Logger.getLogger(SessionIDGenerator.class);
45
46    protected MessageDigest JavaDoc digest = null;
47    protected Random JavaDoc random = null;
48    protected static SessionIDGenerator s_;
49    
50    protected String JavaDoc sessionIdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*";
51
52    public static SessionIDGenerator getInstance()
53    {
54       if (s_ == null) s_ = new SessionIDGenerator();
55       return s_;
56    }
57
58    /**
59     * The SessionIdAlphabet is the set of characters used to create a session Id
60     */

61    public void setSessionIdAlphabet(String JavaDoc sessionIdAlphabet)
62    {
63       if (sessionIdAlphabet.length() != 65) {
64          throw new IllegalArgumentException JavaDoc("SessionIdAlphabet must be exactly 65 characters long");
65       }
66
67       checkDuplicateChars(sessionIdAlphabet);
68
69       this.sessionIdAlphabet = sessionIdAlphabet;
70    }
71
72    protected void checkDuplicateChars(String JavaDoc sessionIdAlphabet) {
73       char[] alphabet = sessionIdAlphabet.toCharArray();
74       for (int i=0; i < alphabet.length; i++) {
75           if (!uniqueChar(alphabet[i], sessionIdAlphabet)) {
76               throw new IllegalArgumentException JavaDoc("All chars in SessionIdAlphabet must be unique");
77           }
78       }
79    }
80       
81    // does a character appear in the String once and only once?
82
protected boolean uniqueChar(char c, String JavaDoc s) {
83        int firstIndex = s.indexOf(c);
84        if (firstIndex == -1) return false;
85        return s.indexOf(c, firstIndex + 1) == -1;
86    }
87
88    /**
89     * The SessionIdAlphabet is the set of characters used to create a session Id
90     */

91    public String JavaDoc getSessionIdAlphabet() {
92       return this.sessionIdAlphabet;
93    }
94    
95    public synchronized String JavaDoc getSessionId()
96    {
97       String JavaDoc id = generateSessionId();
98       if (log.isDebugEnabled())
99          log.debug("getSessionId called: " + id);
100       return id;
101    }
102
103
104    /**
105     * Generate a session-id that is not guessable
106     *
107     * @return generated session-id
108     */

109    protected synchronized String JavaDoc generateSessionId()
110    {
111       if (this.digest == null)
112       {
113          this.digest = getDigest();
114       }
115
116       if (this.random == null)
117       {
118          this.random = getRandom();
119       }
120
121       byte[] bytes = new byte[SESSION_ID_BYTES];
122
123       // get random bytes
124
this.random.nextBytes(bytes);
125
126       // Hash the random bytes
127
bytes = this.digest.digest(bytes);
128
129       // Render the result as a String of hexadecimal digits
130
return encode(bytes);
131    }
132
133    /**
134     * Encode the bytes into a String with a slightly modified Base64-algorithm
135     * This code was written by Kevin Kelley <kelley@ruralnet.net>
136     * and adapted by Thomas Peuss <jboss@peuss.de>
137     *
138     * @param data The bytes you want to encode
139     * @return the encoded String
140     */

141    protected String JavaDoc encode(byte[] data)
142    {
143       char[] out = new char[((data.length + 2) / 3) * 4];
144       char[] alphabet = this.sessionIdAlphabet.toCharArray();
145
146       //
147
// 3 bytes encode to 4 chars. Output is always an even
148
// multiple of 4 characters.
149
//
150
for (int i = 0, index = 0; i < data.length; i += 3, index += 4)
151       {
152          boolean quad = false;
153          boolean trip = false;
154
155          int val = (0xFF & (int) data[i]);
156          val <<= 8;
157          if ((i + 1) < data.length)
158          {
159             val |= (0xFF & (int) data[i + 1]);
160             trip = true;
161          }
162          val <<= 8;
163          if ((i + 2) < data.length)
164          {
165             val |= (0xFF & (int) data[i + 2]);
166             quad = true;
167          }
168          out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
169          val >>= 6;
170          out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
171          val >>= 6;
172          out[index + 1] = alphabet[val & 0x3F];
173          val >>= 6;
174          out[index + 0] = alphabet[val & 0x3F];
175       }
176       return new String JavaDoc(out);
177    }
178
179    /**
180     * get a random-number generator
181     *
182     * @return a random-number generator
183     */

184    protected synchronized Random JavaDoc getRandom()
185    {
186       long seed;
187       Random JavaDoc random = null;
188
189       // Mix up the seed a bit
190
seed = System.currentTimeMillis();
191       seed ^= Runtime.getRuntime().freeMemory();
192
193       try
194       {
195          random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
196       }
197       catch (NoSuchAlgorithmException JavaDoc e)
198       {
199          try
200          {
201             random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
202          }
203          catch (NoSuchAlgorithmException JavaDoc e_alt)
204          {
205             log.error("Could not generate SecureRandom for session-id randomness", e);
206             log.error("Could not generate SecureRandom for session-id randomness", e_alt);
207             return null;
208          }
209       }
210
211       // set the generated seed for this PRNG
212
random.setSeed(seed);
213
214       return random;
215    }
216
217    /**
218     * get a MessageDigest hash-generator
219     *
220     * @return a hash generator
221     */

222    protected synchronized MessageDigest JavaDoc getDigest()
223    {
224       MessageDigest JavaDoc digest = null;
225
226       try
227       {
228          digest = MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
229       }
230       catch (NoSuchAlgorithmException JavaDoc e)
231       {
232          log.error("Could not generate MessageDigest for session-id hashing", e);
233          return null;
234       }
235
236       return digest;
237    }
238
239 }
240
Popular Tags