将唯一的16位数字ID映射到唯一的字母数字ID

时间:2016-07-04 16:25:22

标签: java random mapping unique hash-function

在我正在进行的项目中,我需要生成16个字符长的唯一ID,包括10个数字加上26个大写字母(仅大写)。必须保证它们具有普遍的独特性,没有机会重复。

ID不会永久存储。一段时间后,ID将从数据库中抛出,并且必须生成新的唯一ID。 ID也永远不会与抛出的ID重复。

所以随机生成16位数并检查以前生成的ID列表不是一个选项,因为没有以前ID的综合列表。此外,UUID将无效,因为ID的长度必须为16位。

现在我正在使用16位数的唯一ID,每次生成时都保证它们是唯一的(我使用timestamps生成它们加上唯一的服务器ID)。但是,我需要难以预测ID,使用timestamps可以轻松预测。

所以我需要做的是将我拥有的16位数字ID映射到更大范围的10位+ 26位字母而不会失去唯一性。我需要某种散列函数,从较小的范围映射到较大的范围,保证一对一的映射,以确保唯一的ID在映射后保持唯一。

我已经搜索过,到目前为止还没有找到任何保证无冲突的散列或映射函数,但如果我映射到更大的空间,则必须存在。任何建议都表示赞赏。

4 个答案:

答案 0 :(得分:0)

编辑:这是一个更新的答案,因为我误读了最终ID的约束。

这是一个可能的解决方案。

设置:

  • UID16 = 16位唯一身份证
  • LUID = 16个符号的UID(使用数字+字母)
  • SECRET =秘密字符串
  • HASH = SECRET + UID16的一些哈希

现在,您可以计算:

LUID = BASE36(UID16) + SUBSTR(BASE36(HASH), 0, 5)

BASE36(UID16)将生成一个11个字符的字符串(因为16 / log10(36)〜= 10.28)

保证是唯一的,因为原始UID16完全包含在最终ID中。如果碰巧与两个不同的UID16发生哈希冲突,那么你仍然会有两个不同的LUID。

然而,很难预测,因为其他5个符号基于不可预测的哈希值。

注意:您只能在哈希部分获得log2(36 ^ 5)〜= 26位熵,根据您的安全要求,这可能是也可能不够。原始UID16越不可预测,越好。

答案 1 :(得分:0)

您希望从包含10个 16 不同值的空间映射到具有36个 16 值的空间。

这两个空格的大小比率为~795,866,110

使用BigDecimal并将每个输入值乘以比率,以在输出空间上平均分配输入键。然后base-36对结果值进行编码。

这是一个16位数值的样本,包括11位“时间戳”和使用上述方案编码的5位服务器ID。

Decimal ID          Base-36 Encoding
----------------    ----------------
4156333000101044 -> EYNSC8L1QJD7MJDK
4156333000201044 -> EYNSC8LTY4Y8Y7A0
4156333000301044 -> EYNSC8MM5QJA9V6G
4156333000401044 -> EYNSC8NEDC4BLJ2W
4156333000501044 -> EYNSC8O6KXPCX6ZC
4156333000601044 -> EYNSC8OYSJAE8UVS
4156333000701044 -> EYNSC8PR04VFKIS8
4156333000801044 -> EYNSC8QJ7QGGW6OO

前11位形成“时间戳”,我计算了一系列增加1的结果;最后五位数字是任意“服务器ID”,在本例中为01044

答案 2 :(得分:0)

Brandon Staggs写了一篇关于Implementing a Partial Serial Number Verification System的好文章。这些示例是用Delphi编写的,但可以转换为其他语言。

答案 3 :(得分:0)

您的问题的一个通用解决方案是加密。由于加密是可逆的,因此始终是从明文到密文的一对一映射。如果你加密数字[0,1,2,3,...],那么你可以保证得到的密文也是唯一的,只要你保持相同的密钥,不要重复一个数字或溢出允许的大小。您只需要跟踪下一个要加密的数字,根据需要递增,并检查它是否永远不会溢出。

然后问题减少到加密的大小(以位为单位)以及如何将其呈现为文本。你说:" 10个数字加上26个大写字母(只有大写)。"这类似于Base32编码,它使用数字2,3,4,5,6,7和26个字母。不完全符合您的要求,但可能足够接近并可以现货供应。每个Base32字符5位的16个字符是80位。您可以使用80位分组密码并将输出转换为Base32。滚动您自己的简单Feistel cipher或使用Hasty Pudding cipher,可以为任何块大小设置。如果此处存在重大安全要求,请不要自行推送。您自己的Feistel密码将为您提供独特性和混淆,而不是安全性。 Hasty Pudding也提供安全保障。

如果你确实需要所有10位数字和26个字母,那么你正在查看基数为36的数字。计算出所需的位数为36 ^ 16并继续进行。将密文位转换为以36为基数表示的数字。

如果您编写自己的密码,那么您似乎不需要解密功能,这将节省一些工作。