001: package ru.emdev.EmForge.util;
002:
003: /*
004: * RandomGUID
005: * @version 1.2.1 11/05/02
006: * @author Marc A. Mnich
007: *
008: * From www.JavaExchange.com, Open Software licensing
009: *
010: * 11/05/02 -- Performance enhancement from Mike Dubman.
011: * Moved InetAddr.getLocal to static block. Mike has measured
012: * a 10 fold improvement in run time.
013: * 01/29/02 -- Bug fix: Improper seeding of nonsecure Random object
014: * caused duplicate GUIDs to be produced. Random object
015: * is now only created once per JVM.
016: * 01/19/02 -- Modified random seeding and added new constructor
017: * to allow secure random feature.
018: * 01/14/02 -- Added random function seeding with JVM run time
019: *
020: */
021:
022: import java.net.*;
023: import java.util.*;
024: import java.security.*;
025:
026: /*
027: * In the multitude of java GUID generators, I found none that
028: * guaranteed randomness. GUIDs are guaranteed to be globally unique
029: * by using ethernet MACs, IP addresses, time elements, and sequential
030: * numbers. GUIDs are not expected to be random and most often are
031: * easy/possible to guess given a sample from a given generator.
032: * SQL Server, for example generates GUID that are unique but
033: * sequencial within a given instance.
034: *
035: * GUIDs can be used as security devices to hide things such as
036: * files within a filesystem where listings are unavailable (e.g. files
037: * that are served up from a Web server with indexing turned off).
038: * This may be desireable in cases where standard authentication is not
039: * appropriate. In this scenario, the RandomGUIDs are used as directories.
040: * Another example is the use of GUIDs for primary keys in a database
041: * where you want to ensure that the keys are secret. Random GUIDs can
042: * then be used in a URL to prevent hackers (or users) from accessing
043: * records by guessing or simply by incrementing sequential numbers.
044: *
045: * There are many other possiblities of using GUIDs in the realm of
046: * security and encryption where the element of randomness is important.
047: * This class was written for these purposes but can also be used as a
048: * general purpose GUID generator as well.
049: *
050: * RandomGUID generates truly random GUIDs by using the system's
051: * IP address (name/IP), system time in milliseconds (as an integer),
052: * and a very large random number joined together in a single String
053: * that is passed through an MD5 hash. The IP address and system time
054: * make the MD5 seed globally unique and the random number guarantees
055: * that the generated GUIDs will have no discernable pattern and
056: * cannot be guessed given any number of previously generated GUIDs.
057: * It is generally not possible to access the seed information (IP, time,
058: * random number) from the resulting GUIDs as the MD5 hash algorithm
059: * provides one way encryption.
060: *
061: * ----> Security of RandomGUID: <-----
062: * RandomGUID can be called one of two ways -- with the basic java Random
063: * number generator or a cryptographically strong random generator
064: * (SecureRandom). The choice is offered because the secure random
065: * generator takes about 3.5 times longer to generate its random numbers
066: * and this performance hit may not be worth the added security
067: * especially considering the basic generator is seeded with a
068: * cryptographically strong random seed.
069: *
070: * Seeding the basic generator in this way effectively decouples
071: * the random numbers from the time component making it virtually impossible
072: * to predict the random number component even if one had absolute knowledge
073: * of the System time. Thanks to Ashutosh Narhari for the suggestion
074: * of using the static method to prime the basic random generator.
075: *
076: * Using the secure random option, this class compies with the statistical
077: * random number generator tests specified in FIPS 140-2, Security
078: * Requirements for Cryptographic Modules, secition 4.9.1.
079: *
080: * I converted all the pieces of the seed to a String before handing
081: * it over to the MD5 hash so that you could print it out to make
082: * sure it contains the data you expect to see and to give a nice
083: * warm fuzzy. If you need better performance, you may want to stick
084: * to byte[] arrays.
085: *
086: * I believe that it is important that the algorithm for
087: * generating random GUIDs be open for inspection and modification.
088: * This class is free for all uses.
089: *
090: *
091: * - Marc
092: */
093:
094: public class RandomGUID extends Object {
095:
096: public String valueBeforeMD5 = "";
097: public String valueAfterMD5 = "";
098: private static Random myRand;
099: private static SecureRandom mySecureRand;
100:
101: private static String s_id;
102:
103: /*
104: * Static block to take care of one time secureRandom seed.
105: * It takes a few seconds to initialize SecureRandom. You might
106: * want to consider removing this static block or replacing
107: * it with a "time since first loaded" seed to reduce this time.
108: * This block will run only once per JVM instance.
109: */
110:
111: static {
112: mySecureRand = new SecureRandom();
113: long secureInitializer = mySecureRand.nextLong();
114: myRand = new Random(secureInitializer);
115: try {
116: s_id = InetAddress.getLocalHost().toString();
117: } catch (UnknownHostException e) {
118: e.printStackTrace();
119: }
120:
121: }
122:
123: /*
124: * Default constructor. With no specification of security option,
125: * this constructor defaults to lower security, high performance.
126: */
127: public RandomGUID() {
128: getRandomGUID(false);
129: }
130:
131: /*
132: * Constructor with security option. Setting secure true
133: * enables each random number generated to be cryptographically
134: * strong. Secure false defaults to the standard Random function seeded
135: * with a single cryptographically strong random number.
136: */
137: public RandomGUID(boolean secure) {
138: getRandomGUID(secure);
139: }
140:
141: /*
142: * Method to generate the random GUID
143: */
144: private void getRandomGUID(boolean secure) {
145: MessageDigest md5 = null;
146: StringBuffer sbValueBeforeMD5 = new StringBuffer();
147:
148: try {
149: md5 = MessageDigest.getInstance("MD5");
150: } catch (NoSuchAlgorithmException e) {
151: System.out.println("Error: " + e);
152: }
153:
154: try {
155: long time = System.currentTimeMillis();
156: long rand = 0;
157:
158: if (secure) {
159: rand = mySecureRand.nextLong();
160: } else {
161: rand = myRand.nextLong();
162: }
163:
164: // This StringBuffer can be a long as you need; the MD5
165: // hash will always return 128 bits. You can change
166: // the seed to include anything you want here.
167: // You could even stream a file through the MD5 making
168: // the odds of guessing it at least as great as that
169: // of guessing the contents of the file!
170: sbValueBeforeMD5.append(s_id);
171: sbValueBeforeMD5.append(":");
172: sbValueBeforeMD5.append(Long.toString(time));
173: sbValueBeforeMD5.append(":");
174: sbValueBeforeMD5.append(Long.toString(rand));
175:
176: valueBeforeMD5 = sbValueBeforeMD5.toString();
177: md5.update(valueBeforeMD5.getBytes());
178:
179: byte[] array = md5.digest();
180: StringBuffer sb = new StringBuffer();
181: for (int j = 0; j < array.length; ++j) {
182: int b = array[j] & 0xFF;
183: if (b < 0x10)
184: sb.append('0');
185: sb.append(Integer.toHexString(b));
186: }
187:
188: valueAfterMD5 = sb.toString();
189:
190: } catch (Exception e) {
191: System.out.println("Error:" + e);
192: }
193: }
194:
195: /*
196: * Convert to the standard format for GUID
197: * (Useful for SQL Server UniqueIdentifiers, etc.)
198: * Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6
199: */
200: public String toString() {
201: String raw = valueAfterMD5.toUpperCase();
202: StringBuffer sb = new StringBuffer();
203: sb.append(raw.substring(0, 8));
204: sb.append("-");
205: sb.append(raw.substring(8, 12));
206: sb.append("-");
207: sb.append(raw.substring(12, 16));
208: sb.append("-");
209: sb.append(raw.substring(16, 20));
210: sb.append("-");
211: sb.append(raw.substring(20));
212:
213: return sb.toString();
214: }
215:
216: /*
217: * Demonstraton and self test of class
218: */
219: public static void main(String args[]) {
220: for (int i = 0; i < 100; i++) {
221: RandomGUID myGUID = new RandomGUID();
222: System.out.println("Seeding String="
223: + myGUID.valueBeforeMD5);
224: System.out.println("rawGUID=" + myGUID.valueAfterMD5);
225: System.out.println("RandomGUID=" + myGUID.toString());
226: }
227: }
228: }
|