整数ID混淆技术

我正在寻找一种简单且可逆的混淆整数ID的方法。 理想情况下,我希望得到的混淆最多为8个字符长度和非顺序,这意味着“1”的混淆应该看起来不像“2”的混淆,依此类推。

这并不意味着任何方式都是安全的,所以这不是一个大问题。 另外,我将要混淆的整数并不大 – 介于1到10,000之间 – 但我也不想要任何碰撞。

是否有人对符合此标准的内容有任何想法?

我从Pearson散列中得出了一个想法,它也适用于任意输入,而不仅仅是32位整数。 我不知道这是否与Greg的答案完全相同,但我无法理解他的意思。 但我所知道的是内存需求在这里是不变的。 无论输入多大,这仍然是一个可靠的混淆/加密技巧。

对于记录,此方法不是散列,并且没有冲突。 这是混淆字节字符串的完美声音方法。

你需要的是一个秘密密钥_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 )。 因此,第一个加密字节表示该字节的随机位置。 要解决这个问题,您可以通过向PRNG(在init之后使用种子)询问随机字节来选择基于密钥的c的初始值,该值不是常量。 这样,只要您无法观察输入和输出,即使使用大量样本来破解加密也会非常困难。

如果你只有大约10,000个整数,那么最简单和最可靠的方法可能是整数和随机生成的字符串之间的映射表。 要么预先生成一堆对应于每个整数的随机标识符,要么根据需要填充它们。

这样就可以保证不会发生冲突,也不必担心加密,因为没有什么可以解密的,因为字符串不是从整数本身派生的。

您可以根据需要在数据库表或内存(例如双向字典)中实现它。

我意识到这是在7个月前被问到所以你现在已经找到了解决方案,但我遇到的解决方案是Skip32 / Skipjack密码+ base32编码的组合。 perl示例(因为我知道的那个)显示:

 use Crypt::Skip32::Base32Crockford; my $key = pack( 'H20', "112233445566778899AA" ); # Always 10 bytes! my $cipher = Crypt::Skip32::Base32Crockford->new($key); my $b32 = $cipher->encrypt_number_b32_crockford(3493209676); # 1PT4W80 my $number = $cipher->decrypt_number_b32_crockford('1PT4W80'); # 3493209676 

我不知道ac#的实现,但perl的一个是http://search.cpan.org/perldoc?Crypt::Skip32::Base32Crockford和ruby的两个组成部分是https:// github。 com / levinalex / base32和https://github.com/patdeegan/integer-obfuscator 。 在他们两个之间,你应该能够将它移植到你需要的任何语言。

XOR是一种混淆整数的好方法:

 1 xor 1234 = 1235 2 xor 1234 = 1232 3 xor 1234 = 1233 100 xor 1234 = 1206 120 xor 1234 = 1194 

它速度很快,并且使用相同的数字进行再次调整可以让您恢复原状! 唯一的麻烦是,如果一个“攻击者”知道任何数字,他们可以通过用已知原始的xor-ing结果来找出xor掩码!

例如我(“攻击者”)现在该列表中的第4个数字是混淆的“100”。 所以我会这样做:

 100 xor 1206 = 1234 

…现在我已经有了XOR掩码,我可以对任何数字进行无混淆。 令人高兴的是,这个问题有一个微不足道的解决方案。 算法改变XOR掩模。 例如,如果需要在数组中混淆1000个整数,请从XOR掩码“1234”开始,并为arrray中的每个数字增加MASK 4。

在其他人感兴趣的情况下,有人在几年前调整了32位分组密码,这对于此任务特别有用。

还有上面提供的Perl和Ruby端口:

如果您需要8个字符或更少的结果,则可以使用hex或base64表示。

处理大多数语言的图书馆的好项目: http : //hashids.org/

2017年5月更新

随意使用(或修改)我开发的库,可通过Nuget安装:

安装包Kent.Cryptography.Obfuscation

这会将非负的id(例如127)转换为8个字符的字符串 (例如xVrAndNb) ,然后返回(使用一些可用选项将序列随机生成每次生成)。

示例用法

 var obfuscator = new Obfuscator(); string maskedID = obfuscator.Obfuscate(15); 

完整文档: Github 。


老答案

只是为旧答案添加多样性。 也许有人需要它。 这是我在某个时候做过的混淆课。

Obfuscation.cs – Github

你可以用它来:

 Obfuscation obfuscation = new Obfuscation(); string maskedValue = obfuscation.Obfuscate(5); int? value = obfuscation.DeObfuscate(maskedValue); 

干杯,希望它可以使用。

您可以使用数字的位模式 – 例如旋转和交换位。 这将为您提供一种在多个26位和另一个26位之间移动的方法,这对于人类观察者来说不会立即显而易见。 虽然它绝不“安全”。

我意识到这是一个旧post,但我认为发布我的技术来混淆整数id可能会有所帮助

缺点:确实使用超过8个字符,仅适用于3300万以下的id值

优点:不需要密钥去混淆,URL / cookie友好,每次生成不同的值,这使得它更难破坏,没有冲突,包括校验和function,以消除随机/powershell企图破坏(一个问题,上面的post没有解决,是人们试图“刮”你的网站。如果我看到一个以id = 123结尾的url,我知道我可以尝试id = 124等…来获取更多数据,这就是为什么有些XOR示例可能不是一个好主意)

我建议稍微调整一下(我已经为我做过),因为我认为你不应该使用公开发布的混淆技术,但它是一个很好的起点。

快乐的编码!

  public static string ObfuscateId(int id) { try { string rtn; int sid = id + 279; int xm = sid * 3; int xl = xm.ToString().Length + 10; string sc = xl.ToString().Substring(1, 1); string fc = xl.ToString().Substring(0, 1); string csum = sid.ToString().Substring(sid.ToString().Length - 3); rtn = Guid.NewGuid().ToString().Replace("-", "").ToLower(); rtn = sc + rtn.Substring(2, 26) + fc; rtn = rtn.Remove(4, 3).Insert(4, csum); rtn = rtn.Remove(xl, (xl - 10)).Insert(xl, xm.ToString()); rtn = rtn.Replace('1', 'g'); rtn = rtn.Replace('2', 'h'); rtn = rtn.Replace('3', 'i'); rtn = rtn.Replace('4', 'w'); rtn = rtn.Replace('5', 'y'); rtn = rtn.Replace('6', 'u'); rtn = rtn.Replace('7', 'z'); rtn = rtn.Replace('8', 'l'); rtn = rtn.Replace('9', 'v'); rtn = rtn.Replace('0', 'n'); rtn = rtn.Replace('c', 'j'); rtn = rtn.Replace('d', 'p'); rtn = rtn.Replace('f', 'q'); return rtn.ToUpper(); } catch { return "ERROR BAD ID"; } } public static int DeObfuscateId(string obtxt) { try { string rtn; int id; rtn = obtxt.ToLower(); rtn = rtn.Replace('g', '1'); rtn = rtn.Replace('h', '2'); rtn = rtn.Replace('i', '3'); rtn = rtn.Replace('w', '4'); rtn = rtn.Replace('y', '5'); rtn = rtn.Replace('u', '6'); rtn = rtn.Replace('z', '7'); rtn = rtn.Replace('l', '8'); rtn = rtn.Replace('v', '9'); rtn = rtn.Replace('n', '0'); rtn = rtn.Replace('j', 'c'); rtn = rtn.Replace('p', 'd'); rtn = rtn.Replace('q', 'f'); string sc = rtn.Substring(0, 1); string fc = rtn.Substring(rtn.Length - 1); int xl = int.Parse(fc + sc); int mv = int.Parse(rtn.Substring(xl, (xl - 10))); int sid = mv / 3; id = sid - 279; string csum = sid.ToString().Substring(sid.ToString().Length - 3); string xsum = rtn.Substring(4, 3); if (csum!=xsum) { return -99999; } return id; } catch { return -99999; } } } 

只需获取整数字节表示的MD5 / SHA1哈希值。 保证不会发生碰撞。