001: // ========================================================================
002: // $Id: GUIDGenerator.java,v 1.4 2004/05/09 20:30:47 gregwilkins Exp $
003: // Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
004: // ------------------------------------------------------------------------
005: // Licensed under the Apache License, Version 2.0 (the "License");
006: // you may not use this file except in compliance with the License.
007: // You may obtain a copy of the License at
008: // http://www.apache.org/licenses/LICENSE-2.0
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014: // ========================================================================
015:
016: package org.mortbay.j2ee.session;
017:
018: // shamelessly distilled from
019: // org.jboss.ha.httpsession.server.ClusteredHTTPSessionService
020: // written by : sacha.labourey@cogito-info.ch
021:
022: import java.security.MessageDigest;
023: import java.security.NoSuchAlgorithmException;
024: import java.security.SecureRandom;
025: import java.util.Random;
026:
027: import javax.servlet.http.HttpServletRequest;
028:
029: import org.jboss.logging.Logger;
030:
031: public class GUIDGenerator implements IdGenerator {
032: protected static final Logger _log = Logger
033: .getLogger(GUIDGenerator.class);
034:
035: protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for
036: // the session-id
037:
038: protected final static String SESSION_ID_HASH_ALGORITHM = "MD5";
039:
040: protected final static String SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
041:
042: protected final static String SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
043:
044: protected MessageDigest _digest = null;
045:
046: protected Random _random = null;
047:
048: /**
049: * Generate a session-id that is not guessable
050: *
051: * @return generated session-id
052: */
053: public synchronized String nextId(HttpServletRequest request) {
054: if (_digest == null) {
055: _digest = getDigest();
056: }
057:
058: if (_random == null) {
059: _random = getRandom();
060: }
061:
062: byte[] bytes = new byte[SESSION_ID_BYTES];
063:
064: // get random bytes
065: _random.nextBytes(bytes);
066:
067: // Hash the random bytes
068: bytes = _digest.digest(bytes);
069:
070: // Render the result as a String of hexadecimal digits
071: return encode(bytes);
072: }
073:
074: /**
075: * Encode the bytes into a String with a slightly modified Base64-algorithm
076: * This code was written by Kevin Kelley <kelley@ruralnet.net> and adapted
077: * by Thomas Peuss <jboss@peuss.de>
078: *
079: * @param data The bytes you want to encode
080: * @return the encoded String
081: */
082: protected String encode(byte[] data) {
083: char[] out = new char[((data.length + 2) / 3) * 4];
084: char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*"
085: .toCharArray();
086:
087: //
088: // 3 bytes encode to 4 chars. Output is always an even
089: // multiple of 4 characters.
090: //
091: for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
092: boolean quad = false;
093: boolean trip = false;
094:
095: int val = (0xFF & (int) data[i]);
096: val <<= 8;
097: if ((i + 1) < data.length) {
098: val |= (0xFF & (int) data[i + 1]);
099: trip = true;
100: }
101: val <<= 8;
102: if ((i + 2) < data.length) {
103: val |= (0xFF & (int) data[i + 2]);
104: quad = true;
105: }
106: out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
107: val >>= 6;
108: out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
109: val >>= 6;
110: out[index + 1] = alphabet[val & 0x3F];
111: val >>= 6;
112: out[index + 0] = alphabet[val & 0x3F];
113: }
114: return new String(out);
115: }
116:
117: /**
118: * get a random-number generator
119: *
120: * @return a random-number generator
121: */
122: protected synchronized Random getRandom() {
123: long seed;
124: Random random = null;
125:
126: // Mix up the seed a bit
127: seed = System.currentTimeMillis();
128: seed ^= Runtime.getRuntime().freeMemory();
129:
130: try {
131: random = SecureRandom
132: .getInstance(SESSION_ID_RANDOM_ALGORITHM);
133: } catch (NoSuchAlgorithmException e) {
134: try {
135: random = SecureRandom
136: .getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
137: } catch (NoSuchAlgorithmException e_alt) {
138: _log
139: .error(
140: "Could not generate SecureRandom for session-id randomness",
141: e);
142: _log
143: .error(
144: "Could not generate SecureRandom for session-id randomness",
145: e_alt);
146: return null;
147: }
148: }
149:
150: // set the generated seed for this PRNG
151: random.setSeed(seed);
152:
153: return random;
154: }
155:
156: /**
157: get a MessageDigest hash-generator
158: @return a hash generator
159: */
160: protected synchronized MessageDigest getDigest() {
161: MessageDigest digest = null;
162:
163: try {
164: digest = MessageDigest
165: .getInstance(SESSION_ID_HASH_ALGORITHM);
166: } catch (NoSuchAlgorithmException e) {
167: _log
168: .error(
169: "Could not generate MessageDigest for session-id hashing",
170: e);
171: return null;
172: }
173:
174: return digest;
175: }
176:
177: public synchronized Object clone() {
178: try {
179: return super .clone();
180: } catch (CloneNotSupportedException e) {
181: _log.warn("could not clone IdGenerator", e);
182: return null;
183: }
184: }
185: }
|