Я ищу легкий и обратимый метод запутывания целочисленных идентификаторов. Идеально, я хотел бы, чтобы получающаяся путаница была самое большее восемью символами в длине и непоследовательный, подразумевая, что путаница "1" не должна смотреть ничто как путаница для "2" и так далее.
Это не предназначено, чтобы быть безопасным каким-либо образом, таким образом, это не огромное беспокойство. Кроме того, целые числа, которые я буду запутывать, не являются большими - между один и 10,000 - но я не хочу коллизий, также.
У кого-либо есть какие-либо идеи для чего-то, что соответствовало бы этому критерии?
Я получил идею из хеширования Пирсона, которая будет работать и для произвольных входных данных, а не только для 32-битных целых чисел. Я не знаю, совпадает ли это с ответом Грега, но я не мог понять, что он имел в виду. Но я точно знаю, что требования к памяти здесь постоянные. Независимо от того, насколько велик ввод, это все равно надежный трюк обфускации / шифрования.
Для справки, этот метод не является хеширующим и не имеет коллизий. Это отличный способ обфускации байтовой строки.
Для работы вам нужен секретный ключ _encryptionTable
, который представляет собой случайную перестановку включающего диапазона 0..255. Вы используете это для перемешивания байтов. Чтобы сделать реверсирование действительно сложным, он использует XOR для небольшого смешивания байтовой строки.
public byte[] Encrypt(byte[] plaintext)
{
if (plaintext == null)
{
throw new ArgumentNullException("plaintext");
}
byte[] ciphertext = new byte[plaintext.Length];
int c = 0;
for (int i = 0; i < plaintext.Length; i++)
{
c = _encryptionTable[plaintext[i] ^ c];
ciphertext[i] = (byte)c;
}
return ciphertext;
}
Затем вы можете использовать BitConverter для перехода между значениями и байтовыми массивами или преобразовать их в базу 64 или 32 для получения текстового представления. Кодировка Base 32 может быть дружественной к URL, если это важно. Расшифровка - это так же просто, как обратная операция путем вычисления инверсии _encryptionTable
.
public byte[] Decrypt(byte[] ciphertext)
{
if (ciphertext == null)
{
throw new ArgumentNullException("ciphertext");
}
byte[] plaintext = new byte[ciphertext.Length];
int c = 0;
for (int i = 0; i < ciphertext.Length; i++)
{
plaintext[i] = (byte)(_decryptionTable[ciphertext[i]] ^ c);
c = ciphertext[i];
}
return plaintext;
}
Вы также можете делать другие забавные вещи, если работаете с 32-битным целым числом и заботитесь только о числах, больших или равных 0, что затрудняет угадание запутанного числа.
Я также использую секретное слово для заполнения генератора псевдочислений и использую его для настройки начальной перестановки. Вот почему я могу просто получить ценность, зная, какое секретное слово я использовал для создания каждой вещи.
var mt = new MersenneTwister(secretKey.ToUpperInvariant());
var mr = new byte[256];
for (int i = 0; i < 256; i++)
{
mr[i] = (byte)i;
}
var encryptionTable = mt.NextPermutation(mr);
var decryptionTable = new byte[256];
for (int i = 0; i < 256; i++)
{
decryptionTable[encryptionTable[i]] = (byte)i;
}
this._encryptionTable = encryptionTable;
this._decryptionTable = decryptionTable;
Это в некоторой степени надежно, самый большой недостаток здесь в том, что шифрование XOR с 0 является идентификатором XOR и не меняет значения ( a ^ 0 == a
). Таким образом, первый зашифрованный байт представляет собой случайную позицию этого байта. Чтобы обойти это, вы можете выбрать начальное значение для c
, которое не является постоянным, на основе секретного ключа, просто запросив PRNG (после инициализации с семенем) для случайного байта. Таким образом, даже с большой выборкой намного сложнее взломать шифрование, если вы не можете наблюдать ввод и вывод.
Просто получите хэш MD5 / SHA1 байтового представления целого числа. У вас гарантированно не будет столкновений.
Вы можете играть с битовыми шаблонами числа - например, вращать и менять местами биты. Это даст вам возможность перемещаться между числом, скажем, 26 битов, и другим числом из 26 бит, что не будет сразу очевидно для человека-наблюдателя. Хотя это ни в коем случае не «безопасно».
Если у вас всего около 10 000 целых чисел, то самым простым и надежным способом, вероятно, будет таблица сопоставления между целым числом и случайно сгенерированной строкой. Либо заранее сгенерируйте кучу случайных идентификаторов, соответствующих каждому целому числу, либо просто заполняйте их по требованию.
Таким образом вы можете гарантировать отсутствие коллизий и не беспокоиться о шифровании, поскольку расшифровывать нечего, так как строки не являются производными от самих целых чисел.
Вы можете реализовать это в таблице базы данных или в памяти (например, двусторонний словарь) в зависимости от ваших потребностей.
XOR - хороший и быстрый способ обфускации целых чисел:
1 xor 1234 = 1235
2 xor 1234 = 1232
3 xor 1234 = 1233
100 xor 1234 = 1206
120 xor 1234 = 1194
Это быстро, и xor-ing снова с тем же номером возвращает оригинал! Единственная проблема заключается в том, что если «злоумышленник» знает какое-либо из чисел, он может тривиально вычислить маску xor ... путем поиска результата с известным оригиналом!
Например, я («злоумышленник») теперь, когда четвертое число в этом списке - запутанное «100». Итак, я сделаю:
100 xor 1206 = 1234
... и теперь у меня есть маска XOR, и я могу разблокировать любое из чисел. К счастью, у этой проблемы есть тривиальное решение. Алгоритмически изменить маску XOR. Например, если вам нужно скрыть 1000 целых чисел в массиве, начните с маски XOR «1234» и увеличивайте МАСКУ на 4 для каждого числа в массиве.
Если кому-то интересно, кто-то несколько лет назад адаптировал 32-битный блочный шифр, который особенно полезен для этой задачи.
Также доступны Perl и Ruby-порт из перечисленных выше:
Если вам нужен результат в 8 символов или меньше, вы можете использовать шестнадцатеричное или base64 представление.