001 /*
002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003 * Copyright (C) 2011 NightLabs Consulting GmbH
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program. If not, see <http://www.gnu.org/licenses/>.
017 */
018 package org.cumulus4j.keymanager.back.shared;
019
020 import java.math.BigInteger;
021 import java.security.SecureRandom;
022 import java.util.UUID;
023
024 /**
025 * Utility class for identifiers used within Cumulus4j.
026 *
027 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
028 */
029 public class IdentifierUtil
030 {
031 private static SecureRandom random = new SecureRandom();
032
033 private static double log(double base, double value)
034 {
035 return Math.log10(value) / Math.log10(base);
036 }
037
038 /**
039 * <p>
040 * Create a random <code>String</code> identifier with a sufficiently unique length.
041 * </p>
042 * <p>
043 * This method calls {@link #createRandomID(int)} with a <code>length</code> of 25.
044 * </p>
045 * <p>
046 * The <code>length</code> of 25 is chosen, because it produces an identifier
047 * which has about the same uniqueness as {@link UUID#randomUUID()}. This is because
048 * the String has 36 ^ 25 (approximately equals 2 ^ 129) possible values while a UUID
049 * has 2 ^ 128 possible values and both identifiers are created using the same
050 * method ({@link SecureRandom#nextBytes(byte[])}).
051 * </p>
052 * @return a random <code>String</code>.
053 * @see #createRandomID(int)
054 */
055 public static String createRandomID()
056 {
057 return createRandomID(25);
058 }
059
060 /**
061 * <p>
062 * Create a random <code>String</code> identifier with a specified length.
063 * </p>
064 * <p>
065 * The generated identifier will contain
066 * only the characters '0'...'9' and 'a'...'z' and will have the specified <code>length</code>.
067 * This method uses a {@link SecureRandom} (just like {@link UUID#randomUUID()}). With a length
068 * of 25, the identifier will have about the same uniqueness as a <code>UUID</code> - see
069 * {@link #createRandomID()}.
070 * </p>
071 *
072 * @param length the number of <code>char</code>s in the result.
073 * @return a random <code>String</code> with the given <code>length</code>.
074 * @see #createRandomID()
075 */
076 public static String createRandomID(int length)
077 {
078 int byteArrayLength = (int)log(256, Math.pow(36, length)) + 1;
079
080 byte[] val = new byte[byteArrayLength];
081 random.nextBytes(val);
082 val[0] = (byte)(val[0] & 0x7F); // ensure a positive value
083 BigInteger bi = new BigInteger(val);
084 String result = bi.toString(36).substring(1); // cut the first character, because its range is limited (never reaches 'z')
085
086 if (result.length() < length) { // prepend with '0' to reach a fixed length.
087 StringBuilder sb = new StringBuilder(length);
088 for (int i = result.length(); i < length; ++i)
089 sb.append('0');
090
091 sb.append(result);
092 result = sb.toString();
093 }
094
095 if (result.length() > length + 1)
096 throw new IllegalStateException("Why is result.length == " + result.length() + " > " + length + "+1 chars?!");
097
098 if (result.length() > length)
099 result = result.substring(result.length() - length);
100
101 if (result.length() != length)
102 throw new IllegalStateException("Why is result.length != " + length + " chars?!");
103
104 return result;
105 }
106
107 // public static void main(String[] args) {
108 // // Check to see whether a length of 25 is approximately as unique as a UUID.
109 // double possibleValues = Math.pow(36, 25);
110 // double possibleValuesExponentTo2 = log(2, possibleValues);
111 // System.out.println(possibleValues);
112 // System.out.println(possibleValuesExponentTo2);
113 // System.out.println(Math.pow(2, possibleValuesExponentTo2));
114 // }
115
116 // public static void main(String[] args) {
117 // long start = System.currentTimeMillis();
118 // double a = Math.random();
119 // double b = Math.random();
120 // double p = Math.pow(a, b);
121 // double l = log(a, p);
122 // System.out.println("a = " + a);
123 // System.out.println("b = " + b);
124 // System.out.println("p = " + p);
125 // System.out.println("l = " + l);
126 // System.out.println("|b-l| = " + Math.abs(b-l));
127 //
128 // for (int i = 0; i < 10000; ++i) {
129 // String id = createRandomID(1 + (i % 50));
130 // System.out.println(id);
131 // }
132 // System.out.println("Duration: " + (System.currentTimeMillis() - start) + " msec");
133 // }
134 }