Если цифры для идентификаторов пользователей, я бы сделал следующее:
(1) Сгенерируйте ключ AES из пароля. Простой вызов getBytes () - это нормально, если вы доверяете администратору использовать действительно очень надежный пароль. В идеале, используйте стандартную технику «шифрования на основе пароля» для хеширования байтов, скажем, несколько тысяч раз, каждый раз добавляя случайные «соленые» байты, которые вы изначально сгенерировали, чтобы избежать атак по словарю.
(2) Зашифруйте соответствующий номер с помощью этого ключа AES.
(3) Отрезать бит из 12 цифр из полученного зашифрованного блока, преобразовать его в десятичную и представить это число пользователю. (Для этого вы можете обернуть BigInteger вокруг байтов, вызвать toString () и вытащить, скажем, байты между позициями 4 и 16.) Экспериментально похоже, что вы не должны брать цифры из крайний правый конец.
[ Обновление : я думаю это, вероятно, потому, что BigInteger буквально распределяет свои числа слева направо - но я не проверял - так что потенциально «запасные» биты в самом правом байте и, следовательно, меньшее количество возможных чисел, если вы включите самый последний байт.]
Теперь, я слышу, как ты плачешь, это, очевидно, не отображение 1-1. Но если у вас не будет более десятков тысяч пользователей, этого действительно достаточно. С 12-значным числом вы ожидаете в среднем зашифровать около 300 000 номеров, прежде чем столкнетесь. Так что, хотя у вас нет строго сопоставления 1-1, на практике это почти так же, как черт.
(В любом случае, если ваше приложение действительно имеет сотни пользователей и безопасность имеет решающее значение, вам, вероятно, захочется инвестировать в серьезные консультации по этому вопросу ...)
Просто чтобы убедить себя в том, что действительно можно утверждать, что это отображение 1-1, вы можете запустить симуляцию, которая неоднократно пытается выделить, скажем, 200 000 идентификаторов пользователя со случайными ключами, и распечатать, сколько коллизий было на каждый прогон:
next_pass :
for (int pass = 0; pass < 100; pass++) {
byte[] key = new byte[16];
(new SecureRandom()).nextBytes(key);
Cipher ciph = Cipher.getInstance("AES");
SecretKeySpec ks = new SecretKeySpec(key, "AES");
ByteBuffer bb = ByteBuffer.allocate(16);
Set<String> already = new HashSet<String>(100000);
int colls = 0;
for (int i = 0; i < 200000; i++) {
bb.putLong(0, i);
ciph.init(Cipher.ENCRYPT_MODE, ks);
byte[] encr = ciph.doFinal(bb.array());
encr[0] &= 0x7f; // make all numbers positive
BigInteger bigint = new BigInteger(encr);
String userNo = bigint.toString();
userNo = userNo.substring(4, 16);
if (!already.add(userNo)) {
System.out.println("Coll after " + i);
continue next_pass;
}
}
System.out.println("No collision.");
}