我需要生成将用作代金券或类似代币的兑换码的数字代码。要求是代码是数字的,并且对于直到操作员的数据输入速度相对较短。大约6个字符长和数字。我们知道这是一个很小的数字,因此我们有一个流程,以便代码可以过期并重复使用。
我们开始只使用顺序整数生成器,它在生成唯一代码方面运行良好。这个问题是生成的代码是顺序的,所以可预测,这意味着客户可以猜测我们生成的代码并兑换不适合他们的代金券。
我一直在阅读Format Preserving Encryption,这似乎对我们有用。我们不需要在任何时候解密代码,因为代码本身是任意的,我们只需要确保它不可预测(由日常人员)。这对于安全来说并不重要,只是为了让诚实的人诚实。
维基百科文章中引用了各种密码,但我具有非常基本的加密和数学技能,并且无法根据密码编写自己的代码来实现此目的。
我想我的问题是,是否有人知道c#的实现会将整数加密为另一个整数并保持相同的长度?
FPE似乎可以很好地用于将16位信用卡号加密成另一个16位数字。我们需要相同的东西,但不一定固定为长度,但是普通值长度与加密值长度匹配的时间长。
因此以下四个整数将被加密
这 123456 123457 123458 123459
像这样非顺序的东西
521482 265012 961450 346582
我愿意接受任何其他建议来实现这个FPE似乎是个不错的选择。
编辑
感谢有关生成唯一代码并存储它们并检查重复项的建议。现在我们已经避免这样做,因为我们不希望在生成时检查存储。这就是我们使用顺序整数生成器的原因,因此我们不需要检查代码是否唯一。我会重新调查这样做,但现在仍然在寻找避免每次生成代码时都要去存储的方法。
答案 0 :(得分:1)
我想知道这是否也不会偏离基地,但让我试一试。这种解决方案不需要存储,但需要处理能力(很小的数量,但它不会是铅笔纸很容易)。它本质上是一个自制的PRNG,但可能具有比内置的更适合你想做的特性。
要制作数字生成器,请使用素数系数和素数模数进行多项式。例如,让X表示您获得的第N张凭证。然后:
凭证编号=(23x ^ 4 + 19x ^ 3 + 5x ^ 2 + 29x + 3)%65537。这当然只是一个例子;你可以使用任意数量的术语,你想要的任何素数系数,你可以使模数尽可能大。事实上,模数根本不需要是素数。它仅设置最大凭证编号。系数为素数有助于减少碰撞。
在这种情况下,凭证#100,101和102将分别具有数字26158,12076和6949。将其视为一种玩具加密,其中系数是您的关键。不是超级安全,但没有像你要求的那样小的输出空间对于强大的对手是安全的。但这应该可以阻止日常欺诈者。
确认有效的凭证会占用计算机(但仅限计算,而不是存储)。它将遍历几千或几万个输入X,寻找与提供给您的凭证相匹配的输出Y.当它找到匹配时,它可以发出有效凭证的信号。
或者,您可以发出包含序列号和计算连接在一起的凭证,如值和校验和。然后,您可以使用您的秘密系数手动运行该值的计算,以确认有效性。
只要您不向任何人透露系数,就很难识别输出中的模式。我不确定这是否与您所寻求的一样安全,但发布这个想法以防万一。
我错误地计算了100的输出(手动完成并失败)。刚才更正了。让我添加一些代码来说明我如何检查有效的凭证:
using System;
using System.Numerics;
namespace Vouchers
{
class Program
{
static void Main(string[] args)
{
Console.Write("Enter voucher number: ");
BigInteger input = BigInteger.Parse(Console.ReadLine());
for (BigInteger i = 0;i<10000000;i++)
{
BigInteger testValue = (23 * i * i * i * i + 19 * i * i * i + 5 * i * i + 29 * i + 3) % 65537;
if(testValue==input)
{
Console.WriteLine("That is voucher # " + i.ToString());
break;
}
if (i == 100) Console.WriteLine(testValue);
}
Console.ReadKey();
}
}
}
答案 1 :(得分:0)
一种选择是建立数字的就地随机排列。请考虑以下代码:
private static readonly Random random = new Random((int)DateTime.UtcNow.Ticks);
private static int GetRandomPermutation(int input)
{
char[] chars = input.ToString().ToCharArray();
for (int i = 0; i < chars.Length; i++ )
{
int j = random.Next(chars.Length);
if (j != i)
{
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
}
return int.Parse(new string(chars));
}
您提到过使用其他一些技术遇到性能问题。此方法做了很多工作,因此可能无法满足您的性能要求。无论如何,这是一个很好的学术练习。
答案 2 :(得分:0)