Integer ID obfuscation techniques

后端 未结 10 1582
萌比男神i
萌比男神i 2020-12-06 07:14

I\'m looking for an easy and reversible method of obfuscating integer IDs. Ideally, I\'d want the resulting obfuscation to be at most eight characters in length and non-seq

10条回答
  •  庸人自扰
    2020-12-06 07:34

    I derived an idea from Pearson hashing which will work for arbitrary inputs as well, not just 32-bit integers. I don't know if this is the exact same as Greg answer, but I couldn't get at what he meant. But what I do know is that the memory requirements are constant here. No matter how big the input, this is still a reliable obfuscation/encryption trick.

    For the record, this method is not hashing, and it does not have collisions. It's a perfectly sound method of obfuscating a byte string.

    What you need for this to work is a secret key _encryptionTable which is a random permutation of the inclusive range 0..255. You use this to shuffle bytes around. To make it really hard to reverse it uses XOR to mix the byte string a bit.

    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;
    }
    

    You can then use the BitConverter to go between values and byte arrays or some convert to base 64 or 32 to get a textual representation. Base 32 encoding can be URL friendly if that's important. Decrypting is as simply as reversing the operation by computing the inverse of the _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;
        }
    

    You can also do other fun things if you're working on a 32-bit integer and only care about the numbers greater than or equal to 0 which makes it harder to guess an obfuscated number.

    I also use a secret word to seed a pseudo number generator and use that to setup the initial permutation. That's why I can simply get the value by knowing what secret word I used to create every thing.

    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;
    

    This is somewhat secure, the biggest flaw here is that the encryption, XOR with 0, happens to be the identity of XOR and doesn't change the value (a ^ 0 == a). Thus the first encrypted byte represent the random position of that byte. To work around this you can pick a initial value for c, that is not constant, based of the secret key by just asking the PRNG (after init with seed) for a random byte. That way it's immensely more difficult even with a large sample to crack the encryption as long as you can't observe input and output.

提交回复
热议问题