views:

660

answers:

6

Hi,

I'm looking for a simple way to translate long to String and back in a way that will "hide" the long value.

I'd prefer to avoid adding another .jar to the project for this feature.

It does not have to be a hard-to-crack encryption, just to look random to the inexperienced eye.

Addition:

My purpose here is to attach a counter value (of type long) to URLs as a tracking parameter without the users aware of the counter's value (sort of like tinyURL's hash), so that the servlet will know the value of the counter when the URL is clicked.

Thanks

+2  A: 

If you're just obfuscating, something trivial like

long -> string -> char array for each character, XOR with the previous output value (with some arbitrary value for before the first character) char array -> string

To reveal for each character, XOR with the previous obfuscated input value (with some arbitrary value for before the first character)

Steve Gilham
+1  A: 

Uhm… without further details it’s hard to come up with a solution on this. You can do lots of different things to accomplish your goal… I guess.

You could XOR it with a random number and store both numbers. This obviously won’t work when your algorithm is out in the open (e.g. if you want to put it in open source).

Or you could use some symmetric encryption algorithm, e.g. Twofish or Serpent.

All of this is rather futile if the number is stored on the client system and evaluated by a client application. As soon as you hand out your application clients can access everything it stores. If the information is worth it, decryption programs will be available soon after your application is released. If it’s not worth it, then why bother?

Bombe
+6  A: 

If

X * Y = 1 (mod 2^32)

then

A * (X * Y) = A (mod 2^32)
(A * X) * Y = A (mod 2^32)

So, you can "encrypt" some 32-bit number by multiplying it by X, and then later "decrypt" by multiplying by Y. All you need is to find some non-trivial X and Y that satisfy the condition.

For instance, (X, Y) = (3766475841, 1614427073), or (699185821, 3766459317). I just found these with a simple brute force program.

Once you have A*X you can encode it using Base-64 or hexadecimal or some similar scheme in the URL. I'd suggest Base64 because it takes up less space and looks fairly "random".

Artelius
Since this is meant to go in a URL, the base 64 will need to be encoded also, unless the web safe variant is used.
laz
Does this algorithm has some name so I can read more about it ??
SeeR
Given an odd X, it is easy to find a modular inverse Y:In python use: Y=X;\n for i in range(5): Y=(2*Y-X*Y*Y)%(2**32).You could call it a Hill-cipher with a 1x1 matrix. It is completely insecure.
Accipitridae
I don't see how that's a problem in this case?
Artelius
The OP asks for an *encryption*. Your solution, however, is a linear function.
Accipitridae
A: 

XOR with an one-time pad (OTP) provides perfect secrecy. I wrote a cipher using OTP to encrypt integers. I just adapted it to long to meet your requirement. It encrypts a long into a 24 char hex string with salt prepended,

-3675525778535888036 => 4fe555ca33021738a3797ab2
-6689673470125604264 => 76092fda5cd67e93b18b4f2f
 8956473951386520443 => 0fb25e533be315bdb6356a2a
 4899819575233977659 => 7cf17d74d6a2968370fbe149

Here is the code,

public class IntegerCipher {
    // This is for salt so secure random is not needed
    private static Random PRNG = new Random();
    private String secret;

    public IntegerCipher(String secret) {
     if (secret.length() < 4)
      throw new IllegalArgumentException("Secret is too short");
     this.secret = secret;
    }

    public String encrypt(long value) {
     int salt = PRNG.nextInt();
     long otp = generatePad(salt);
     long cipher = value ^ otp;
     return String.format("%08x%016x", salt, cipher);
    }

    public long decrypt(String ciphertext) {
     if (ciphertext.length() != 24)
      throw new IllegalArgumentException("Invalid cipher text");
     try {
      int salt = (int) Long.parseLong(ciphertext.substring(0, 8), 16);
      long cipher = Long.parseLong(ciphertext.substring(8, 16), 16) << 32
        | Long.parseLong(ciphertext.substring(16), 16);
      long otp = generatePad(salt);
      return cipher ^ otp;
     } catch (NumberFormatException e) {
      throw new IllegalArgumentException("Invalid hex value: "
        + e.getMessage());
     }
    }

    private long generatePad(int salt) {
     String saltString = Integer.toString(salt);
     String lpad = saltString + secret;
     String rpad = secret + saltString;
     return ((long) lpad.hashCode()) << 32 | (long) rpad.hashCode();
    }

    public static void main(String[] args) {
     IntegerCipher cipher = new IntegerCipher("Top Secret");
     Random rand = new Random();
     for (int i = 0; i < 100; i++) {
      Long n = rand.nextLong();
      String ciphertext = cipher.encrypt(n);
      Long m = cipher.decrypt(ciphertext);
      System.out.printf("%24d => %s\n", n,  ciphertext);
      assert(m == n);
     }
    }
}
ZZ Coder
This is not a one time pad. You are reusing "secret". It might pass as a stream cipher, if you had constucted it more carefully. Your biggest mistake is using hashCode(), which is a linear function. As a result your cipher is easy to break.
Accipitridae
A: 

For encrypting short plaintexts in a deterministic way, you might want to have a look at FFSEM.

Accipitridae
Isn't this overkill? The OP was after something simple and not particularly secure.
Artelius
Why not use a secure method, if you can find one? It simplifies your security review, since you have one attack less to evaluate.
Accipitridae
By "encryption", the OP means "obfuscation".
Artelius
A: 

I like to use Long.toString(value, 36). This prints the number in base 36 which is compact, though cryptic (provided the number is >= 10) You can parse it with Long.parseLong(text, 36)

Peter Lawrey