Last active
March 4, 2022 10:54
-
-
Save megayu/d42e7b209954f459f9a76b79287cfa58 to your computer and use it in GitHub Desktop.
an id obfuscation algorithm to generate a string of length 25
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class IDObfuscation { | |
private static final char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray(); | |
private static final int BASE = ALPHABET.length; | |
private static final byte[] PADDING = {0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17}; | |
private static final byte NEGATIVE = 0x2A; | |
private int key; | |
public IDObfuscation(int key) { | |
this.key = key; | |
} | |
public String obfuscate(long id) { | |
return toStr(swap(id)); | |
} | |
private String toStr(long n) { | |
byte[] bytes = Long.toString(n).getBytes(); | |
int len = bytes.length; | |
int paddingLen = 0; | |
StringBuilder sb = new StringBuilder(); | |
for (int i = 0; i <= 16; i += 4) { | |
int chunk; | |
if (i + 4 <= len) { | |
chunk = encode(bytes, i, i + 4); | |
} else if (i <= len) { | |
chunk = encode(bytes, i, len, paddingLen, 4 + i - len); | |
paddingLen += len - i; | |
} else { | |
chunk = encode(paddingLen, 4); | |
paddingLen += 4; | |
} | |
while (chunk > 0) { | |
chunk = mod(chunk, sb); | |
} | |
} | |
return sb.reverse().toString(); | |
} | |
private int mod(int i, StringBuilder sb) { | |
sb.append(ALPHABET[i % BASE]); | |
return i / BASE; | |
} | |
private int encode(int paddingStartIndex, int paddingNum) { | |
int n = 0; | |
for (int i = 0; i < paddingNum; i++) { | |
n += PADDING[paddingStartIndex + i] << (paddingNum - 1 - i) * 8; | |
} | |
return n; | |
} | |
private int encode(byte[] bytes, int start, int end, int paddingStartIndex, int paddingNum) { | |
return encode(bytes, start, end) + encode(paddingStartIndex, paddingNum); | |
} | |
private int encode(byte[] bytes, int start, int end) { | |
int n = 0; | |
for (int i = start; i < end; i++) { | |
n += (bytes[i] - 3) << (3 - i) * 8; | |
} | |
return n; | |
} | |
private long swap(long i) { | |
long low = i & 0xFFFFFFFFL; | |
long high = i >> 32 & 0xFFFFFFFFL ^ opaque(low); | |
return ((low ^ opaque(high)) << 32) + high; | |
} | |
private long opaque(long i) { | |
i = ((i ^ key) + 0x7886) * 0xEA03122921L; | |
return i >> (i & 0xF) & 0xFFFFFFFFL; | |
} | |
public long restore(String obfuscatedId) { | |
if (obfuscatedId == null || obfuscatedId.length() != 25) throw new IllegalArgumentException("invalid obfuscatedId"); | |
try { | |
return swap(toLong(obfuscatedId)); | |
} catch (NumberFormatException e) { | |
throw new IllegalArgumentException("invalid obfuscatedId"); | |
} | |
} | |
private long toLong(String s) { | |
StringBuilder sb = new StringBuilder(); | |
for (int i = 25; i >= 5; i -= 5) { | |
String chunk = decode(base(s.substring(i - 5, i))); | |
sb.append(chunk); | |
if (chunk.length() < 4) break; | |
} | |
return Long.parseLong(sb.toString()); | |
} | |
private String decode(int n) { | |
char[] c = new char[4]; | |
int i = 3; | |
for (; i >= 0; i--) { | |
int x = (n >> i * 8) & 0xFF; | |
if (x <= PADDING[0]) { | |
return i == 3 ? "" : new String(c, 0, 3 - i); | |
} else if (x == NEGATIVE) { | |
c[3 - i] = '-'; | |
} else { | |
c[3 - i] = (char) (x + 3); | |
} | |
} | |
return new String(c); | |
} | |
private int base(String s) { | |
int n = 0; | |
int power = 1; | |
for (int i = s.length() - 1; i >= 0; i--) { | |
int digit = s.charAt(i); | |
if (digit <= '9') { | |
digit += 4;//52 - 48 | |
} else if (digit >= 'a') { | |
digit -= 71;//26 - 97 | |
} else { | |
digit -= 65; | |
} | |
n += digit * power; | |
power *= BASE; | |
} | |
return n; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment