我们正在使用MySQL并开发一个应用程序,我们希望ID序列不被公开显示...... ID几乎不是绝密的,如果有人确实能够解码它们就没有重大问题。
所以,哈希当然是显而易见的解决方案,我们目前正在使用MD5 ... 32位整数进入,我们将MD5修剪为64位然后存储。但是,我们不知道当你这样修剪时碰撞的可能性(特别是因为所有数字都来自自动增量或当前时间)。我们目前检查是否存在冲突,但由于我们可能会同时插入100,000行,因此性能非常糟糕(无法批量插入)。
但最后,我们真的不需要哈希提供的安全性,它们会占用不必要的空间,还需要一个额外的索引...所以,有没有任何简单和足够好的功能/算法保证对于任何数字的一对一映射,没有明显的序列号视觉模式?
编辑:我使用的PHP默认情况下不支持整数运算,但在环顾四周后我发现它可以通过按位运算符进行廉价复制。可以在此处找到32位整数乘法的代码:http://pastebin.com/np28xhQF答案 0 :(得分:6)
你可以简单地用0xDEADBEEF进行XOR,如果那还不错的话。
或者乘以奇数mod 2 ^ 32。对于逆映射,只需乘以multiplicative inverse
示例:n = 2345678901;乘法逆(mod 2 ^ 32):2313902621 对于映射,只需乘以2345678901(mod 2 ^ 32):
1 - > 2345678901 2 - > 396390506
对于逆映射,乘以2313902621。
答案 1 :(得分:3)
如果要确保1:1映射,则使用加密(即置换),而不是哈希。加密必须是1:1,因为它可以解密。
如果你想要32位数字,那么使用Hasty Pudding Cypher或者只写一个简单的四轮Feistel密码。
这是我之前准备的:
import java.util.Random;
/**
* IntegerPerm is a reversible keyed permutation of the integers.
* This class is not cryptographically secure as the F function
* is too simple and there are not enough rounds.
*
* @author Martin Ross
*/
public final class IntegerPerm {
//////////////////
// Private Data //
//////////////////
/** Non-zero default key, from www.random.org */
private final static int DEFAULT_KEY = 0x6CFB18E2;
private final static int LOW_16_MASK = 0xFFFF;
private final static int HALF_SHIFT = 16;
private final static int NUM_ROUNDS = 4;
/** Permutation key */
private int mKey;
/** Round key schedule */
private int[] mRoundKeys = new int[NUM_ROUNDS];
//////////////////
// Constructors //
//////////////////
public IntegerPerm() { this(DEFAULT_KEY); }
public IntegerPerm(int key) { setKey(key); }
////////////////////
// Public Methods //
////////////////////
/** Sets a new value for the key and key schedule. */
public void setKey(int newKey) {
assert (NUM_ROUNDS == 4) : "NUM_ROUNDS is not 4";
mKey = newKey;
mRoundKeys[0] = mKey & LOW_16_MASK;
mRoundKeys[1] = ~(mKey & LOW_16_MASK);
mRoundKeys[2] = mKey >>> HALF_SHIFT;
mRoundKeys[3] = ~(mKey >>> HALF_SHIFT);
} // end setKey()
/** Returns the current value of the key. */
public int getKey() { return mKey; }
/**
* Calculates the enciphered (i.e. permuted) value of the given integer
* under the current key.
*
* @param plain the integer to encipher.
*
* @return the enciphered (permuted) value.
*/
public int encipher(int plain) {
// 1 Split into two halves.
int rhs = plain & LOW_16_MASK;
int lhs = plain >>> HALF_SHIFT;
// 2 Do NUM_ROUNDS simple Feistel rounds.
for (int i = 0; i < NUM_ROUNDS; ++i) {
if (i > 0) {
// Swap lhs <-> rhs
final int temp = lhs;
lhs = rhs;
rhs = temp;
} // end if
// Apply Feistel round function F().
rhs ^= F(lhs, i);
} // end for
// 3 Recombine the two halves and return.
return (lhs << HALF_SHIFT) + (rhs & LOW_16_MASK);
} // end encipher()
/**
* Calculates the deciphered (i.e. inverse permuted) value of the given
* integer under the current key.
*
* @param cypher the integer to decipher.
*
* @return the deciphered (inverse permuted) value.
*/
public int decipher(int cypher) {
// 1 Split into two halves.
int rhs = cypher & LOW_16_MASK;
int lhs = cypher >>> HALF_SHIFT;
// 2 Do NUM_ROUNDS simple Feistel rounds.
for (int i = 0; i < NUM_ROUNDS; ++i) {
if (i > 0) {
// Swap lhs <-> rhs
final int temp = lhs;
lhs = rhs;
rhs = temp;
} // end if
// Apply Feistel round function F().
rhs ^= F(lhs, NUM_ROUNDS - 1 - i);
} // end for
// 4 Recombine the two halves and return.
return (lhs << HALF_SHIFT) + (rhs & LOW_16_MASK);
} // end decipher()
/////////////////////
// Private Methods //
/////////////////////
// The F function for the Feistel rounds.
private int F(int num, int round) {
// XOR with round key.
num ^= mRoundKeys[round];
// Square, then XOR the high and low parts.
num *= num;
return (num >>> HALF_SHIFT) ^ (num & LOW_16_MASK);
} // end F()
} // end class IntegerPerm
答案 2 :(得分:1)
亨里克在他的第二个建议中说了些什么。但是因为这些值似乎被人们使用(否则你不想让它们随机化)。再迈一步。将序号乘以一个大素数并减少mod N,其中N是2的幂。但是选择N比您可以存储的小2位。接下来,将结果乘以11并使用它。所以我们有:
Hash =((count * large_prime)%536870912)* 11
乘以11可防止大多数数据输入错误 - 如果输入的数字错误,结果将不是11的倍数。如果转换了任何2位数,结果将不是11的倍数。对输入的任何值进行初步检查,在查看数据库之前,检查它是否可被11整除。
答案 3 :(得分:0)
您可以对大素数使用mod运算。
你的号码*大素数1 /大素数2.
素数1应大于秒。秒数应接近2 ^ 32但小于它。比它难以替代。
Prime 1和Prime 2应该是常量。
答案 4 :(得分:0)
对于我们的应用程序,我们使用位洗牌来生成ID。还原回原始ID非常容易。
func (m Meeting) MeetingCode() uint {
hashed := (m.ID + 10000000) & 0x00FFFFFF
chunks := [24]uint{}
for i := 0; i < 24; i++ {
chunks[i] = hashed >> i & 0x1
}
shuffle := [24]uint{14, 1, 15, 21, 0, 6, 5, 10, 4, 3, 20, 22, 2, 23, 8, 13, 19, 9, 18, 12, 7, 11, 16, 17}
result := uint(0)
for i := 0; i < 24; i++ {
result = result | (chunks[shuffle[i]] << i)
}
return result
}