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