001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.util;
007:
008: import java.io.ByteArrayOutputStream;
009: import java.io.DataOutputStream;
010: import java.io.IOException;
011: import java.lang.reflect.Method;
012: import java.net.InetAddress;
013: import java.security.NoSuchAlgorithmException;
014: import java.security.SecureRandom;
015: import java.util.Random;
016:
017: /**
018: * Utility class that supports random and secure random functions. In some
019: * systems SecureRandom initialization is very slow, a workaround is implemented
020: * here.
021: */
022: public class RandomUtils {
023:
024: private static SecureRandom secureRandom;
025: private static Random random = new Random();
026: private static volatile boolean seeded;
027:
028: private static synchronized SecureRandom getSecureRandom() {
029: if (secureRandom != null) {
030: return secureRandom;
031: }
032: // Workaround for SecureRandom problem as described in
033: // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6202721
034: // Can not do that in a static initializer block, because
035: // threads are not started after the initializer block exits
036: try {
037: secureRandom = SecureRandom.getInstance("SHA1PRNG");
038: // On some systems, secureRandom.generateSeed() is very slow.
039: // In this case it is initialized using our own seed implementation
040: // and afterwards (in the thread) using the regular algorithm.
041: Runnable runnable = new Runnable() {
042: public void run() {
043: try {
044: SecureRandom sr = SecureRandom
045: .getInstance("SHA1PRNG");
046: byte[] seed = sr.generateSeed(20);
047: synchronized (secureRandom) {
048: secureRandom.setSeed(seed);
049: seeded = true;
050: }
051: } catch (NoSuchAlgorithmException e) {
052: warn("SecureRandom", e);
053: }
054: }
055: };
056: Thread t = new Thread(runnable);
057: // let the process terminate even if generating the seed is really slow
058: t.setDaemon(true);
059: t.start();
060: Thread.yield();
061: try {
062: // normally, generateSeed takes less than 200 ms
063: t.join(400);
064: } catch (InterruptedException e) {
065: warn("InterruptedException", e);
066: }
067: if (!seeded) {
068: byte[] seed = generateAlternativeSeed();
069: // this never reduces randomness
070: synchronized (secureRandom) {
071: secureRandom.setSeed(seed);
072: }
073: }
074: } catch (NoSuchAlgorithmException e) {
075: warn("SecureRandom", e);
076: secureRandom = new SecureRandom();
077: }
078: return secureRandom;
079: }
080:
081: private static byte[] generateAlternativeSeed() {
082: try {
083: ByteArrayOutputStream bout = new ByteArrayOutputStream();
084: DataOutputStream out = new DataOutputStream(bout);
085:
086: // milliseconds
087: out.writeLong(System.currentTimeMillis());
088:
089: // nanoseconds if available
090: try {
091: Method m = System.class.getMethod("nanoTime",
092: new Class[0]);
093: if (m != null) {
094: Object o = m
095: .invoke(null, (java.lang.Object[]) null);
096: out.writeUTF(o.toString());
097: }
098: } catch (Exception e) {
099: // nanoTime not found, this is ok (only exists for JDK 1.5 and higher)
100: }
101:
102: // memory
103: out.writeInt(new Object().hashCode());
104: Runtime runtime = Runtime.getRuntime();
105: out.writeLong(runtime.freeMemory());
106: out.writeLong(runtime.maxMemory());
107: out.writeLong(runtime.totalMemory());
108:
109: // environment
110: try {
111: out.writeUTF(System.getProperties().toString());
112: } catch (Exception e) {
113: warn("generateAlternativeSeed", e);
114: }
115:
116: // host name and ip addresses (if any)
117: try {
118: String hostName = InetAddress.getLocalHost()
119: .getHostName();
120: out.writeUTF(hostName);
121: InetAddress[] list = InetAddress.getAllByName(hostName);
122: for (int i = 0; i < list.length; i++) {
123: out.write(list[i].getAddress());
124: }
125: } catch (Exception e) {
126: // on some system, InetAddress.getLocalHost() doesn't work
127: // for some reason (incorrect configuration)
128: }
129:
130: // timing (a second thread is already running)
131: for (int j = 0; j < 16; j++) {
132: int i = 0;
133: long end = System.currentTimeMillis();
134: while (end == System.currentTimeMillis()) {
135: i++;
136: }
137: out.writeInt(i);
138: }
139:
140: out.close();
141: return bout.toByteArray();
142: } catch (IOException e) {
143: warn("generateAlternativeSeed", e);
144: return new byte[1];
145: }
146: }
147:
148: public static long getSecureLong() {
149: SecureRandom sr = getSecureRandom();
150: synchronized (sr) {
151: return sr.nextLong();
152: }
153: }
154:
155: public static byte[] getSecureBytes(int len) {
156: if (len <= 0) {
157: len = 1;
158: }
159: byte[] buff = new byte[len];
160: SecureRandom sr = getSecureRandom();
161: synchronized (sr) {
162: sr.nextBytes(buff);
163: }
164: return buff;
165: }
166:
167: public static int nextInt(int max) {
168: return random.nextInt(max);
169: }
170:
171: private static void warn(String s, Throwable t) {
172: // not a fatal problem, but maybe reduced security
173: System.out.println("RandomUtils warning: " + s);
174: if (t != null) {
175: t.printStackTrace();
176: }
177: }
178:
179: }
|