1 package com.panogenesis.util;
2 /*
3 * RandomGUID from http://www.javaexchange.com/aboutRandomGUID.html
4 * @version 1.2.1 11/05/02
5 * @author Marc A. Mnich
6 *
7 * From www.JavaExchange.com, Open Software licensing
8 *
9 * 11/05/02 -- Performance enhancement from Mike Dubman.
10 * Moved InetAddr.getLocal to static block. Mike has measured
11 * a 10 fold improvement in run time.
12 * 01/29/02 -- Bug fix: Improper seeding of nonsecure Random object
13 * caused duplicate GUIDs to be produced. Random object
14 * is now only created once per JVM.
15 * 01/19/02 -- Modified random seeding and added new constructor
16 * to allow secure random feature.
17 * 01/14/02 -- Added random function seeding with JVM run time
18 *
19 */
20
21 import java.net.InetAddress;
22 import java.net.UnknownHostException;
23 import java.security.MessageDigest;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.SecureRandom;
26 import java.util.Random;
27
28 /*
29 * In the multitude of java GUID generators, I found none that
30 * guaranteed randomness. GUIDs are guaranteed to be globally unique
31 * by using ethernet MACs, IP addresses, time elements, and sequential
32 * numbers. GUIDs are not expected to be random and most often are
33 * easy/possible to guess given a sample from a given generator.
34 * SQL Server, for example generates GUID that are unique but
35 * sequencial within a given instance.
36 *
37 * GUIDs can be used as security devices to hide things such as
38 * files within a filesystem where listings are unavailable (e.g. files
39 * that are served up from a Web server with indexing turned off).
40 * This may be desireable in cases where standard authentication is not
41 * appropriate. In this scenario, the RandomGUIDs are used as directories.
42 * Another example is the use of GUIDs for primary keys in a database
43 * where you want to ensure that the keys are secret. Random GUIDs can
44 * then be used in a URL to prevent hackers (or users) from accessing
45 * records by guessing or simply by incrementing sequential numbers.
46 *
47 * There are many other possiblities of using GUIDs in the realm of
48 * security and encryption where the element of randomness is important.
49 * This class was written for these purposes but can also be used as a
50 * general purpose GUID generator as well.
51 *
52 * RandomGUID generates truly random GUIDs by using the system's
53 * IP address (name/IP), system time in milliseconds (as an integer),
54 * and a very large random number joined together in a single String
55 * that is passed through an MD5 hash. The IP address and system time
56 * make the MD5 seed globally unique and the random number guarantees
57 * that the generated GUIDs will have no discernable pattern and
58 * cannot be guessed given any number of previously generated GUIDs.
59 * It is generally not possible to access the seed information (IP, time,
60 * random number) from the resulting GUIDs as the MD5 hash algorithm
61 * provides one way encryption.
62 *
63 * ----> Security of RandomGUID: <-----
64 * RandomGUID can be called one of two ways -- with the basic java Random
65 * number generator or a cryptographically strong random generator
66 * (SecureRandom). The choice is offered because the secure random
67 * generator takes about 3.5 times longer to generate its random numbers
68 * and this performance hit may not be worth the added security
69 * especially considering the basic generator is seeded with a
70 * cryptographically strong random seed.
71 *
72 * Seeding the basic generator in this way effectively decouples
73 * the random numbers from the time component making it virtually impossible
74 * to predict the random number component even if one had absolute knowledge
75 * of the System time. Thanks to Ashutosh Narhari for the suggestion
76 * of using the static method to prime the basic random generator.
77 *
78 * Using the secure random option, this class compies with the statistical
79 * random number generator tests specified in FIPS 140-2, Security
80 * Requirements for Cryptographic Modules, secition 4.9.1.
81 *
82 * I converted all the pieces of the seed to a String before handing
83 * it over to the MD5 hash so that you could print it out to make
84 * sure it contains the data you expect to see and to give a nice
85 * warm fuzzy. If you need better performance, you may want to stick
86 * to byte[] arrays.
87 *
88 * I believe that it is important that the algorithm for
89 * generating random GUIDs be open for inspection and modification.
90 * This class is free for all uses.
91 *
92 *
93 * - Marc
94 */
95
96 public class RandomGUID extends Object {
97
98 public String valueBeforeMD5 = "";
99 public String valueAfterMD5 = "";
100 private static Random myRand;
101 private static SecureRandom mySecureRand;
102
103 private static String s_id;
104
105 /*
106 * Static block to take care of one time secureRandom seed.
107 * It takes a few seconds to initialize SecureRandom. You might
108 * want to consider removing this static block or replacing
109 * it with a "time since first loaded" seed to reduce this time.
110 * This block will run only once per JVM instance.
111 */
112
113 static {
114 mySecureRand = new SecureRandom();
115 long secureInitializer = mySecureRand.nextLong();
116 myRand = new Random(secureInitializer);
117 try {
118 s_id = InetAddress.getLocalHost().toString();
119 } catch (UnknownHostException e) {
120 e.printStackTrace();
121 }
122
123 }
124
125
126 /*
127 * Default constructor. With no specification of security option,
128 * this constructor defaults to lower security, high performance.
129 */
130 public RandomGUID() {
131 getRandomGUID(false);
132 }
133
134 /*
135 * Constructor with security option. Setting secure true
136 * enables each random number generated to be cryptographically
137 * strong. Secure false defaults to the standard Random function seeded
138 * with a single cryptographically strong random number.
139 */
140 public RandomGUID(boolean secure) {
141 getRandomGUID(secure);
142 }
143
144 /*
145 * Method to generate the random GUID
146 */
147 private void getRandomGUID(boolean secure) {
148 MessageDigest md5 = null;
149 StringBuffer sbValueBeforeMD5 = new StringBuffer();
150
151 try {
152 md5 = MessageDigest.getInstance("MD5");
153 } catch (NoSuchAlgorithmException e) {
154 System.out.println("Error: " + e);
155 }
156
157 try {
158 long time = System.currentTimeMillis();
159 long rand = 0;
160
161 if (secure) {
162 rand = mySecureRand.nextLong();
163 } else {
164 rand = myRand.nextLong();
165 }
166
167 // This StringBuffer can be a long as you need; the MD5
168 // hash will always return 128 bits. You can change
169 // the seed to include anything you want here.
170 // You could even stream a file through the MD5 making
171 // the odds of guessing it at least as great as that
172 // of guessing the contents of the file!
173 sbValueBeforeMD5.append(s_id);
174 sbValueBeforeMD5.append(":");
175 sbValueBeforeMD5.append(Long.toString(time));
176 sbValueBeforeMD5.append(":");
177 sbValueBeforeMD5.append(Long.toString(rand));
178
179 valueBeforeMD5 = sbValueBeforeMD5.toString();
180 md5.update(valueBeforeMD5.getBytes());
181
182 byte[] array = md5.digest();
183 StringBuffer sb = new StringBuffer();
184 for (int j = 0; j < array.length; ++j) {
185 int b = array[j] & 0xFF;
186 if (b < 0x10) sb.append('0');
187 sb.append(Integer.toHexString(b));
188 }
189
190 valueAfterMD5 = sb.toString();
191
192 } catch (Exception e) {
193 System.out.println("Error:" + e);
194 }
195 }
196
197
198 /*
199 * Convert to the standard format for GUID
200 * (Useful for SQL Server UniqueIdentifiers, etc.)
201 * Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6
202 */
203 public String toString() {
204 String raw = valueAfterMD5.toUpperCase();
205 StringBuffer sb = new StringBuffer();
206 sb.append(raw.substring(0, 8));
207 sb.append("-");
208 sb.append(raw.substring(8, 12));
209 sb.append("-");
210 sb.append(raw.substring(12, 16));
211 sb.append("-");
212 sb.append(raw.substring(16, 20));
213 sb.append("-");
214 sb.append(raw.substring(20));
215
216 return sb.toString();
217 }
218
219 /*
220 * Demonstraton and self test of class
221 */
222 public static void main(String args[]) {
223 for (int i=0; i< 100; i++) {
224 RandomGUID myGUID = new RandomGUID();
225 System.out.println("Seeding String=" + myGUID.valueBeforeMD5);
226 System.out.println("rawGUID=" + myGUID.valueAfterMD5);
227 System.out.println("RandomGUID=" + myGUID.toString());
228 }
229 }
230 }