• 微信小程序开放数据解密 AES-128-CBC 解密(C#版本)


    最近在开发小程序,需要跟微信服务端交互,微信敏感数据都有加密返回,需要在服务端接收进行解密后再返回给客户端小程序,今天就通过C# 进行数据的解密,官方下载下来是Node、C++、php等,就是没有C# 代码,于是就上度娘,找到

    https://blog.csdn.net/jasonsong2008/article/details/83586119

    https://www.cnblogs.com/jetz/p/6384809.html

    经过一番研究,搞定啦,其实就是用了aes加密方法。

    下面把第一篇博文进行整理

    如果你使用的Java,请访问这个地址(Java版本) https://blog.csdn.net/jasonsong2008/article/details/83588666
    我们先来看一下微信官方的说明文档,以下直接文档来自微信小程序官方:

    加密数据解密算法
    接口如果涉及敏感数据(如wx.getUserInfo当中的 openId 和 unionId),接口的明文内容将不包含这些敏感数据。开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData) 进行对称解密。 解密算法如下:

    对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
    对称解密的目标密文为 Base64_Decode(encryptedData)。
    对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
    对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
    一定要认真看官方提供的文档

    session_key 是在 wx.login 中获取

    1、先Base64_Decode encryptedData、session_key、iv

    2、调用 TransformFinalBlock方法

        public class AesHelper
        {
            /// <summary>
            /// AES加解密(C#版本)
            /// Add by 张旺
            /// https://www.cnblogs.com/zhangwang/
            ///
            /// AES,高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,
            /// 是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
            /// 严格地说,AES和Rijndael加密法并不完全一样(虽然在实际应用中二者可以互换),
            /// 因为Rijndael加密法可以支持更大范围的区块和密钥长度:AES的区块长度固定为128 比特,
            /// 密钥长度则可以是128,192或256比特;而Rijndael使用的密钥和区块长度可以是32位的整数倍,
            /// 以128位为下限,256比特为上限。包括AES-ECB,AES-CBC,AES-CTR,AES-OFB,AES-CFB
            /// </summary>
    
            #region 微信小程序 开放数据解密
            /// <summary>
            /// 微信小程序 开放数据解密
            /// AES解密(Base64)
            /// Add by 张旺
            /// </summary>
            /// <param name="encryptedData">已加密的数据</param>
            /// <param name="sessionKey">解密密钥</param>
            /// <param name="iv">IV偏移量</param>
            /// <returns></returns>
            public string DecryptForWeChatApplet(string encryptedData, string sessionKey, string iv)
            {
                var decryptBytes = Convert.FromBase64String(encryptedData);
                var keyBytes = Convert.FromBase64String(sessionKey);
                var ivBytes = Convert.FromBase64String(iv);
                var outputBytes = DecryptByAesBytes(decryptBytes, keyBytes, ivBytes);
                return Encoding.UTF8.GetString(outputBytes);
            }
    
            #endregion
    
            #region AES加密
    
            /// <summary>
            /// AES加密
            /// Add by 张旺
            /// </summary>
            /// <param name="encryptedBytes">待加密的字节数组</param>
            /// <param name="keyBytes">加密密钥字节数组</param>
            /// <param name="ivBytes">IV初始化向量字节数组</param>
            /// <param name="cipher">运算模式</param>
            /// <param name="padding">填充模式</param>
            /// <returns></returns>
            public static byte[] EncryptToAesBytes(byte[] encryptedBytes, byte[] keyBytes, byte[] ivBytes,
                CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
            {
                if (encryptedBytes == null || encryptedBytes.Length <= 0)
                    throw new ArgumentNullException(nameof(encryptedBytes));
                if (keyBytes == null || keyBytes.Length <= 0)
                    throw new ArgumentNullException(nameof(keyBytes));
                if (ivBytes == null || ivBytes.Length <= 0)
                    throw new ArgumentNullException(nameof(ivBytes));
    
                var des = new AesCryptoServiceProvider
                {
                    Key = keyBytes,
                    IV = ivBytes,
                    Mode = cipher,
                    Padding = padding
                };
                var outputBytes = des.CreateEncryptor().TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
                return outputBytes;
            }
    
            #endregion
    
            #region AES解密
            /// <summary>
            /// AES解密
            /// Add by 张旺
            /// </summary>
            /// <param name="decryptedBytes">待解密的字节数组</param>
            /// <param name="keyBytes">解密密钥字节数组</param>
            /// <param name="ivBytes">IV初始化向量字节数组</param>
            /// <param name="cipher">运算模式</param>
            /// <param name="padding">填充模式</param>
            /// <returns></returns>
            public static byte[] DecryptByAesBytes(byte[] decryptedBytes, byte[] keyBytes, byte[] ivBytes,
                CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
            {
                if (decryptedBytes == null || decryptedBytes.Length <= 0)
                    throw new ArgumentNullException(nameof(decryptedBytes));
                if (keyBytes == null || keyBytes.Length <= 0)
                    throw new ArgumentNullException(nameof(keyBytes));
                if (ivBytes == null || ivBytes.Length <= 0)
                    throw new ArgumentNullException(nameof(ivBytes));
    
                var aes = new AesCryptoServiceProvider
                {
                    Key = keyBytes,
                    IV = ivBytes,
                    Mode = cipher,
                    Padding = padding
                };
                var outputBytes = aes.CreateDecryptor().TransformFinalBlock(decryptedBytes, 0, decryptedBytes.Length);
                return outputBytes;
            }
            #endregion
        }
    

      3、调用方法如下

     public void DecryptForWeChatAppletTest() {
                //微信小程序返回的加密数据
                const string encryptedData =
                    "tsyLVebikY1aLQ0aNpg10NHxCTV2Ar+FJHUZdwIchBXFbJU7hXyf5gbDibaLU+lT6bzzut/nVymRFp/U8MrF0c8yOCFbnK5aevyearR7vopeel2y929weVA/s16shDPnRMkIn9xiMfVY3LDmuptnBpy1loZfSW2CPfXFuKXQf2z+Kiruynve1cq2mnzAadNaw3/g/tjHRPzxBnTkMsu8sQ==";
                //会话密钥
                const string sessionKey = "vBwBswWRVmD0WQvRbdbMZg==";
                //解密算法初始向量
                const string iv = "8IzE0WUF0j5hXy4oIKuLHA==";
     
                var result = AesHelper.DecryptForWeChatApplet(encryptedData, sessionKey, iv);
                Console.Write(result);
            }
  • 相关阅读:
    在 .NET 3.5 中序列化和反序列化 JSON Kevin
    .NET 3.5 获取不全Cookie的问题 Kevin
    C#反序列化JSON数组对象 Kevin
    使用ConfigurationManager类 读写配置文件 Kevin
    C# post数据时 出现如下错误: System.Net.WebException: 远程服务器返回错误: (417) Expectation Failed 的解决办法 Kevin
    .NET 关于反序列化 JSON 对象数组的问题 Kevin
    为什么调用thread.Abort(),线程不会马上停止 Kevin
    vs2005调用迅雷完美解决方案 Kevin
    .NET 复杂的 DataBinding 接受 IList 或 IListSource 作为数据源 Kevin
    找不到DataContract属性! Kevin
  • 原文地址:https://www.cnblogs.com/zhangwang/p/11723269.html
Copyright © 2020-2023  润新知