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 }