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

Posted

技术标签:

【中文标题】让 .NET 中的 SlowAES 和 RijndaelManaged 类一起玩【英文标题】:Getting SlowAES and RijndaelManaged class in .NET to play together 【发布时间】:2010-11-12 02:03:44 【问题描述】:

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

我在阅读this post 后选择了这种方法,其中 Cheeso 设法让这两种加密方法一起发挥作用

“在我的测试中 COM-wrapped-SlowAEs,我用的是 CBC 模式, 并且加密完全 与 RijndaelManaged 兼容 .NET 中的类" - Cheeso

我从 Cheeso 的 Windows 脚本组件、最新的 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());

我得到以下输出:

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

我已修改以下示例找到 on 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);
            
        

字节数组的观察:

-  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

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

谢谢,

鲍勃

【问题讨论】:

【参考方案1】:

也许我可以帮忙。

我使用了您的 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);
        
    


编译上面的
csc.exe /target:exe /out:Bob.exe Bob.cs

我使用您引用的另一篇文章中的 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'];

这意味着您必须使用 正好 密钥大小所需长度的密码短语。如果您使用 AES256,则传递一个 32 字符的字符串(32 * 8 = 256 位)。好像你已经想通了。

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);

如果我随后运行此代码,则 Javascript 和 C# 使用 AES256、密码 12345678901234567890123456789012 和 16 字节零的 IV 为纯文本“watson?”生成相同的密文。生成的密文为:

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

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

编辑:虽然我在 WSC 中打包了慢速 AES 加密,但它在 COM 环境之外也可以互操作。此问题不需要 WSC 部分,但有必要证明前一个问题的答案,即“how can I get VBScript and .NET AES to interoperate?”

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

EDIT3 好读:Javascript Cryptography considered harmful.

【讨论】:

我真的很尊重你为帮助别人付出的努力。 +1。 @Cheeso:我真的很佩服你为解决这个问题所做的工作。做得很好。 @DanielLittle - 抱歉耽搁了。该链接现已生效。

以上是关于让 .NET 中的 SlowAES 和 RijndaelManaged 类一起玩的主要内容,如果未能解决你的问题,请参考以下文章

如何让 ASP.NET 访问证书存储中证书中的私钥?

让 .NET 了解返回的 REF_CURSOR 中的 Oracle 对象

有没有办法让 Asp.net 零公共站点(Asp.net MVC)中的实时登录用户?

是什么让.NET7的Min和Max方法性能暴增了45倍?

如何让登录用户退出.Net WebAPI中的控制器操作方法

是啥让某些东西成为 ASP.NET Core 中的请求功能?