C# 如何解密已知的Aes密文

s666666__ 2020-08-06 03:27:23

在网上搜了很多的文章都没找到通用的算法,基本都只能自己加密 自己解密,不通用~

比如我在第三方工具如 https://www.sojson.com/encrypt_aes.html 或者 http://www.jsons.cn/aesencrypt/ 生成了一段Aes密文,如以下:

明文:“中华人民共和国”
密钥:“123456789” (非固定8位数的密钥)
密文:“U2FsdGVkX181wiGhM+Xqv4hES9ZoZ1ScvxLUDGYRor5qjrfU6ua1Z41N+1V+B11k”

求C#通用的Aes加密解密方法~~ T _ T
...全文
21452 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
s666666__ 2020-08-12
  • 打赏
  • 举报
回复
我在这做个总结吧,也希望能够帮到其他的同学! 一、AES只有以下这部分是核心,其他拼接的部分是靠大家发挥想象力设计出来的方便做交互,所以拼接的部分的确是没有统一的标准,大家只需按适合自己软件的标准来进行拼接即可。
using (var aes = new AesCryptoServiceProvider() {Key = key, IV = iv})
    using (var encryptor = aes.CreateEncryptor())
        byte[] encryptedBytes = encryptor.TransformFinalBlock(bytes, 0, bytes.Length).ToArray();
二、以下是按照5楼大神提供的思路实现的AES加密与解密方法,可供大家参考使用:
class Program
{
    static void Main()
    {
        var ciphertext = CryptoJsEncrypt("hello");          // 输出类似:U2FsdGVkX19lNcn2s3GN35rz565eW5E50paqPatgiV8=
        var plaintext = CryptoJsDecrypt(ciphertext);        // 输出:hello

        var ciphertext2 = CryptoJsEncrypt_New("hello");     // 输出类似:aes256:uIrriP5/zbH3fYHDVOga7XfI0Bt3xD1j64+Zmw68XQ4=
        var plaintext2 = CryptoJsDecrypt_New(ciphertext2);  // 输出:hello
    }

    /// <summary>
    /// 加密 (常用思路)
    /// </summary>
    /// <param name="content">明文</param>
    /// <param name="passphase">密码</param>
    /// <returns>密文(base64)</returns>
    static string CryptoJsEncrypt(string content, string passphase = "123456789")
    {
        // openssl aes-256-cbc -k 123456789 -md md5 -e -base64
        //
        byte[] bytes = Encoding.UTF8.GetBytes(content);
        byte[] key, iv, salt = new byte[8];

        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(salt); // 产生8个字节的随机salt
        }

        using (var md5 = new MD5CryptoServiceProvider())
        {
            var preHash = Encoding.UTF8.GetBytes(passphase).Concat(salt).ToArray();
            var bs1 = md5.ComputeHash(preHash);
            var bs2 = md5.ComputeHash(bs1.Concat(preHash).ToArray());
            var bs3 = md5.ComputeHash(bs2.Concat(preHash).ToArray());

            key = bs1.Concat(bs2).ToArray();
            iv = bs3;
        }

        using (var aes = new AesCryptoServiceProvider() { Key = key, IV = iv })
        using (var encryptor = aes.CreateEncryptor())
        {
            var encryptedBytes = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
            var concated = Encoding.UTF8.GetBytes("Salted__").Concat(salt).Concat(encryptedBytes);
            return Convert.ToBase64String(concated.ToArray());
        }
    }
    /// <summary>
    /// 解密 (常用思路)
    /// </summary>
    /// <param name="content">密文(base64)</param>
    /// <param name="passphase">密码</param>
    /// <returns>明文</returns>
    static string CryptoJsDecrypt(string content, string passphase = "123456789")
    {
        // openssl aes-256-cbc -k 123456789 -md md5 -e -base64
        //
        byte[] bytes = Convert.FromBase64String(content);
        byte[] key, iv, salt = new byte[8], encryptedBytes = new byte[bytes.Length - 8 - 8];

        //提取 salt
        Array.ConstrainedCopy
        (
            sourceArray: bytes,
            sourceIndex: 8, //剔除开头的 "Salted__"
            destinationArray: salt,
            destinationIndex: 0,
            length: salt.Length
        );
        //提取 encryptedBytes
        Array.ConstrainedCopy
        (
            sourceArray: bytes,
            sourceIndex: 8 + 8, //并剔除开头的 salt
            destinationArray: encryptedBytes,
            destinationIndex: 0,
            length: encryptedBytes.Length
        );

        using (var md5 = new MD5CryptoServiceProvider())
        {
            var preHash = Encoding.UTF8.GetBytes(passphase).Concat(salt).ToArray();
            var bs1 = md5.ComputeHash(preHash);
            var bs2 = md5.ComputeHash(bs1.Concat(preHash).ToArray());
            var bs3 = md5.ComputeHash(bs2.Concat(preHash).ToArray());

            key = bs1.Concat(bs2).ToArray();
            iv = bs3;
        }

        using (var aes = new AesCryptoServiceProvider() { Key = key, IV = iv })
        using (var decryptor = aes.CreateDecryptor())
        {
            byte[] decryptedBytes = decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
            return Encoding.UTF8.GetString(decryptedBytes);
        }
    }

    /// <summary>
    /// 加密 (新的思路)
    /// </summary>
    /// <param name="content">明文</param>
    /// <param name="passphase">密码(固定32位)</param>
    /// <returns>密文(base64)</returns>
    static string CryptoJsEncrypt_New(string content, string passphase = "12345678123456781234567812345678")
    {
        byte[] bytes = Encoding.UTF8.GetBytes(content);
        byte[] key = Encoding.UTF8.GetBytes(passphase); //32位的key
        byte[] iv = new byte[16];

        using (var rng = new RNGCryptoServiceProvider())
            rng.GetBytes(iv); // 产生16个字节的随机iv

        using (var aes = new AesCryptoServiceProvider() { Key = key, IV = iv })
        using (var encryptor = aes.CreateEncryptor())
        {
            var encryptedBytes = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
            var concated = iv.Concat(encryptedBytes);
            return "aes256:" + Convert.ToBase64String(concated.ToArray());
        }
    }
    /// <summary>
    /// 解密 (新的思路)
    /// </summary>
    /// <param name="content">密文(base64)</param>
    /// <param name="passphase">密码(固定32位)</param>
    /// <returns>明文</returns>
    static string CryptoJsDecrypt_New(string content, string passphase = "12345678123456781234567812345678")
    {
        byte[] bytes = Convert.FromBase64String(content.Substring(7)); //剔除开头的 "aes256:"
        byte[] key = Encoding.UTF8.GetBytes(passphase); //32位的key
        byte[] iv = new byte[16];
        byte[] encryptedBytes = new byte[bytes.Length - 16];

        //提取 iv
        Array.ConstrainedCopy
        (
            sourceArray: bytes,
            sourceIndex: 0,
            destinationArray: iv,
            destinationIndex: 0,
            length: iv.Length
        );
        //提取 加密结果
        Array.ConstrainedCopy
        (
            sourceArray: bytes,
            sourceIndex: 16, //并剔除开头的 iv
            destinationArray: encryptedBytes,
            destinationIndex: 0,
            length: encryptedBytes.Length
        );

        using (var aes = new AesCryptoServiceProvider() { Key = key, IV = iv })
        using (var decryptor = aes.CreateDecryptor())
        {
            byte[] decryptedBytes = decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
            return Encoding.UTF8.GetString(decryptedBytes);
        }
    }
}
s666666__ 2020-08-12
  • 打赏
  • 举报
回复
引用 5 楼 github_36000833 的回复:
你引述的两个网站,加密结果类似: U2FsdGVkX19FjYhEqHBXR1dhsb0GIIPSE1dOLrg7Icc= U2FsdGVkX18pXaGJHFvhOvi0UTP81903Ufqn32Lbvkw= 他们都用了javascript写的CryptoJS,版本3.0.2来做加解密,因此他们能够互相加解密。 但是,不是说他们的做法就是“标准的”Aes。其实CryptoJS 3.0.2已经很老了,他们用的加解密参数也已经被认为不安全了。 CryptoJS的文档说他们要和openssl(另一种流行的工具)的输入兼容,我测试的结果是CryptorJS 4.0和openssl 1.1.1可以默认兼容。 但是openssl 1.1.1要用上一些过时的参数,才能解开CryptoJS 3.0.2的加密。 类似U2FsdGVkX19FjYhEqHBXR1dhsb0GIIPSE1dOLrg7Icc=的输出,是base64编码,把base64编码换成字符串,可以看到他们的开头都是:Salted__...。这个格式是openssl的惯用输出格式,就是一个Salted__的标识,然后紧跟8个字节的salt(密码学上的扰动),然后跟这aes256的加密结果。 其中,aes256使用的密码,固定为32个字节,由那个密码短文123456789,和一个随机的salt来计算推出,计算方法出自RFC2898。 CryptoJS 4.0和openssl 1.1.1默认使用sha1或sha256来推出密码,并有10000次自我迭代;而CryptorJS 3.0.2用了比较弱的MD5,并且只迭代一次。 要和CryptorJS 3.0.2兼容,openssl需要用如下参数加密"hello": echo "hello" | openssl aes-256-cbc -k 123456789 -md md5 -e -base64 用C#写,也没有办法直接用标准的Rfc2898DeriveBytes类(它已经把MD5排除掉了),只能自己按照标准写。
class Program
{
    static void Main(string[] args)
    {
        var o = CryptoJsEncrypt("hello"); // 输出类似:U2FsdGVkX19lNcn2s3GN35rz565eW5E50paqPatgiV8=
    }

    static string CryptoJsEncrypt(string content, string passphase = "123456789")
    {
        // openssl aes-256-cbc -k 123456789 -md md5 -e -base64
        //
        byte[] bytes = Encoding.UTF8.GetBytes(content);
        byte[] key, iv, salt = new byte[8];

        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(salt); // 产生8个字节的随机salt
        }

        using (var md5 = new MD5CryptoServiceProvider())
        {
            var preHash = Encoding.UTF8.GetBytes(passphase).Concat(salt).ToArray();
            var bs1 = md5.ComputeHash(preHash);
            var bs2 = md5.ComputeHash(bs1.Concat(preHash).ToArray());
            var bs3 = md5.ComputeHash(bs2.Concat(preHash).ToArray());

            key = bs1.Concat(bs2).ToArray();
            iv = bs3;
        }

        using (var aes = new AesCryptoServiceProvider() {Key = key, IV = iv})
        using (var encryptor = aes.CreateEncryptor())
        {
            var encryptedBytes = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
            var concated = Encoding.UTF8.GetBytes("Salted__").Concat(salt).Concat(encryptedBytes);
            return Convert.ToBase64String(concated.ToArray());
        }
    }
}
建议你不要沿用那两个网站的加密方法。可以模仿他们的输出格式。 或者,(建议)你跳过密码短文到实际密码的计算,而是直接用32字节的密码。用随机的IV来扰动每一次传输。比如结果为(我经常用的格式): 输出 = aes256:<base64(字节数组)> 字节数组 = (16个字节的IV)(加密结果) 加密结果 = Aes(内容,32字节密码,16字节IV) 对方解密时, 1、把aes256:的scheme标头去除 2、把剩下的base64反编码,得到字节数组 3、字节数组的前面16个字节为每次传输的IV,后面的密文 4、用32个字节的密码,和16个字节的IV,来解密密文。
感谢大神提供的思路!看到这我才发现原来AES的密文是这么拼出来的
github_36000833 2020-08-12
  • 打赏
  • 举报
回复
引用 7 楼 s666666__ 的回复:
...
多谢反馈。 说到,//剔除开头的 "aes256:" 它的一个作用是作为方案号,保留以后更改格式的可能,比如降级为aes128:,或改变为twofish192:等等。 另外,我可能会写成: static string Decrypt_New(string content, byte[] password)... 参数名字不叫passphase(密码短语),而是叫password(实际使用的密码)。
s666666__ 2020-08-12
  • 打赏
  • 举报
回复
引用 8 楼 github_36000833 的回复:
...
GET了,感谢!
github_36000833 2020-08-11
  • 打赏
  • 举报
回复
你引述的两个网站,加密结果类似: U2FsdGVkX19FjYhEqHBXR1dhsb0GIIPSE1dOLrg7Icc= U2FsdGVkX18pXaGJHFvhOvi0UTP81903Ufqn32Lbvkw= 他们都用了javascript写的CryptoJS,版本3.0.2来做加解密,因此他们能够互相加解密。 但是,不是说他们的做法就是“标准的”Aes。其实CryptoJS 3.0.2已经很老了,他们用的加解密参数也已经被认为不安全了。 CryptoJS的文档说他们要和openssl(另一种流行的工具)的输入兼容,我测试的结果是CryptorJS 4.0和openssl 1.1.1可以默认兼容。 但是openssl 1.1.1要用上一些过时的参数,才能解开CryptoJS 3.0.2的加密。 类似U2FsdGVkX19FjYhEqHBXR1dhsb0GIIPSE1dOLrg7Icc=的输出,是base64编码,把base64编码换成字符串,可以看到他们的开头都是:Salted__...。这个格式是openssl的惯用输出格式,就是一个Salted__的标识,然后紧跟8个字节的salt(密码学上的扰动),然后跟这aes256的加密结果。 其中,aes256使用的密码,固定为32个字节,由那个密码短文123456789,和一个随机的salt来计算推出,计算方法出自RFC2898。 CryptoJS 4.0和openssl 1.1.1默认使用sha1或sha256来推出密码,并有10000次自我迭代;而CryptorJS 3.0.2用了比较弱的MD5,并且只迭代一次。 要和CryptorJS 3.0.2兼容,openssl需要用如下参数加密"hello": echo "hello" | openssl aes-256-cbc -k 123456789 -md md5 -e -base64 用C#写,也没有办法直接用标准的Rfc2898DeriveBytes类(它已经把MD5排除掉了),只能自己按照标准写。
class Program
{
    static void Main(string[] args)
    {
        var o = CryptoJsEncrypt("hello"); // 输出类似:U2FsdGVkX19lNcn2s3GN35rz565eW5E50paqPatgiV8=
    }

    static string CryptoJsEncrypt(string content, string passphase = "123456789")
    {
        // openssl aes-256-cbc -k 123456789 -md md5 -e -base64
        //
        byte[] bytes = Encoding.UTF8.GetBytes(content);
        byte[] key, iv, salt = new byte[8];

        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(salt); // 产生8个字节的随机salt
        }

        using (var md5 = new MD5CryptoServiceProvider())
        {
            var preHash = Encoding.UTF8.GetBytes(passphase).Concat(salt).ToArray();
            var bs1 = md5.ComputeHash(preHash);
            var bs2 = md5.ComputeHash(bs1.Concat(preHash).ToArray());
            var bs3 = md5.ComputeHash(bs2.Concat(preHash).ToArray());

            key = bs1.Concat(bs2).ToArray();
            iv = bs3;
        }

        using (var aes = new AesCryptoServiceProvider() {Key = key, IV = iv})
        using (var encryptor = aes.CreateEncryptor())
        {
            var encryptedBytes = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
            var concated = Encoding.UTF8.GetBytes("Salted__").Concat(salt).Concat(encryptedBytes);
            return Convert.ToBase64String(concated.ToArray());
        }
    }
}
建议你不要沿用那两个网站的加密方法。可以模仿他们的输出格式。 或者,(建议)你跳过密码短文到实际密码的计算,而是直接用32字节的密码。用随机的IV来扰动每一次传输。比如结果为(我经常用的格式): 输出 = aes256:<base64(字节数组)> 字节数组 = (16个字节的IV)(加密结果) 加密结果 = Aes(内容,32字节密码,16字节IV) 对方解密时, 1、把aes256:的scheme标头去除 2、把剩下的base64反编码,得到字节数组 3、字节数组的前面16个字节为每次传输的IV,后面的密文 4、用32个字节的密码,和16个字节的IV,来解密密文。
s666666__ 2020-08-10
  • 打赏
  • 举报
回复
引用 3 楼 datafansbj 的回复:
加解密工作应该是应用底层的东西,与业务逻辑没什么关系。况且 AES 加解密是工业标准,与具体的开发语言无关(只是不同的语言编写的语法不同),只要算法正确,不存在异构语言之间的对接问题。即使对接失败(是在找不到原因的情况下),也可以使用对方的开发语言写一个接口来调用。
比如想要实现一个区块链的功能,数据通过AES加密后、同步不同的区块,然后每个区块的密文数据可以由任何语言去读取; 所以在前期设计加密算法时就需要先考虑好后期通用的问题,想找到AES加密算法的属性标准、然后按标准来设计; 否则如果在前期用错了加密算法、那之后留下一个大坑就完了~
datafansbj 2020-08-10
  • 打赏
  • 举报
回复
加解密工作应该是应用底层的东西,与业务逻辑没什么关系。况且 AES 加解密是工业标准,与具体的开发语言无关(只是不同的语言编写的语法不同),只要算法正确,不存在异构语言之间的对接问题。即使对接失败(是在找不到原因的情况下),也可以使用对方的开发语言写一个接口来调用。
ying1234 2020-08-07
  • 打赏
  • 举报
回复
通用?指的是和任何语言加解密都可以?这个通用不了,你没和其它语言联调过,怎么知道能不能加解密成功?至少我用c#的AES只和php,go联调过可以,java的其它同事联调过可以,和c++怎么都不行,放弃了。
s666666__ 2020-08-07
  • 打赏
  • 举报
回复
引用 1 楼 ying1234 的回复:
通用?指的是和任何语言加解密都可以?这个通用不了,你没和其它语言联调过,怎么知道能不能加解密成功?至少我用c#的AES只和php,go联调过可以,java的其它同事联调过可以,和c++怎么都不行,放弃了。
或者有没有办法实现解密 sojson.com 与 jsons.cn 在线工具生成的AES密文呢? 就想知道平时大家用的AES加密标准,以防前期软件设计完成后、结果后期跟其他软件对接时发现加密算法对不上那就老尴尬了
在证书的体系架构当中,包括了四个基本的组件,分别是密钥与加密算法,证书和证书颁发机构,证书申请、续订和吊销的通道,以及使用证书的应用程序 密钥与加密算法,就能够将明文信息,转化成为密文信息,以确保在网络当中传输时信息的安全,它主要分为两种类型,一种是对称加密算法,加密和解密时使用的都是同样的密钥和算法,一般用于加密数据,另外的一种是非对称加密算法,它包括了一对加密密钥,分别是公钥和私钥,如果公钥用于加密明文成为密文,那么私钥将用于解密密文到明文,公钥可以在网络当中进行传输,私钥不在网络当中传输 一般来说,如果两台计算机之间要传输数据,首先将由发送方计算机通过对称加密完成数据的加密过程,然后再由接收方计算机通过网络传输公钥给发送方,发送方再使用公钥加密对称加密的密钥,并且与加密的数据一起发送给接收方计算机,接收方计算机再使用私钥解密出对称加密的密钥,再使用密钥解密加密的数据,完成数据安全传输的过程 而反过来,如果接收方使用私钥加密一个文件,再通过网络将加密文件、公钥和明文一起发送给发送方,发送方使用公钥解密了文件,与一起接收的明文对比,如果相同,就能证明文件未进行修改,实现签名的功能 而证书,就是加密密钥和算法的载体,一张证书当中,包括了加密密钥,算法,签名等信息,并且对于证书申请、颁发、续订和吊销的整个生命周期,还包括了很多其它的一些属性,应用程序可以通过读取证书的这些信息,完成数据的加密解密,签名和身份验证等不同的应用场景 而证书颁发机构,将负责的证书的整个生命周期管理,证书颁发机构是一个层次化的体系结构,根证书颁发机构将只负责子证书颁发机构的证书颁发和吊销管理,子证书颁发机构将负责应用程序证书的申请请求和管理,根证书颁发机构可以放置在一个隔离的环境当中,进一步增强整个证书链的安全,而子证书颁发机构可以按照需求进行扩展,满足不同规模的应用要求 在我们的课程当中,将按照此最佳实践,完成证书服务当中,不同组件的安装和配置

110,550

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧