View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
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  package org.apache.juddi.uuidgen;
17  
18  import java.math.BigInteger;
19  import java.security.SecureRandom;
20  import java.util.Random;
21  
22  /***
23   * Used to create new universally unique identifiers or UUID's (sometimes called
24   * GUID's).  UDDI UUID's are allways formmated according to DCE UUID conventions.
25   *
26   * @author Maarten Coene
27   * @author Steve Viens (sviens@apache.org)
28   */
29  public class SecureUUIDGen implements UUIDGen
30  {
31    private static final BigInteger COUNT_START = new BigInteger("-12219292800000");  // 15 October 1582
32    private static final int CLOCK_SEQUENCE = (new Random()).nextInt(16384);
33  
34    /***
35     *
36     */
37    public String uuidgen()
38    {
39      return nextUUID();
40    }
41  
42    /***
43     *
44     */
45    public String[] uuidgen(int nmbr)
46    {
47      String[] uuids = new String[nmbr];
48  
49      for (int i=0; i<uuids.length; i++)
50        uuids[i] = nextUUID();
51  
52      return uuids;
53    }
54  
55    /***
56     * Creates a new UUID. The algorithm used is described by The Open Group.
57     * See <a href="http://www.opengroup.org/onlinepubs/009629399/apdxa.htm">
58     * Universal Unique Identifier</a> for more details.
59     * <p>
60     * Due to a lack of functionality in Java, a part of the UUID is a secure
61     * random. This results in a long processing time when this method is called
62     * for the first time.
63     */
64    protected String nextUUID()
65    {
66        // the count of 100-nanosecond intervals since 00:00:00.00 15 October 1582
67        BigInteger count;
68  
69        // the number of milliseconds since 1 January 1970
70        BigInteger current = BigInteger.valueOf(System.currentTimeMillis());
71  
72        // the number of milliseconds since 15 October 1582
73        BigInteger countMillis = current.subtract(COUNT_START);
74  
75        // the result
76        count = countMillis.multiply(BigInteger.valueOf(10000));
77  
78        String bitString = count.toString(2);
79        if (bitString.length() < 60) {
80            int nbExtraZeros = 60 - bitString.length();
81            String extraZeros = new String();
82            for (int i = 0; i < nbExtraZeros; i++)
83                extraZeros = extraZeros.concat("0");
84  
85            bitString = extraZeros.concat(bitString);
86        }
87  
88        byte[] bits = bitString.getBytes();
89  
90        // the time_low field
91        byte[] time_low = new byte[32];
92        for (int i = 0; i < 32; i++)
93            time_low[i] = bits[bits.length - i - 1];
94  
95        // the time_mid field
96        byte[] time_mid = new byte[16];
97        for (int i = 0; i < 16; i++)
98            time_mid[i] = bits[bits.length - 32 - i - 1];
99  
100       // the time_hi_and_version field
101       byte[] time_hi_and_version = new byte[16];
102       for (int i = 0; i < 12; i++)
103           time_hi_and_version[i] = bits[bits.length - 48 - i - 1];
104 
105       time_hi_and_version[12] = ((new String("1")).getBytes())[0];
106       time_hi_and_version[13] = ((new String("0")).getBytes())[0];
107       time_hi_and_version[14] = ((new String("0")).getBytes())[0];
108       time_hi_and_version[15] = ((new String("0")).getBytes())[0];
109 
110       // the clock_seq_low field
111       BigInteger clockSequence = BigInteger.valueOf(CLOCK_SEQUENCE);
112       String clockString = clockSequence.toString(2);
113       if (clockString.length() < 14) {
114           int nbExtraZeros = 14 - bitString.length();
115           String extraZeros = new String();
116           for (int i = 0; i < nbExtraZeros; i++)
117               extraZeros = extraZeros.concat("0");
118 
119           clockString = extraZeros.concat(bitString);
120       }
121 
122       byte[] clock_bits = clockString.getBytes();
123       byte[] clock_seq_low = new byte[8];
124       for (int i = 0; i < 8; i++)
125           clock_seq_low[i] = clock_bits[clock_bits.length - i - 1];
126 
127       // the clock_seq_hi_and_reserved
128       byte[] clock_seq_hi_and_reserved = new byte[8];
129       for (int i = 0; i < 6; i++)
130           clock_seq_hi_and_reserved[i] = clock_bits[clock_bits.length - 8 - i - 1];
131 
132       clock_seq_hi_and_reserved[6] = ((new String("0")).getBytes())[0];
133       clock_seq_hi_and_reserved[7] = ((new String("1")).getBytes())[0];
134 
135       String timeLow = Long.toHexString((new BigInteger(new String(reverseArray(time_low)), 2)).longValue());
136       if (timeLow.length() < 8) {
137           int nbExtraZeros = 8 - timeLow.length();
138           String extraZeros = new String();
139           for (int i = 0; i < nbExtraZeros; i++)
140               extraZeros = extraZeros.concat("0");
141 
142           timeLow = extraZeros.concat(timeLow);
143       }
144 
145       String timeMid = Long.toHexString((new BigInteger(new String(reverseArray(time_mid)), 2)).longValue());
146       if (timeMid.length() < 4) {
147           int nbExtraZeros = 4 - timeMid.length();
148           String extraZeros = new String();
149           for (int i = 0; i < nbExtraZeros; i++)
150               extraZeros = extraZeros.concat("0");
151           timeMid = extraZeros.concat(timeMid);
152       }
153 
154       String timeHiAndVersion = Long.toHexString((new BigInteger(new String(reverseArray(time_hi_and_version)), 2)).longValue());
155       if (timeHiAndVersion.length() < 4) {
156           int nbExtraZeros = 4 - timeHiAndVersion.length();
157           String extraZeros = new String();
158           for (int i = 0; i < nbExtraZeros; i++)
159               extraZeros = extraZeros.concat("0");
160 
161           timeHiAndVersion = extraZeros.concat(timeHiAndVersion);
162       }
163 
164       String clockSeqHiAndReserved = Long.toHexString((new BigInteger(new String(reverseArray(clock_seq_hi_and_reserved)), 2)).longValue());
165       if (clockSeqHiAndReserved.length() < 2) {
166           int nbExtraZeros = 2 - clockSeqHiAndReserved.length();
167           String extraZeros = new String();
168           for (int i = 0; i < nbExtraZeros; i++)
169               extraZeros = extraZeros.concat("0");
170 
171           clockSeqHiAndReserved = extraZeros.concat(clockSeqHiAndReserved);
172       }
173 
174       String clockSeqLow = Long.toHexString((new BigInteger(new String(reverseArray(clock_seq_low)), 2)).longValue());
175       if (clockSeqLow.length() < 2) {
176           int nbExtraZeros = 2 - clockSeqLow.length();
177           String extraZeros = new String();
178           for (int i = 0; i < nbExtraZeros; i++)
179               extraZeros = extraZeros.concat("0");
180 
181           clockSeqLow = extraZeros.concat(clockSeqLow);
182       }
183 
184       // problem: the node should be the IEEE 802 ethernet address, but can not
185       // be retrieved in Java yet.
186       // see bug ID 4173528
187       // workaround (also suggested in bug ID 4173528)
188       // If a system wants to generate UUIDs but has no IEE 802 compliant
189       // network card or other source of IEEE 802 addresses, then this section
190       // describes how to generate one.
191       // The ideal solution is to obtain a 47 bit cryptographic quality random
192       // number, and use it as the low 47 bits of the node ID, with the most
193       // significant bit of the first octet of the node ID set to 1. This bit
194       // is the unicast/multicast bit, which will never be set in IEEE 802
195       // addresses obtained from network cards; hence, there can never be a
196       // conflict between UUIDs generated by machines with and without network
197       // cards.
198       Random secureRandom = null;
199       try {
200           secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
201       } catch (Exception e) {
202           secureRandom = new Random();
203       }
204 
205       long nodeValue = secureRandom.nextLong();
206       nodeValue = Math.abs(nodeValue);
207       while (nodeValue > 140737488355328L) {
208           nodeValue = secureRandom.nextLong();
209           nodeValue = Math.abs(nodeValue);
210       }
211 
212       BigInteger nodeInt = BigInteger.valueOf(nodeValue);
213       String nodeString = nodeInt.toString(2);
214       if (nodeString.length() < 47) {
215           int nbExtraZeros = 47 - nodeString.length();
216           String extraZeros = new String();
217           for (int i = 0; i < nbExtraZeros; i++)
218               extraZeros = extraZeros.concat("0");
219 
220           nodeString = extraZeros.concat(nodeString);
221       }
222 
223       byte[] node_bits = nodeString.getBytes();
224       byte[] node = new byte[48];
225       for (int i = 0; i < 47; i++)
226           node[i] = node_bits[node_bits.length - i - 1];
227 
228       node[47] = ((new String("1")).getBytes())[0];
229       String theNode = Long.toHexString((new BigInteger(new String(reverseArray(node)), 2)).longValue());
230       if (theNode.length() < 12) {
231           int nbExtraZeros = 12 - theNode.length();
232           String extraZeros = new String();
233           for (int i = 0; i < nbExtraZeros; i++)
234               extraZeros = extraZeros.concat("0");
235           theNode = extraZeros.concat(theNode);
236       }
237 
238       String result = timeLow + "-" + timeMid + "-" + timeHiAndVersion + "-" + clockSeqHiAndReserved + clockSeqLow + "-" + theNode;
239 
240       return result.toUpperCase();
241   }
242 
243   private static byte[] reverseArray(byte[] bits)
244   {
245     byte[] result = new byte[bits.length];
246     for (int i = 0; i < result.length; i++)
247       result[i] = bits[result.length - 1 - i];
248 
249     return result;
250   }
251 
252 
253   /****************************************************************************/
254   /****************************** TEST DRIVER *********************************/
255   /****************************************************************************/
256 
257 
258   public static void main(String args[])
259   {
260     SecureUUIDGen generator = new SecureUUIDGen();
261 
262     long start = System.currentTimeMillis();
263 
264     for (int i = 1; i <= 100; ++i)
265       generator.uuidgen();
266 
267     long end = System.currentTimeMillis();
268 
269     System.out.println("SecureUUIDGen: Generation of 100 UUID's took "+(end-start)+" milliseconds.");
270   }
271 }