我需要生成数字代码,这些代码将用作代金券或类似的兑换代码。要求代码是数字的,并且相对较短,以便于操作人员的数据输入速度。大约6个字符长和数字。我们知道这是一个很小的数字,所以我们有一个适当的流程,使代码可以过期并被重用。
我们从一个序列整数生成器开始,它在生成唯一代码方面工作得很好。这样做的问题是,生成的代码是顺序的,因此可以预测,这意味着客户可以猜到我们生成的代码,并兑换不适合他们的代金券。
我一直在阅读格式保存加密,这似乎对我们来说可能很有效。我们不需要在任何时候解密代码,因为代码本身是任意的,我们只需要确保它是不可预测的(由日常人员)。这对安全来说并不重要,重要的是让诚实的人诚实。
维基百科文章中引用了各种密码,但我有非常基本的密码学和数学技能,并且无法根据密码编写自己的代码来实现这一点。
我想我的问题是,有人知道c#实现这将加密一个整数到另一个整数,并保持相同的长度?
FPE似乎很好地用于将16位信用卡号码加密为另一个16位数字。我们需要同样的东西,但不一定要固定到一个长度,因为long是明文值长度匹配加密值长度。
那么下面四个整数将被加密
从123456123457123458123459
转换成非顺序的
521482265012961450346582
我愿意接受任何其他的建议来实现这个FPE,这似乎是一个不错的选择。
编辑感谢关于生成唯一代码并存储它们并检查重复的建议。现在我们避免这样做,因为我们不想在生成时检查存储空间。这就是为什么我们使用顺序整数生成器,这样我们就不需要检查代码是否唯一。我将重新研究如何做到这一点,但目前仍在寻找避免每次生成代码时都必须访问存储的方法。
我想知道这是否也会不正确,但让我试一试。这种解决方案不需要存储,但需要处理能力(少量,但不容易用铅笔和纸)。它本质上是一个自制的PRNG,但可能比内置的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();
}
}
}
一种选择是构建数字的就地随机排列。考虑以下代码:
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));
}
您提到在使用其他一些技术时会遇到性能问题。这种方法需要做很多工作,因此可能无法满足您的性能要求。无论如何,这是一个简洁的学术练习。
感谢来自Blogbeard和lc对我的文章的评论。事实证明,在生成代码时我们需要访问存储,所以这意味着实现PRNG对我们来说是一个更好的选择,而不是胡乱加密。
这就是我们最后做的
- 继续使用我们的序列号生成器生成整数
- 创建一个c#随机类的实例(一个PRNG),使用序列号作为种子。
- 在我们想要的最小和最大值范围内生成一个随机数。
- 检查副本并重新生成,直到我们找到唯一的
结果是,当使用序列号作为每一代的种子时,使用带有种子的c# random使得随机数实际上是相当可预测的。
例如,对于范围在1到999999之间的序列种子,我测试了生成500000个值而没有一次冲突。