jjzjj

c# - 为什么我的字节在加密算法的这个 C# 端口的第四轮不同?

coder 2024-05-27 原文

我正在尝试将 C++ 代码移植到 C# 并且在大多数情况下它都可以正常工作,但仅适用于循环的前 3 轮。在第四轮,输入 block 的字节开始不同,我不明白为什么。如果我们假设 C++ 版本是正确的实现,为什么 C# 代码在第四轮给出不同的结果。下面是我的结果和代码(C++/CLR 和 C# 的控制台应用程序)

我认为输入 block 在传递给 AES 之前在每一轮中创建的方式有所不同(在 C++ 中,有一种方法可以转换为基数 256,to_base_256from_base_256) 但在 C# 中,我将基本 256 字节数组直接转换为 BigInteger,然后再转换回字节数组。我只是不知道为什么每个人都会在前 3 轮中产生相同的输入 block 值,但在第四轮中却不会。

编辑: 经过更多调试后,我缩小了问题开始出现在 for 循环中这一行的位置,当 i = 2(第 3 轮)

BigInteger AESResult = new BigInteger(t);

在对我的字节数组 t 包含的 block 进行 AES 加密后

23 , 111 , 30 , 144 , 117 , 161 , 87 , 113 , 157 , 52 , 215 , 157 , 130 , 135 , 20 , 184

但是,当我使用上面的行将这些字节转换为 BigInteger 时,值上的符号突然变为负数,并且从那里开始走下坡路。该值甚至不会像前几轮那样显示在 Locals 窗口中。

输出结果

ROUND 1

INPUT
C++ 224,144,103,1,0,0,0,0,0,0,0,0,0,0,0,0,
C# 224,144,103,1,0,0,0,0,0,0,0,0,0,0,0,0,
AES ENCRYPTED
C++ 175,19,208,16,98,242,219,41,136,137,124,214,117,242,222,20,
C# 175,19,208,16,98,242,219,41,136,137,124,214,117,242,222,20,

ROUND 2

INPUT
C++ 168,68,153,2,0,0,0,0,0,0,0,0,1,0,0,0,
C# 168,68,153,2,0,0,0,0,0,0,0,0,1,0,0,0,
AES ENCRYPTED
C++ 182,186,181,102,204,102,32,32,232,213,226,133,59,128,225,109,
C# 182,186,181,102,204,102,32,32,232,213,226,133,59,128,225,109,

ROUND 3

INPUT
C++ 150,126,97,5,0,0,0,0,0,0,0,0,2,0,0,0,
C# 150,126,97,5,0,0,0,0,0,0,0,0,2,0,0,0,
AES ENCRYPTED
C++ 23,111,30,144,117,161,87,113,157,52,215,157,130,135,20,184,
C# 23,111,30,144,117,161,87,113,157,52,215,157,130,135,20,184,

ROUND 4

INPUT
C++ 191,210,191,0,0,0,0,0,0,0,0,0,3,0,0,0,
C# 191,255,174,252,0,0,0,0,0,0,0,0,3,0,0,0,
AES ENCRYPTED
C++ 130,187,182,115,251,12,63,157,109,110,234,35,137,208,172,203,
C# 248,197,125,177,46,103,91,217,246,8,202,219,115,4,213,37,

C++ CLR 控制台应用程序

// ConsoleApplication2.cpp : main project file.

#include "stdafx.h"

using namespace System;
using namespace System::Security::Cryptography;


void unpack(unsigned int a, unsigned char *b)
{ /* unpack bytes from a word */
    b[0] = unsigned char(a);
    b[1] = unsigned char(a >> 8);
    b[2] = unsigned char(a >> 16);
    b[3] = unsigned char(a >> 24);

}

array<unsigned char>^ AES_encrypt_block(array<unsigned char>^ plainText)
{
    array<unsigned char>^ key = gcnew array<unsigned char>(16);
    key[0] = 0x01; key[1] = 0x01; key[2] = 0x01; key[3] = 0x01; key[4] = 0x01; key[5] = 0x01; key[6] = 0x01; key[7] = 0x01;
    key[8] = 0x01; key[9] = 0x01; key[10] = 0x01; key[11] = 0x01; key[12] = 0x01; key[13] = 0x01; key[14] = 0x01; key[15] = 0x01;

    AesManaged^ AES = gcnew AesManaged();
    AES->BlockSize = 128;
    AES->KeySize = 128;
    AES->Key = key;
    AES->Mode = CipherMode::ECB;
    AES->Padding = PaddingMode::None;

    array<unsigned char>^ output_buffer = gcnew array<unsigned char>(16);

    ICryptoTransform^ encryptor = AES->CreateEncryptor(AES->Key, AES->IV);
    encryptor->TransformBlock(plainText, 0, plainText->Length, output_buffer, 0);

    return output_buffer;
}

void from_base_256(unsigned char *y, int len, int s, char *x)
{
    int i, m, n;
    unsigned int c, d;

    m = 16;
    n = 0; c = 0;
    for (;;)
    {
        while (m>0 && y[m - 1] == 0) m--;
        d = 0;

        for (i = m - 1; i >= 0; i--)
        {
            d = (d << 8) + y[i];
            y[i] = d / s;
            d %= s;
        }


        d += c + x[n]; c = 0;
        if ((int)d >= s)
        {
            c = 1; x[n] = d - s;
        }
        else x[n] = d;

        n++;
        if (n >= len) break;
    }
}

int to_base_256(char *x, int len, int s, unsigned char *y)
{
    int i, j, m;
    unsigned int c;

    for (i = 0; i<16; i++)
        y[i] = 0;
    if (len == 0) return 0;

    m = 1; y[0] = x[len - 1];
    for (j = len - 2; j >= 0; j--)
    { 
        c = x[j];
        for (i = 0; i<m; i++)
        {
            c += (unsigned int)y[i] * s;
            y[i] = c & 0xff;
            c >>= 8;
        }
        if (c>0) { m++; y[m - 1] = c; }
    }

    return m;
}



int main(array<System::String ^> ^args)
{


    int i, n;

    //PLAINTEXT
    char x[256]; 
    x[0] = 1; x[1] = 4; x[2] = 2; x[3] = 5; x[4] = 6; x[5] = 9; x[6] = 8; x[7] = 7;
    x[8] = 2; x[9] = 1; x[10] = 5; x[11] = 4; x[12] = 6; x[13] = 5; x[14] = 3; x[15] = 2;

    unsigned int TL, TR;

    TR = 0;     
    TL = 0;

    int j;
    char *left, *right;
    unsigned char buff[16];
    int l, r;
    l = r = 16 / 2;
    if (16 % 2 == 1) l++;

    left = &x[0]; right = &x[l];

    for (j = 0; j < 8; j++)
    {
        System::Diagnostics::Debug::WriteLine("");
        System::Diagnostics::Debug::WriteLine("ROUND " + (j+1));

        if (j % 2 == 0)
        {
            to_base_256(right, r, 10, buff);

            unpack(TR^j, &buff[12]);

            int size = sizeof(buff) / sizeof(*buff);
            array<unsigned char>^ inputPlaintext = gcnew array<unsigned char>(size);
            for (int i = 0; i < size; i++)
                inputPlaintext[i] = buff[i];

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("INPUT");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(inputPlaintext[z] + ",");

            array<unsigned char>^ result = AES_encrypt_block(inputPlaintext);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("AES ENCRYPTED");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(result[z] + ",");

            pin_ptr<unsigned char>buff = &result[0];

            from_base_256( buff, l, 10, left);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("afterFromBase256 - left");
            for (int z = 0; z < sizeof(left) / sizeof(*left); z++)
                System::Diagnostics::Debug::Write(left[z] + " , ");
        }
        else
        {
            to_base_256(left, l, 10, buff);

            unpack(TL^j, &buff[12]);

            int size = sizeof(buff) / sizeof(*buff);
            array<unsigned char>^ inputPlaintext = gcnew array<unsigned char>(size);
            for (int i = 0; i < size; i++)
                inputPlaintext[i] = buff[i];

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("INPUT");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(inputPlaintext[z] + ",");

            array<unsigned char>^ result = AES_encrypt_block(inputPlaintext);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("AES ENCRYPTED");
            for (int z = 0; z < size; z++)
                System::Diagnostics::Debug::Write(result[z] + ",");

            pin_ptr<unsigned char>buff = &result[0];

            from_base_256( buff, r, 10, right);

            System::Diagnostics::Debug::WriteLine("");
            System::Diagnostics::Debug::WriteLine("afterFromBase256 - right");
            for (int z = 0; z < sizeof(right) / sizeof(*right); z++)
                System::Diagnostics::Debug::Write(right[z] + " , ");
        }
    }

    return 0;
}

C# 控制台应用程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Numerics;

namespace BPS_ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {

            //integer array to hold the bytes of plaintext 
            int[] plaintext = new int[16] { 1, 4, 2, 5, 6, 9, 8, 7, 2, 1, 5, 4, 6, 5, 3, 2 };

            byte[] key = new byte[16];
            key[0] = 0x01; key[1] = 0x01; key[2] = 0x01; key[3] = 0x01; key[4] = 0x01; key[5] = 0x01; key[6] = 0x01; key[7] = 0x01;
            key[8] = 0x01; key[9] = 0x01; key[10] = 0x01; key[11] = 0x01; key[12] = 0x01; key[13] = 0x01; key[14] = 0x01; key[15] = 0x01;

            //Block Cipher - AES-128
            AES AESEncrypt = new AES(key);

            int bits = 128 - 32; //128 block for AES-128

            //tweak
            int tweak = 0; //64 bit user provided tweak value
            int TR = 0; //left side of tweak
            int TL = 0; //right side of tweak

            int s = 10;

            int w = 8; //recommended number of rounds

            int BLOCK_SIZE = 16; //block size in bytes (16 = 128 bits for AES-128)
            int b = 0; //s-integer input length


            b = plaintext.Length;

            //Split the tweak into right and left
            //

            TR = tweak % (1 << 32);
            TL = (tweak - TR) / (1 << 32);

            //Split the plaintext into left and right substrings
            //
            int j = 0;
            int[] XR; //right substring
            int[] XL; //left substring

            int l; //length of left substring
            int r; //length of right substring

            if (b % 2 == 1) //b is odd
            {
                l = (b + 1) / 2;
                r = (b - 1) / 2;
            }
            else //b is even
            {
                l = r = b / 2;
            }

            XL = new int[l];
            XR = new int[r];

            for (int i = 0; i < l; i++)
                XL[i] = plaintext[i];

            j = 0;
            for (int i = l; i <= l + r - 1; i++, j++)
                XR[j] = plaintext[i];


            //initialize left and right branches

            BigInteger L = 0;

            for (int i = 0; i < l; i++)
            {
                L += XL[i] * BigInteger.Pow(s, i);
            }

            BigInteger R = 0;

            for (int i = 0; i < l; i++)
            {
                R += XR[i] * BigInteger.Pow(s, i);
            }


            byte[] initial_Lbytes = L.ToByteArray();
            byte[] initial_Rbytes = R.ToByteArray();

            int[] intitial_L = new int[l];
            int[] intitial_R = new int[r];

            foreach (byte bL in initial_Lbytes)
            {
                BigInteger num = new BigInteger(new byte[] { bL });
            }

            //8 Rounds
            for (int i = 0; i < 8; i++)
            {
                System.Diagnostics.Debug.WriteLine("");
                System.Diagnostics.Debug.WriteLine("ROUND " + (i + 1));

                if (i % 2 == 0) //even
                {
                    byte[] RBytes = R.ToByteArray();

                    byte[] inputPlaintext = new byte[16];
                    for (int k = 0; k < RBytes.Length; k++)
                        inputPlaintext[k] = RBytes[k];

                    inputPlaintext = INT2LE(TR ^ i, inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("INPUT");
                    foreach (byte bb in inputPlaintext)
                        System.Diagnostics.Debug.Write(bb + ",");

                    byte[] t = AESEncrypt.Encrypt(inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("");
                    System.Diagnostics.Debug.WriteLine("AES ENCRYPTED");
                    foreach (byte bb in t)
                        System.Diagnostics.Debug.Write(bb + ",");

                    BigInteger AESResult = new BigInteger(t);

                    BigInteger res = (L + AESResult) % BigInteger.Pow(s, l);

                    L = res;

                }
                else //odd
                {

                    byte[] LBytes = L.ToByteArray();


                    byte[] inputPlaintext = new byte[16];
                    for (int k = 0; k < LBytes.Length; k++)
                        inputPlaintext[k] = LBytes[k];

                    inputPlaintext = INT2LE(TL ^ i, inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("INPUT");
                    foreach (byte bb in inputPlaintext)
                        System.Diagnostics.Debug.Write(bb + ",");

                    byte[] t = AESEncrypt.Encrypt(inputPlaintext);

                    System.Diagnostics.Debug.WriteLine("");
                    System.Diagnostics.Debug.WriteLine("AES ENCRYPTED");
                    foreach (byte bb in t)
                        System.Diagnostics.Debug.Write(bb + ",");

                    BigInteger AESResult = new BigInteger(t);

                    BigInteger res = (R + AESResult) % BigInteger.Pow(s, r);

                    R = res;
                }
            }

            BigInteger FINAL_R = R;
            BigInteger FINAL_L = L;

        }

        public static byte[] INT2LE(Int32 data, byte[] arr)
        {
            byte[] b = arr;
            b[12] = (byte)data;
            b[13] = (byte)(((uint)data >> 8) & 0xFF);
            b[14] = (byte)(((uint)data >> 16) & 0xFF);
            b[15] = (byte)(((uint)data >> 24) & 0xFF);
            return b;
        }
    }


    public class AES : IBlockCipher
    {
        private byte[] _key;
        public AES(byte[] key)
        {
            _key = key;
        }
        public byte[] Encrypt(byte[] input)
        {
            byte[] output_buffer = new byte[16];
            using (AesManaged E = new AesManaged())
            {
                E.BlockSize = 128;
                E.KeySize = 128;
                E.Mode = CipherMode.ECB;
                E.Key = _key;
                E.Padding = PaddingMode.None;
                //E.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = E.CreateEncryptor(E.Key, E.IV);
                encryptor.TransformBlock(input, 0, 16, output_buffer, 0);

            }

            //return encrypted;
            return output_buffer;
        }
    }

    interface IBlockCipher
    {
        byte[] Encrypt(byte[] input);
    }


}

最佳答案

BigInteger constructor 的文档明确指出:

The constructor expects positive values in the byte array to use sign-and-magnitude representation, and negative values to use two's complement representation. In other words, if the highest-order bit of the highest-order byte in value is set, the resulting BigInteger value is negative. Depending on the source of the byte array, this may cause a positive value to be misinterpreted as a negative value.

有几种方法可以解决这个问题,其中最简单的方法是简单地将零字节附加到字节数组,否则它会被解释为负数。这是执行此操作的简单方法。

public static BigInteger BuildPositiveBigInteger(byte [] littleEndianBytes) {
    if (littleEndianBytes[littleEndianBytes.Length-1] >= 0x80) {
        byte[] newBytes = new byte[littleEndianBytes.Length + 1];
        littleEndianBytes.CopyTo (newBytes, 0);
        return new BigInteger (newBytes);
    } else {
        return new BigInteger (littleEndianBytes);
    }
}

在您的代码中,如果您将 new BigInteger(byte[]) 的所有实例替换为调用 BuildPositiveBigInteger 它应该按预期运行。

关于c# - 为什么我的字节在加密算法的这个 C# 端口的第四轮不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33188681/

有关c# - 为什么我的字节在加密算法的这个 C# 端口的第四轮不同?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  9. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  10. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

随机推荐