让.NET中的SlowAES和RijndaelManaged类一起玩

时间:2021-09-22 17:21:08

I'm trying to setup AES encryption / decryption using the javascript library SlowAES and the RijndaelManaged class in .NET.

我正在尝试使用JavaScript中的JavaScript库SlowAES和RijndaelManaged类来设置AES加密/解密。

I chose this method after reading this post, where Cheeso has managed to get these two encryption methods to play together

我在阅读这篇文章后选择了这种方法,Cheeso设法让这两种加密方法一起玩

"In my tests of the COM-wrapped-SlowAEs, I used CBC mode, and the encryption was completely compatible with the RijndaelManaged class in .NET" - Cheeso

“在我对COM-wrapped-SlowAEs的测试中,我使用了CBC模式,加密与.NET中的RijndaelManaged类完全兼容” - Cheeso

I've taken the javascript code from Cheeso's Windows Scripting Component, the latest slowaes libraries, and using the following javascript script to test:

我从Cheeso的Windows Scripting Component,最新的slowaes库中获取了javascript代码,并使用以下javascript脚本来测试:

var key = "12345678901234567890123456789012";
var message = "watson?";
var decrypted;

slowAES.aes.keySize.SIZE_256;
slowAES.modeOfOperation.CBC;
put_PassPhrase(key);
var result = EncryptString(message);
decrypted = DecryptCommaDelimitedStringToString(result)
document.write("Key:" + key + "<br />original:" + message + "<br />Cypher:" + result + "<br />Decrypted:" + decrypted + "<br />IV(): " + get_IV());

I'm getting the following output:

我得到以下输出:

Key:12345678901234567890123456789012
original:watson?
Cypher:245,159,1,1,168,1,1,143,1,1,146,1,1,239,117,1
Decrypted:watson? 
IV(): 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

I've modified the following example found on MSDN to try and match the encryption in C# :

我修改了MSDN上的以下示例,尝试匹配C#中的加密:

public static void Main()
        {
            try
            {
                string original = "watson?";
                byte[] IV = new byte[16];   // match slowaes IV
                byte[] key = new System.Text.ASCIIEncoding().GetBytes("12345678901234567890123456789012");// match slowaes KEY

                RijndaelManaged myRijndael = new RijndaelManaged();
                myRijndael.BlockSize = 128;
                myRijndael.KeySize = 256;
                myRijndael.Mode = CipherMode.CBC;

                // Encrypt the string to an array of bytes.
                byte[] encrypted = encryptStringToBytes_AES(original, key, IV);

                // Decrypt the bytes to a string.
                string roundtrip = decryptStringFromBytes_AES(encrypted, key, IV);

                //Display the original data and the decrypted data.
                Console.WriteLine("Original:   {0}", original);
                Console.WriteLine("Round Trip: {0}", roundtrip);

            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }

Watch of the byte array:

观察字节数组:

-  encrypted {byte[16]} byte[]
  [0] 139 byte
  [1] 104 byte
  [2] 166 byte
  [3] 35 byte
  [4] 8 byte
  [5] 42 byte
  [6] 216 byte
  [7] 160 byte
  [8] 235 byte
  [9] 153 byte
  [10] 23 byte
  [11] 143 byte
  [12] 105 byte
  [13] 3 byte
  [14] 24 byte
  [15] 255 byte

I've tried all the padding options with the managed .NET class, however I can't get the encrypted outputs to match. Can anyone help me?

我已经尝试了托管.NET类的所有填充选项,但是我无法获得匹配的加密输出。谁能帮我?

Thanks,

Bob

1 个解决方案

#1


37  

maybe I can help.

也许我可以帮忙。

I took your C# code and modified it slightly. The C# code I use, in its entirety, is:

我拿了你的C#代码并稍微修改了一下。我使用的C#代码完整地是:

using System;
using System.Security.Cryptography;

public class Bob
{
    internal static string FormatByteArray(byte[] b)
    {
        System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
        int i = 0;
        for (i = 0; i < b.Length; i++)
        {
            if (i != 0 && i % 16 == 0)
                sb1.Append("\n");
            sb1.Append(System.String.Format("{0:X2} ", b[i]));
        }
        return sb1.ToString();
    }

    public static void Main()
    {
        try
        {
            string original = "watson?";

            Console.WriteLine("Original:   {0}", original);

            byte[] IV = new byte[16];   // match slowaes IV
            var ascii = new System.Text.ASCIIEncoding();

            // match slowaes KEY
            string passPhrase = "12345678901234567890123456789012";
            byte[] key = ascii.GetBytes(passPhrase);

            RijndaelManaged myRijndael = new RijndaelManaged();
            myRijndael.BlockSize = 128;
            myRijndael.KeySize = 256;
            myRijndael.IV = IV;
            myRijndael.Padding   = PaddingMode.PKCS7;
            myRijndael.Mode = CipherMode.CBC;
            myRijndael.Key = key;

            // Encrypt the string to an array of bytes.
            byte[] plainText = new System.Text.ASCIIEncoding().GetBytes(original);
            ICryptoTransform transform = myRijndael.CreateEncryptor();
            byte[] cipherText = transform.TransformFinalBlock(plainText, 0, plainText.Length);

            Console.WriteLine("cipherText: {0}", FormatByteArray(cipherText));

            // Decrypt the bytes to a string.
            transform = myRijndael.CreateDecryptor();
            plainText = transform.TransformFinalBlock(cipherText, 0, cipherText.Length);


            string roundtrip = ascii.GetString(plainText);

            Console.WriteLine("Round Trip: {0}", roundtrip);

        }
        catch (Exception e)
        {
            Console.WriteLine("Error: {0}", e.Message);
        }
    }

}

compile the above with

编译上面的

csc.exe /target:exe /out:Bob.exe Bob.cs

I use the slowAES.wsc from the other post you referenced, with 2 changes: I do not call getPaddedBlock() for the key in the EncryptString or DecryptString() methods. This really needs a PBKDF but let's not worry about that now. Here's what the modified EncryptString looks like:

我使用你引用的其他帖子中的slowAES.wsc,有2个更改:我没有为EncryptString或DecryptString()方法中的键调用getPaddedBlock()。这真的需要一个PBKDF,但我们现在不用担心。以下是修改后的EncryptString的样子:

function EncryptString(plainText)
{
  // this is really wrong - need a PBBKDF to get the key, instead
  // of just using the passphrase
  var key = cryptoHelpers.convertStringToByteArray(_passphrase);
  // var nkey = slowAES.getPaddedBlock(key, 0, _keysize, _mode);
  var bytesToEncrypt = cryptoHelpers.convertStringToByteArray(plainText);

  var result = slowAES.encrypt(bytesToEncrypt, 
      _mode,
      key,
      _keysize,
      _iv);
  return result['cipher'];
}

This means you have to use a passPhrase that is exactly the length required by the keysize. If you use AES256, then pass a 32-char string (32 * 8 = 256 bits). Seems like you figured this out already.

这意味着您必须使用passPhrase,它恰好是keysize所需的长度。如果使用AES256,则传递32个字符串(32 * 8 = 256位)。好像你已经想到了这一点。

The client of the WSC component is also Javascript (though it can be any COM language). Here's what I used.

WSC组件的客户端也是Javascript(尽管它可以是任何COM语言)。这是我用的。

function toHexString(a)
{
    var ret = '';
    for(var i = 0;i < a.length;i++)
        ret += (a[i] < 16 ? '0' : '') + a[i].toString(16) + ' ';
    return ret.toLowerCase();
}


//var plaintext = "Hello. This is a test. of the emergency broadcasting system.";
var plaintext = "watson?";

try
{
    WScript.echo( "plaintext: " + plaintext);
    WScript.echo( "plaintext.length: " + plaintext.length);

    WScript.echo( "instantiate ");
    var aes = new ActiveXObject("Ionic.Com.SlowAES");

    WScript.echo( "keysize ");
    aes.KeySize = 256;

    WScript.echo( "passphrase ");
    aes.PassPhrase= "12345678901234567890123456789012";  // 32 chars long

    WScript.echo( "mode ");
    aes.Mode = "CBC";

    WScript.echo( "encrypting... ");
    var result = aes.EncryptString(plaintext);

    WScript.echo( "Cryptotext: " + toHexString(result));

    WScript.echo( "decrypting... ");
    var decrypted = aes.DecryptBytesToString(result);

    WScript.echo( "decrypted: " + decrypted);
}
catch(e)
{
    WScript.echo("Exception: " + e); 
    //     WScript.echo(e.Number + ": " + e.Name);
    WScript.echo(e.Message);
}

If I then run this code, the Javascript and C# produces the same cipher text for a plaintext of "watson?", using AES256, passphrase of 12345678901234567890123456789012, and an IV of 16 bytes of zero. The ciphertext generated is:

如果我然后运行此代码,Javascript和C#使用AES256,密码为12345678901234567890123456789012,以及16字节零的IV,为“watson?”的明文生成相同的密文。生成的密文是:

8B 68 A6 23 08 2A D8 A0 EB 99 17 8F 69 03 18 FF

It successfully decrypts in both cases.

它在两种情况下都成功解密。

EDIT: Though I packaged the slowAES encryption in a WSC, it will be interoperable running outside the COM environment as well. The WSC part is unnecessary for this question, but was necessary to demonstrate the answer for a prior question, which was, "how can I get VBScript and .NET AES to interoperate?"

编辑:虽然我在一个WSC中打包了slowAES加密,但它也可以在COM环境之外运行。 WSC部分对于这个问题是不必要的,但是有必要证明先前问题的答案,即“如何才能使VBScript和.NET AES进行互操作?”

EDIT2: The source code that demonstrates AES interop between Javascript or VBScript and .NET is available. I extended the basic example given here to produce test apps in 3 languages: C#, Javascript and VBScript. They all take the same set of arguments. They each use a RFC2898-compliant key derivation function. You can specify the password, salt, IV, and plaintext, as well as the number of RFC2898 iterations to use in the PBKDF2. You can easily verify that the ciphertext is the same for each of these test programs. Maybe the example will be useful for someone.

EDIT2:演示了Javascript或VBScript与.NET之间的AES互操作的源代码。我扩展了这里给出的基本示例,以3种语言生成测试应用程序:C#,Javascript和VBScript。他们都采取相同的论点。它们每个都使用符合RFC2898的密钥派生函数。您可以指定密码,salt,IV和纯文本,以及要在PBKDF2中使用的RFC2898迭代次数。您可以轻松验证每个测试程序的密文是否相同。也许这个例子对某些人有用。

EDIT3
a good read: Javascript Cryptography considered harmful.

EDIT3读起来很好:Javascript Cryptography被认为是有害的。

#1


37  

maybe I can help.

也许我可以帮忙。

I took your C# code and modified it slightly. The C# code I use, in its entirety, is:

我拿了你的C#代码并稍微修改了一下。我使用的C#代码完整地是:

using System;
using System.Security.Cryptography;

public class Bob
{
    internal static string FormatByteArray(byte[] b)
    {
        System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
        int i = 0;
        for (i = 0; i < b.Length; i++)
        {
            if (i != 0 && i % 16 == 0)
                sb1.Append("\n");
            sb1.Append(System.String.Format("{0:X2} ", b[i]));
        }
        return sb1.ToString();
    }

    public static void Main()
    {
        try
        {
            string original = "watson?";

            Console.WriteLine("Original:   {0}", original);

            byte[] IV = new byte[16];   // match slowaes IV
            var ascii = new System.Text.ASCIIEncoding();

            // match slowaes KEY
            string passPhrase = "12345678901234567890123456789012";
            byte[] key = ascii.GetBytes(passPhrase);

            RijndaelManaged myRijndael = new RijndaelManaged();
            myRijndael.BlockSize = 128;
            myRijndael.KeySize = 256;
            myRijndael.IV = IV;
            myRijndael.Padding   = PaddingMode.PKCS7;
            myRijndael.Mode = CipherMode.CBC;
            myRijndael.Key = key;

            // Encrypt the string to an array of bytes.
            byte[] plainText = new System.Text.ASCIIEncoding().GetBytes(original);
            ICryptoTransform transform = myRijndael.CreateEncryptor();
            byte[] cipherText = transform.TransformFinalBlock(plainText, 0, plainText.Length);

            Console.WriteLine("cipherText: {0}", FormatByteArray(cipherText));

            // Decrypt the bytes to a string.
            transform = myRijndael.CreateDecryptor();
            plainText = transform.TransformFinalBlock(cipherText, 0, cipherText.Length);


            string roundtrip = ascii.GetString(plainText);

            Console.WriteLine("Round Trip: {0}", roundtrip);

        }
        catch (Exception e)
        {
            Console.WriteLine("Error: {0}", e.Message);
        }
    }

}

compile the above with

编译上面的

csc.exe /target:exe /out:Bob.exe Bob.cs

I use the slowAES.wsc from the other post you referenced, with 2 changes: I do not call getPaddedBlock() for the key in the EncryptString or DecryptString() methods. This really needs a PBKDF but let's not worry about that now. Here's what the modified EncryptString looks like:

我使用你引用的其他帖子中的slowAES.wsc,有2个更改:我没有为EncryptString或DecryptString()方法中的键调用getPaddedBlock()。这真的需要一个PBKDF,但我们现在不用担心。以下是修改后的EncryptString的样子:

function EncryptString(plainText)
{
  // this is really wrong - need a PBBKDF to get the key, instead
  // of just using the passphrase
  var key = cryptoHelpers.convertStringToByteArray(_passphrase);
  // var nkey = slowAES.getPaddedBlock(key, 0, _keysize, _mode);
  var bytesToEncrypt = cryptoHelpers.convertStringToByteArray(plainText);

  var result = slowAES.encrypt(bytesToEncrypt, 
      _mode,
      key,
      _keysize,
      _iv);
  return result['cipher'];
}

This means you have to use a passPhrase that is exactly the length required by the keysize. If you use AES256, then pass a 32-char string (32 * 8 = 256 bits). Seems like you figured this out already.

这意味着您必须使用passPhrase,它恰好是keysize所需的长度。如果使用AES256,则传递32个字符串(32 * 8 = 256位)。好像你已经想到了这一点。

The client of the WSC component is also Javascript (though it can be any COM language). Here's what I used.

WSC组件的客户端也是Javascript(尽管它可以是任何COM语言)。这是我用的。

function toHexString(a)
{
    var ret = '';
    for(var i = 0;i < a.length;i++)
        ret += (a[i] < 16 ? '0' : '') + a[i].toString(16) + ' ';
    return ret.toLowerCase();
}


//var plaintext = "Hello. This is a test. of the emergency broadcasting system.";
var plaintext = "watson?";

try
{
    WScript.echo( "plaintext: " + plaintext);
    WScript.echo( "plaintext.length: " + plaintext.length);

    WScript.echo( "instantiate ");
    var aes = new ActiveXObject("Ionic.Com.SlowAES");

    WScript.echo( "keysize ");
    aes.KeySize = 256;

    WScript.echo( "passphrase ");
    aes.PassPhrase= "12345678901234567890123456789012";  // 32 chars long

    WScript.echo( "mode ");
    aes.Mode = "CBC";

    WScript.echo( "encrypting... ");
    var result = aes.EncryptString(plaintext);

    WScript.echo( "Cryptotext: " + toHexString(result));

    WScript.echo( "decrypting... ");
    var decrypted = aes.DecryptBytesToString(result);

    WScript.echo( "decrypted: " + decrypted);
}
catch(e)
{
    WScript.echo("Exception: " + e); 
    //     WScript.echo(e.Number + ": " + e.Name);
    WScript.echo(e.Message);
}

If I then run this code, the Javascript and C# produces the same cipher text for a plaintext of "watson?", using AES256, passphrase of 12345678901234567890123456789012, and an IV of 16 bytes of zero. The ciphertext generated is:

如果我然后运行此代码,Javascript和C#使用AES256,密码为12345678901234567890123456789012,以及16字节零的IV,为“watson?”的明文生成相同的密文。生成的密文是:

8B 68 A6 23 08 2A D8 A0 EB 99 17 8F 69 03 18 FF

It successfully decrypts in both cases.

它在两种情况下都成功解密。

EDIT: Though I packaged the slowAES encryption in a WSC, it will be interoperable running outside the COM environment as well. The WSC part is unnecessary for this question, but was necessary to demonstrate the answer for a prior question, which was, "how can I get VBScript and .NET AES to interoperate?"

编辑:虽然我在一个WSC中打包了slowAES加密,但它也可以在COM环境之外运行。 WSC部分对于这个问题是不必要的,但是有必要证明先前问题的答案,即“如何才能使VBScript和.NET AES进行互操作?”

EDIT2: The source code that demonstrates AES interop between Javascript or VBScript and .NET is available. I extended the basic example given here to produce test apps in 3 languages: C#, Javascript and VBScript. They all take the same set of arguments. They each use a RFC2898-compliant key derivation function. You can specify the password, salt, IV, and plaintext, as well as the number of RFC2898 iterations to use in the PBKDF2. You can easily verify that the ciphertext is the same for each of these test programs. Maybe the example will be useful for someone.

EDIT2:演示了Javascript或VBScript与.NET之间的AES互操作的源代码。我扩展了这里给出的基本示例,以3种语言生成测试应用程序:C#,Javascript和VBScript。他们都采取相同的论点。它们每个都使用符合RFC2898的密钥派生函数。您可以指定密码,salt,IV和纯文本,以及要在PBKDF2中使用的RFC2898迭代次数。您可以轻松验证每个测试程序的密文是否相同。也许这个例子对某些人有用。

EDIT3
a good read: Javascript Cryptography considered harmful.

EDIT3读起来很好:Javascript Cryptography被认为是有害的。