将VB6中的Randomize转换为C#

3
我正在编写一个C#前端,用于连接使用VB6的Rnd()和Randomize()方法进行用户密码加密的遗留数据库。这个加密函数非常简单,实际上并不是很安全,但所有当前的密码都是用它存储的。
我想在C#应用程序中对古老的用户进行身份验证。我可以为VB6编写新的加密(或更好的哈希)代码,以使所有未来用户都具有更安全的密码,并且这可以在C#中复制。但我不想要求当前用户在能够使用新前端之前重置其密码。
是否有任何办法可以在C#中重新实现该算法,以便其生成与遗留的VB6代码相同的结果?

当然是可以的,但需要一些VB6的示例代码来帮助翻译。 - JaredPar
它已经在问题中链接了。 - Tom Mayfield
4
与其试图精确匹配VB6的未指定行为,为什么不在VB6中编写一个控件以执行所需操作,然后构建一个可运行时调用包装器来调用该代码,以便您可以从C#程序中调用它? - Eric Lippert
3个回答

2
“这应该是可能的。棘手的部分将是模拟对 Visual Basic 的 Randomize 语句和 Rnd 函数的调用。
我刚刚找到了一篇看起来可能包含你需要的信息的知识库文章:

Visual Basic 如何为 RND 函数生成伪随机数

编辑... 经过一些调查,最近版本的 Visual Basic 中的 RandomizeRnd 实现使用与 VB6 完全相同的算法。
所以,好消息是你不需要自己找出并重新实现 VB6 算法。只需导入 Microsoft.VisualBasic 命名空间,就可以从 C# 中调用内置方法:”
using Microsoft.VisualBasic;

// ...

float x = VBMath.Rnd(-1);
VBMath.Randomize(password.Length);
float y = VBMath.Rnd();
// etc

(如果您仍然对实际使用的算法感到好奇,您可以随时在反编译器中查看!)

已经成功地复制了Rnd()的行为--在不改变种子的情况下,我得到了与VBA代码相同的序列。但是我似乎找不到一个确切的参考来复制Randomize()的行为。 - Tom Mayfield
我从KB文章中的印象是(虽然没有明确说明)Randomize只是重新播种随机数发生器。所以,例如"Randomize Len(password)" 将变成 "x0=password.Length"。你试过这个方法吗? - LukeH
是的。http://www.noesis.net.au/main/Resources/Resources/prng.html 提供了一个粗略的参考,但它绝对搞乱了数据。 - Tom Mayfield
你可以从VB参考中了解到,需要Rnd(负数)的函数会使用当前种子生成新的种子。 Rnd(负数)使用该负值作为计算的种子,这本身会重新设置为特定值。 我正在复制的代码使用Rnd(-1):Randomize(Len(password))。 - Tom Mayfield

1

您可以从VB6和C#生成相同的序列。只需注意四舍五入误差(C#的结果更精确)。在将新种子传递给VBMath.Randomize()之前,请确保调用VBMath.Rnd(-1)

[TestFixture]
public class VbaRandomTests
{
    // Random numbers generated from a known seed from VB6
    [TestCase(1, new[] { 0.333575300f, 0.068163870f, 0.593829300f, 0.766039500f, 0.189289400f, 0.537398600f, 0.326994400f, 0.393937000f, 0.073419150f, 0.831542500f, 0.854963000f, 0.828829900f, 0.962344000f, 0.833957400f, 0.090149820f, 0.645974500f, 0.192794900f, 0.346950500f, 0.188133400f, 0.691135000f })]
    [TestCase(32, new[] { 0.579913200f, 0.579150200f, 0.310870300f, 0.864916400f, 0.142658500f, 0.927291200f, 0.407316600f, 0.402970200f, 0.296319500f, 0.412841300f, 0.361066500f, 0.560519300f, 0.017275630f, 0.919162500f, 0.084534590f, 0.912820200f, 0.642257800f, 0.248561900f, 0.733299400f, 0.305637000f })]
    [TestCase(327680, new[] { 0.882708600f, 0.733264000f, 0.661029000f, 0.376940400f, 0.919086800f, 0.660506500f, 0.020170630f, 0.126908200f, 0.437005600f, 0.053283210f, 0.252240800f, 0.449496400f, 0.662844500f, 0.044955970f, 0.519654200f, 0.169961300f, 0.183334400f, 0.687831900f, 0.227989400f, 0.384067200f })]
    public void generates_same_results_as_VB6(int seed, float[] values)
    {
        VBMath.Rnd(-1);
        VBMath.Randomize(seed);

        float[] results = new float[values.Length];
        for (int index = 0; index < results.Length; index++)
        {
            results[index] = VBMath.Rnd();
        }

        CollectionAssert.AreEqual(values, results, new FloatEpsilonComparer(0.0000001f));
    }

    private class FloatEpsilonComparer
        : IComparer<float>, IComparer
    {
        private readonly float _epsilon;

        public FloatEpsilonComparer(float epsilon)
        {
            _epsilon = epsilon;
        }

        public int Compare(float x, float y)
        {
            float difference = x - y;

            if (Math.Abs(difference) < _epsilon)
            {
                return 0;
            }
            if (x < y)
            {
                return -1;
            }
            return 1;
        }

        public int Compare(object x, object y)
        {
            float xF = Convert.ToSingle(x);
            float yF = Convert.ToSingle(y);
            return Compare(xF, yF);
        }
    }
}

0

示例VB代码:

Randomize()
Dim x as Single = Rnd()

(大致)等效的 C# 代码:

Random r = new Random();
double x = r.NextDouble();

Random类的构造函数使用当前时间初始化随机数生成器,这就是Randomize所做的。您还可以将种子传递给构造函数,这相当于使用种子参数调用Randomize


我需要这个函数返回与VB6相同的确定性结果:重新实现算法是可行的(结果类似,但不完全相同)。复制该算法是我需要帮助的地方。 - Tom Mayfield
基本上,给定一个已知的种子,我需要C#随机函数(或封送的COM对象,或其他什么)返回相同的随机数。 - Tom Mayfield
我认为你需要将Random对象设为静态,否则你将无法获得足够的随机性。 - code4life
@code4life,这取决于你对它的使用方式...如果你只调用一次(或不太频繁),每次创建一个新的Random对象是可以的。但如果你在循环中使用它,当然应该重复使用同一个Random对象。 - Thomas Levesque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接