c# 格式保留整数加密
c# format preserving encryption for integers
我需要生成数字代码,这些代码将用作代金券或类似物品的兑换代码。要求是代码是数字的,并且相对较短,以加快收银机操作员的数据输入速度。大约 6 个字符长和数字。我们知道这是一个小数字,因此我们制定了一个流程,以便代码可以过期并重新使用。
我们一开始只使用一个顺序整数生成器,它在生成唯一代码方面运行良好。问题是生成的代码是连续的,因此可以预测,这意味着客户可以猜出我们生成的代码并兑换不适合他们的代金券。
我一直在阅读 Format Preserving Encryption,这似乎对我们很有用。我们不需要在任何时候解密代码,因为代码本身是任意的,我们只需要确保它不可预测(普通人)。这对安全来说并不重要,只是让诚实的人保持诚实。
维基百科文章中引用了各种密码,但我只有非常基本的密码学和数学技能,无法根据密码编写自己的代码来实现这一点。
我想我的问题是,有人知道将一个整数加密为另一个整数并保持相同长度的 c# 实现吗?
FPE 似乎可以很好地用于将 16 位信用卡号加密为另一个 16 位数字。我们需要同样的东西,但不一定固定为长度,但只要普通值长度与加密值长度匹配即可。
所以下面四个整数将被加密
来自
123456
123457
123458
123459
像这样不连续的东西
521482
265012
961450
346582
我愿意接受任何其他实现此 FPE 的建议,这似乎是一个不错的选择。
编辑
感谢您提出有关仅生成唯一代码并存储它们并检查重复项的建议。现在我们避免这样做,因为我们不想在生成时检查存储。这就是我们使用顺序整数生成器的原因,这样我们就不需要检查代码是否唯一。我会重新调查这样做,但现在仍在寻找避免每次生成代码时都必须进入存储的方法。
一种选择是构建数字的就地随机排列。考虑这段代码:
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));
}
您提到 运行 其他一些技术的性能问题。这种方法做了很多工作,因此它可能无法满足您的性能要求。无论如何,这是一个很好的学术练习。
我想知道这是否也有问题,但让我试一试。该解决方案不需要存储,但需要处理能力(数量很少,但用纸笔来实现并不容易)。它本质上是一个自制的 PRNG,但可能比内置的更适合你想做的事情。
要制作数字生成器,请制作一个具有素数系数和素数模数的多项式。例如,让 X 代表您发出的第 N 个代金券。然后:
凭证编号 = (23x^4+19x^3+5x^2+29x+3)%65537。这当然只是一个例子;你可以使用任意数量的项,任何你想要的素数作为系数,你可以使模数尽可能大。事实上,模数根本不需要是质数。它只设置最大凭证数量。使系数为质数有助于减少碰撞。
在这种情况下,凭证 #100、101 和 102 的编号分别为 26158、12076 和 6949。将其视为一种玩具加密,其中系数是您的密钥。不是超级安全,但没有任何输出 space 像你所要求的那样小的东西可以抵御强大的对手。但这应该可以阻止日常欺诈者。
确认凭证有效需要用电脑(但只计算,不存储)。它会遍历几千或几万个输入 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();
}
}
}
感谢 Blogbeard and lc 对我最初 post 的评论提供的帮助。事实证明,我们无论如何都需要在生成代码时命中存储,所以这意味着实施 PRNG 对我们来说是一个更好的选择,而不是搞乱加密。
这就是我们最终所做的
- 继续使用我们的序号生成器生成整数
- 使用序列号作为种子创建 C# Random class(PRNG)实例。
- 在我们想要的最小和最大数范围内生成一个随机数。
- 检查重复项并重新生成,直到我们找到一个唯一的
事实证明,在使用序列号作为每一代的种子时,使用带有种子的 c# random 使得随机数实际上非常可预测。
例如,使用顺序种子,范围在 1 到 999999 之间,我测试了在没有一次碰撞的情况下生成 500000 个值。
我需要生成数字代码,这些代码将用作代金券或类似物品的兑换代码。要求是代码是数字的,并且相对较短,以加快收银机操作员的数据输入速度。大约 6 个字符长和数字。我们知道这是一个小数字,因此我们制定了一个流程,以便代码可以过期并重新使用。
我们一开始只使用一个顺序整数生成器,它在生成唯一代码方面运行良好。问题是生成的代码是连续的,因此可以预测,这意味着客户可以猜出我们生成的代码并兑换不适合他们的代金券。
我一直在阅读 Format Preserving Encryption,这似乎对我们很有用。我们不需要在任何时候解密代码,因为代码本身是任意的,我们只需要确保它不可预测(普通人)。这对安全来说并不重要,只是让诚实的人保持诚实。
维基百科文章中引用了各种密码,但我只有非常基本的密码学和数学技能,无法根据密码编写自己的代码来实现这一点。
我想我的问题是,有人知道将一个整数加密为另一个整数并保持相同长度的 c# 实现吗?
FPE 似乎可以很好地用于将 16 位信用卡号加密为另一个 16 位数字。我们需要同样的东西,但不一定固定为长度,但只要普通值长度与加密值长度匹配即可。
所以下面四个整数将被加密
来自 123456 123457 123458 123459
像这样不连续的东西
521482 265012 961450 346582
我愿意接受任何其他实现此 FPE 的建议,这似乎是一个不错的选择。
编辑
感谢您提出有关仅生成唯一代码并存储它们并检查重复项的建议。现在我们避免这样做,因为我们不想在生成时检查存储。这就是我们使用顺序整数生成器的原因,这样我们就不需要检查代码是否唯一。我会重新调查这样做,但现在仍在寻找避免每次生成代码时都必须进入存储的方法。
一种选择是构建数字的就地随机排列。考虑这段代码:
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));
}
您提到 运行 其他一些技术的性能问题。这种方法做了很多工作,因此它可能无法满足您的性能要求。无论如何,这是一个很好的学术练习。
我想知道这是否也有问题,但让我试一试。该解决方案不需要存储,但需要处理能力(数量很少,但用纸笔来实现并不容易)。它本质上是一个自制的 PRNG,但可能比内置的更适合你想做的事情。
要制作数字生成器,请制作一个具有素数系数和素数模数的多项式。例如,让 X 代表您发出的第 N 个代金券。然后:
凭证编号 = (23x^4+19x^3+5x^2+29x+3)%65537。这当然只是一个例子;你可以使用任意数量的项,任何你想要的素数作为系数,你可以使模数尽可能大。事实上,模数根本不需要是质数。它只设置最大凭证数量。使系数为质数有助于减少碰撞。
在这种情况下,凭证 #100、101 和 102 的编号分别为 26158、12076 和 6949。将其视为一种玩具加密,其中系数是您的密钥。不是超级安全,但没有任何输出 space 像你所要求的那样小的东西可以抵御强大的对手。但这应该可以阻止日常欺诈者。
确认凭证有效需要用电脑(但只计算,不存储)。它会遍历几千或几万个输入 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();
}
}
}
感谢 Blogbeard and lc 对我最初 post 的评论提供的帮助。事实证明,我们无论如何都需要在生成代码时命中存储,所以这意味着实施 PRNG 对我们来说是一个更好的选择,而不是搞乱加密。
这就是我们最终所做的
- 继续使用我们的序号生成器生成整数
- 使用序列号作为种子创建 C# Random class(PRNG)实例。
- 在我们想要的最小和最大数范围内生成一个随机数。
- 检查重复项并重新生成,直到我们找到一个唯一的
事实证明,在使用序列号作为每一代的种子时,使用带有种子的 c# random 使得随机数实际上非常可预测。
例如,使用顺序种子,范围在 1 到 999999 之间,我测试了在没有一次碰撞的情况下生成 500000 个值。