001: /*
002: * Copyright 2005 Joe Walker
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.util;
017:
018: import java.security.MessageDigest;
019: import java.security.NoSuchAlgorithmException;
020: import java.security.SecureRandom;
021: import java.util.Random;
022:
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.commons.logging.Log;
025:
026: /**
027: * Code to generate page ids.
028: * IdGenerators are expensive to setup so it is suggested that you share
029: * instances wherever possible. This action will also enhance security.
030: * Much of this code is adapted from org.apache.catalina.session.ManagerBase.
031: * Specifically Revision 1.37 which has been unchanged in the past 18 months.
032: * I have taken out the /dev/urandom stuff and simplified things to the point
033: * where we can audit it to work out what might be broken.
034: * @author Joe Walker [joe at getahead dot ltd dot uk]
035: */
036: public class IdGenerator {
037: /**
038: * Seed the random number
039: */
040: public IdGenerator() {
041: // Start with the current system time as a seed
042: long seed = System.currentTimeMillis();
043:
044: // Also throw in the system identifier for 'this' from toString
045: char[] entropy = toString().toCharArray();
046: for (int i = 0; i < entropy.length; i++) {
047: //noinspection IntegerMultiplicationImplicitCastToLong
048: long update = ((byte) entropy[i]) << ((i % 8) * 8);
049: seed ^= update;
050: }
051:
052: random.setSeed(seed);
053: }
054:
055: /**
056: * Generate and return a new session identifier.
057: * @param length The number of bytes to generate
058: * @return A new page id string
059: */
060: public synchronized String generateId(int length) {
061: byte[] buffer = new byte[length];
062:
063: // Render the result as a String of hexadecimal digits
064: StringBuffer reply = new StringBuffer();
065:
066: int resultLenBytes = 0;
067: while (resultLenBytes < length) {
068: random.nextBytes(buffer);
069: buffer = getDigest().digest(buffer);
070:
071: for (int j = 0; j < buffer.length
072: && resultLenBytes < length; j++) {
073: byte b1 = (byte) ((buffer[j] & 0xf0) >> 4);
074: if (b1 < 10) {
075: reply.append((char) ('0' + b1));
076: } else {
077: reply.append((char) ('A' + (b1 - 10)));
078: }
079:
080: byte b2 = (byte) (buffer[j] & 0x0f);
081: if (b2 < 10) {
082: reply.append((char) ('0' + b2));
083: } else {
084: reply.append((char) ('A' + (b2 - 10)));
085: }
086:
087: resultLenBytes++;
088: }
089: }
090:
091: return reply.toString();
092: }
093:
094: /**
095: * @return the algorithm
096: */
097: public synchronized String getAlgorithm() {
098: return algorithm;
099: }
100:
101: /**
102: * @param algorithm the algorithm to set
103: */
104: public synchronized void setAlgorithm(String algorithm) {
105: this .algorithm = algorithm;
106: digest = null;
107: }
108:
109: /**
110: * Return the MessageDigest object to be used for calculating
111: * session identifiers. If none has been created yet, initialize
112: * one the first time this method is called.
113: * @return The hashing algorithm
114: */
115: private MessageDigest getDigest() {
116: if (digest == null) {
117: try {
118: digest = MessageDigest.getInstance(algorithm);
119: } catch (NoSuchAlgorithmException ex) {
120: try {
121: digest = MessageDigest
122: .getInstance(DEFAULT_ALGORITHM);
123: } catch (NoSuchAlgorithmException ex2) {
124: digest = null;
125: throw new IllegalStateException(
126: "No algorithms for IdGenerator");
127: }
128: }
129:
130: log.debug("Using MessageDigest: " + digest.getAlgorithm());
131: }
132:
133: return digest;
134: }
135:
136: /* (non-Javadoc)
137: * @see java.lang.Object#toString()
138: */
139: @SuppressWarnings({"EmptyMethod"})
140: @Override
141: public final String toString() {
142: // This is to make the point that we need toString to return something
143: // that includes some sort of system identifier as does the default.
144: // Don't change this unless you really know what you are doing.
145: return super .toString();
146: }
147:
148: /**
149: * The default message digest algorithm to use if we cannot use
150: * the requested one.
151: */
152: protected static final String DEFAULT_ALGORITHM = "MD5";
153:
154: /**
155: * The message digest algorithm to be used when generating session
156: * identifiers. This must be an algorithm supported by the
157: * <code>java.security.MessageDigest</code> class on your platform.
158: */
159: private String algorithm = DEFAULT_ALGORITHM;
160:
161: /**
162: * A random number generator to use when generating session identifiers.
163: */
164: private Random random = new SecureRandom();
165:
166: /**
167: * Return the MessageDigest implementation to be used when creating session
168: * identifiers.
169: */
170: private MessageDigest digest = null;
171:
172: /**
173: * The log stream
174: */
175: private static final Log log = LogFactory.getLog(IdGenerator.class);
176: }
|