/*
* Copyright 2005 Joe Walker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Revised from dwr
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
/**
* Code to generate page ids.
* IdGenerators are expensive to setup so it is suggested that you share
* instances wherever possible. This action will also enhance security.
* Much of this code is adapted from org.apache.catalina.session.ManagerBase.
* Specifically Revision 1.37 which has been unchanged in the past 18 months.
* I have taken out the /dev/urandom stuff and simplified things to the point
* where we can audit it to work out what might be broken.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public class IdGenerator
{
/**
* Seed the random number
*/
public IdGenerator()
{
// Start with the current system time as a seed
long seed = System.currentTimeMillis();
// Also throw in the system identifier for 'this' from toString
char[] entropy = toString().toCharArray();
for (int i = 0; i < entropy.length; i++)
{
//noinspection IntegerMultiplicationImplicitCastToLong
long update = ((byte) entropy[i]) << ((i % 8) * 8);
seed ^= update;
}
random.setSeed(seed);
}
/**
* Generate and return a new session identifier.
* @param length The number of bytes to generate
* @return A new page id string
*/
public synchronized String generateId(int length)
{
byte[] buffer = new byte[length];
// Render the result as a String of hexadecimal digits
StringBuffer reply = new StringBuffer();
int resultLenBytes = 0;
while (resultLenBytes < length)
{
random.nextBytes(buffer);
buffer = getDigest().digest(buffer);
for (int j = 0; j < buffer.length && resultLenBytes < length; j++)
{
byte b1 = (byte) ((buffer[j] & 0xf0) >> 4);
if (b1 < 10)
{
reply.append((char) ('0' + b1));
}
else
{
reply.append((char) ('A' + (b1 - 10)));
}
byte b2 = (byte) (buffer[j] & 0x0f);
if (b2 < 10)
{
reply.append((char) ('0' + b2));
}
else
{
reply.append((char) ('A' + (b2 - 10)));
}
resultLenBytes++;
}
}
return reply.toString();
}
/**
* @return the algorithm
*/
public synchronized String getAlgorithm()
{
return algorithm;
}
/**
* @param algorithm the algorithm to set
*/
public synchronized void setAlgorithm(String algorithm)
{
this.algorithm = algorithm;
digest = null;
}
/**
* Return the MessageDigest object to be used for calculating
* session identifiers. If none has been created yet, initialize
* one the first time this method is called.
* @return The hashing algorithm
*/
private MessageDigest getDigest()
{
if (digest == null)
{
try
{
digest = MessageDigest.getInstance(algorithm);
}
catch (NoSuchAlgorithmException ex)
{
try
{
digest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
}
catch (NoSuchAlgorithmException ex2)
{
digest = null;
throw new IllegalStateException("No algorithms for IdGenerator");
}
}
System.out.println("Using MessageDigest: " + digest.getAlgorithm());
}
return digest;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@SuppressWarnings({"EmptyMethod"})
@Override
public final String toString()
{
// This is to make the point that we need toString to return something
// that includes some sort of system identifier as does the default.
// Don't change this unless you really know what you are doing.
return super.toString();
}
/**
* The default message digest algorithm to use if we cannot use
* the requested one.
*/
protected static final String DEFAULT_ALGORITHM = "MD5";
/**
* The message digest algorithm to be used when generating session
* identifiers. This must be an algorithm supported by the
* <code>java.security.MessageDigest</code> class on your platform.
*/
private String algorithm = DEFAULT_ALGORITHM;
/**
* A random number generator to use when generating session identifiers.
*/
private Random random = new SecureRandom();
/**
* Return the MessageDigest implementation to be used when creating session
* identifiers.
*/
private MessageDigest digest = null;
}