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