Algorithm 用字母表编码字节数组,输出看起来应该是随机分布的

Algorithm 用字母表编码字节数组,输出看起来应该是随机分布的,algorithm,random,encoding,base64,Algorithm,Random,Encoding,Base64,我正在编码二进制数据b1,b2。。。bn使用字母表。但是由于bs的二进制表示或多或少是顺序的,因此位到字符的简单映射会产生非常相似的字符串。例如: encode(b1) => "QXpB4IBsu0" encode(b2) => "QXpB36Bsu0" ... 我正在寻找使输出更“随机”的方法,这意味着在查看输出字符串时更难猜测输入b 一些要求: 对于不同的bs,输出字符串必须不同。对相同的b进行多次编码不一定会产生相同的输出。只要不同输入bs的输出字符串之间没有冲突,一切正常

我正在编码二进制数据
b1,b2。。。bn
使用字母表。但是由于
b
s的二进制表示或多或少是顺序的,因此位到字符的简单映射会产生非常相似的字符串。例如:

encode(b1) => "QXpB4IBsu0"
encode(b2) => "QXpB36Bsu0"
...
我正在寻找使输出更“随机”的方法,这意味着在查看输出字符串时更难猜测输入
b

一些要求:

  • 对于不同的
    b
    s,输出字符串必须不同。对相同的
    b
    进行多次编码不一定会产生相同的输出。只要不同输入
    b
    s的输出字符串之间没有冲突,一切正常
  • 如果它很重要:每个
    b
    大约为50-60位。字母表包含64个字符
  • 编码函数不应产生比您仅使用从
    b
    s位到字母表字符的简单映射得到的输出字符串更大的输出字符串(给定上述值,这意味着每个
    b
    约10个字符)。因此,仅仅使用像SHA这样的散列函数是不可取的
这个问题的可能解决方案不需要是“加密安全的”。如果有人投入足够的时间和精力来重建二进制数据,那就这样吧。但目标是让它尽可能困难。这可能有助于不需要解码功能

我现在正在做的是:

  • 从二进制数据中取下4位,比如说
    xxxx
  • 预加2个随机位
    r
    以获得
    rrxxxx
  • 在字母表中查找相应的字符:
    val char=alphabet[rrxxxx]
    并将其添加到结果中(因为字母表的大小是64)
  • 继续执行步骤1
  • 这种方法给输出字符串增加了一些噪声,但是,由于随机位的存在,字符串的大小增加了50%。我可以通过添加更多随机位来添加更多噪声(
    rrrxxx
    甚至
    rrrrxx
    ),但输出会越来越大。我上面提到的一个要求是不增加输出字符串的大小。目前我只使用这种方法,因为我没有更好的想法

    作为一种替代方法,我考虑在应用字母表之前对输入的
    b
    位进行洗牌。但是,因为必须保证不同的
    b
    s会产生不同的字符串,所以洗牌函数应该使用某种确定性(可能是一个秘密数作为参数),而不是完全随机的。我没能想出这样一个函数


    我想知道是否有更好的方法,任何提示都值得赞赏。

    基本上,您需要从每个可能的50位值到另一个50位值的可逆伪随机映射。您可以使用可逆(用于某些伪随机数生成器的那种)来实现这一点

    编码时,在前进方向将LCG应用于您的号码,然后使用base64编码。如果需要解码,请从base64解码,然后在相反方向应用LCG以恢复原始号码


    包含可逆LCG的一些代码。你需要一个周期为250的。用于定义LCG的常数将是您的密码。

    基本上,您需要从每个可能的50位值到另一个50位值的可逆伪随机映射。您可以使用可逆(用于某些伪随机数生成器的那种)来实现这一点

    编码时,在前进方向将LCG应用于您的号码,然后使用base64编码。如果需要解码,请从base64解码,然后在相反方向应用LCG以恢复原始号码

    包含可逆LCG的一些代码。你需要一个周期为250的。用于定义LCG的常量将是您的密码。

    您想使用密码。这将获取序列键并将其转换为非序列号。键与其结果之间存在一对一的关系。因此,没有两个数字会创建相同的非顺序密钥,并且该过程是可逆的

    我有一个用C#编写的小例子,说明了这个过程

    private void DoIt()
    {
        const long m = 101;
        const long x = 387420489; // must be coprime to m
    
        var multInv = MultiplicativeInverse(x, m);
    
        var nums = new HashSet<long>();
        for (long i = 0; i < 100; ++i)
        {
            var encoded = i*x%m;
            var decoded = encoded*multInv%m;
            Console.WriteLine("{0} => {1} => {2}", i, encoded, decoded);
            if (!nums.Add(encoded))
            {
                Console.WriteLine("Duplicate");
            }
        }
    }
    
    private long MultiplicativeInverse(long x, long modulus)
    {
        return ExtendedEuclideanDivision(x, modulus).Item1%modulus;
    }
    
    private static Tuple<long, long> ExtendedEuclideanDivision(long a, long b)
    {
        if (a < 0)
        {
            var result = ExtendedEuclideanDivision(-a, b);
            return Tuple.Create(-result.Item1, result.Item2);
        }
        if (b < 0)
        {
            var result = ExtendedEuclideanDivision(a, -b);
            return Tuple.Create(result.Item1, -result.Item2);
        }
        if (b == 0)
        {
            return Tuple.Create(1L, 0L);
        }
        var q = a/b;
        var r = a%b;
        var rslt = ExtendedEuclideanDivision(b, r);
        var s = rslt.Item1;
        var t = rslt.Item2;
        return Tuple.Create(t, s - q*t);
    }
    
    private void DoIt()
    {
    常数长m=101;
    const long x=387420489;//必须是m的互质
    var multInv=乘法逆(x,m);
    var nums=new HashSet();
    用于(长i=0;i<100;++i)
    {
    var编码=i*x%m;
    已解码变量=已编码*多NV%m;
    WriteLine(“{0}=>{1}=>{2}”,i,编码,解码);
    如果(!nums.Add(编码))
    {
    控制台。写入线(“副本”);
    }
    }
    }
    专用长乘法反转(长x,长模)
    {
    返回扩展的欧几里德视觉(x,模数)。项1%模数;
    }
    私有静态元组扩展欧几里德视觉(长a,长b)
    {
    if(a<0)
    {
    var结果=扩展欧几里德视觉(-a,b);
    返回Tuple.Create(-result.Item1,result.Item2);
    }
    if(b<0)
    {
    var结果=扩展欧几里德视觉(a,-b);
    返回Tuple.Create(result.Item1,-result.Item2);
    }
    如果(b==0)
    {
    返回元组。创建(1L,0L);
    }
    var q=a/b;
    var r=a%b;
    var rslt=扩展的欧几里德视觉(b,r);
    var s=rslt.Item1;
    var t=rslt.Item2;
    返回Tuple.Create(t,s-q*t);
    }
    
    上述文章中的代码抄袭,以及支持材料

    这样做的想法是,取你的序列号,计算逆数,然后用base-64编码。要反转此过程,请对给定的值进行base-64解码,然后进行反向计算,得到原始数字。

    如果要使用。这将获取序列键并将其转换为非序列号。这是一对一