微信开发之如何实现消息体签名和加解密
发表于:2024-12-13 作者:千家信息网编辑
千家信息网最后更新 2024年12月13日,这篇文章主要介绍了微信开发之如何实现消息体签名和加解密,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。代码:using System;u
千家信息网最后更新 2024年12月13日微信开发之如何实现消息体签名和加解密
这篇文章主要介绍了微信开发之如何实现消息体签名和加解密,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
代码:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Security.Cryptography;using System.IO;using System.Net;namespace WxApi{ public class Cryptography { public static UInt32 HostToNetworkOrder(UInt32 inval) { UInt32 outval = 0; for (int i = 0; i < 4; i++) outval = (outval << 8) + ((inval >> (i * 8)) & 255); return outval; } public static Int32 HostToNetworkOrder(Int32 inval) { Int32 outval = 0; for (int i = 0; i < 4; i++) outval = (outval << 8) + ((inval >> (i * 8)) & 255); return outval; } ////// 解密方法 /// /// 密文 /// ////// public static string AES_decrypt(String Input, string EncodingAESKey, ref string appid) { byte[] Key; Key = Convert.FromBase64String(EncodingAESKey + "="); byte[] Iv = new byte[16]; Array.Copy(Key, Iv, 16); byte[] btmpMsg = AES_decrypt(Input, Iv, Key); int len = BitConverter.ToInt32(btmpMsg, 16); len = IPAddress.NetworkToHostOrder(len); byte[] bMsg = new byte[len]; byte[] bAppid = new byte[btmpMsg.Length - 20 - len]; Array.Copy(btmpMsg, 20, bMsg, 0, len); Array.Copy(btmpMsg, 20 + len, bAppid, 0, btmpMsg.Length - 20 - len); string oriMsg = Encoding.UTF8.GetString(bMsg); appid = Encoding.UTF8.GetString(bAppid); return oriMsg; } public static String AES_encrypt(String Input, string EncodingAESKey, string appid) { byte[] Key; Key = Convert.FromBase64String(EncodingAESKey + "="); byte[] Iv = new byte[16]; Array.Copy(Key, Iv, 16); string Randcode = CreateRandCode(16); byte[] bRand = Encoding.UTF8.GetBytes(Randcode); byte[] bAppid = Encoding.UTF8.GetBytes(appid); byte[] btmpMsg = Encoding.UTF8.GetBytes(Input); byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length)); byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bAppid.Length + btmpMsg.Length]; Array.Copy(bRand, bMsg, bRand.Length); Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length); Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length); Array.Copy(bAppid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bAppid.Length); return AES_encrypt(bMsg, Iv, Key); } private static string CreateRandCode(int codeLen) { string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z"; if (codeLen == 0) { codeLen = 16; } string[] arr = codeSerial.Split(','); string code = ""; int randValue = -1; Random rand = new Random(unchecked((int)DateTime.Now.Ticks)); for (int i = 0; i < codeLen; i++) { randValue = rand.Next(0, arr.Length - 1); code += arr[randValue]; } return code; } private static String AES_encrypt(String Input, byte[] Iv, byte[] Key) { var aes = new RijndaelManaged(); //秘钥的大小,以位为单位 aes.KeySize = 256; //支持的块大小 aes.BlockSize = 128; //填充模式 aes.Padding = PaddingMode.PKCS7; aes.Mode = CipherMode.CBC; aes.Key = Key; aes.IV = Iv; var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); byte[] xBuff = null; using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) { byte[] xXml = Encoding.UTF8.GetBytes(Input); cs.Write(xXml, 0, xXml.Length); } xBuff = ms.ToArray(); } String Output = Convert.ToBase64String(xBuff); return Output; } private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key) { var aes = new RijndaelManaged(); //秘钥的大小,以位为单位 aes.KeySize = 256; //支持的块大小 aes.BlockSize = 128; //填充模式 //aes.Padding = PaddingMode.PKCS7; aes.Padding = PaddingMode.None; aes.Mode = CipherMode.CBC; aes.Key = Key; aes.IV = Iv; var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); byte[] xBuff = null; #region 自己进行PKCS7补位,用系统自己带的不行 byte[] msg = new byte[Input.Length + 32 - Input.Length % 32]; Array.Copy(Input, msg, Input.Length); byte[] pad = KCS7Encoder(Input.Length); Array.Copy(pad, 0, msg, Input.Length, pad.Length); #endregion #region 注释的也是一种方法,效果一样 //ICryptoTransform transform = aes.CreateEncryptor(); //byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length); #endregion using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) { cs.Write(msg, 0, msg.Length); } xBuff = ms.ToArray(); } String Output = Convert.ToBase64String(xBuff); return Output; } private static byte[] KCS7Encoder(int text_length) { int block_size = 32; // 计算需要填充的位数 int amount_to_pad = block_size - (text_length % block_size); if (amount_to_pad == 0) { amount_to_pad = block_size; } // 获得补位所用的字符 char pad_chr = chr(amount_to_pad); string tmp = ""; for (int index = 0; index < amount_to_pad; index++) { tmp += pad_chr; } return Encoding.UTF8.GetBytes(tmp); } /** * 将数字转化成ASCII码对应的字符,用于对明文进行补码 * * @param a 需要转化的数字 * @return 转化得到的字符 */ static char chr(int a) { byte target = (byte)(a & 0xFF); return (char)target; } private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key) { RijndaelManaged aes = new RijndaelManaged(); aes.KeySize = 256; aes.BlockSize = 128; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None; aes.Key = Key; aes.IV = Iv; var decrypt = aes.CreateDecryptor(aes.Key, aes.IV); byte[] xBuff = null; using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)) { byte[] xXml = Convert.FromBase64String(Input); byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32]; Array.Copy(xXml, msg, xXml.Length); cs.Write(xXml, 0, xXml.Length); } xBuff = decode2(ms.ToArray()); } return xBuff; } private static byte[] decode2(byte[] decrypted) { int pad = (int)decrypted[decrypted.Length - 1]; if (pad < 1 || pad > 32) { pad = 0; } byte[] res = new byte[decrypted.Length - pad]; Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad); return res; } }}
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Xml;using System.Collections;//using System.Web;using System.Security.Cryptography;//-40001 : 签名验证错误//-40002 : xml解析失败//-40003 : sha加密生成签名失败//-40004 : AESKey 非法//-40005 : appid 校验错误//-40006 : AES 加密失败//-40007 : AES 解密失败//-40008 : 解密后得到的buffer非法//-40009 : base64加密异常//-40010 : base64解密异常namespace WxApi{ public class MsgCrypt { string m_sToken; string m_sEncodingAESKey; string m_sAppID; enum WXBizMsgCryptErrorCode { WXBizMsgCrypt_OK = 0, WXBizMsgCrypt_ValidateSignature_Error = -40001, WXBizMsgCrypt_ParseXml_Error = -40002, WXBizMsgCrypt_ComputeSignature_Error = -40003, WXBizMsgCrypt_IllegalAesKey = -40004, WXBizMsgCrypt_ValidateAppid_Error = -40005, WXBizMsgCrypt_EncryptAES_Error = -40006, WXBizMsgCrypt_DecryptAES_Error = -40007, WXBizMsgCrypt_IllegalBuffer = -40008, WXBizMsgCrypt_EncodeBase64_Error = -40009, WXBizMsgCrypt_DecodeBase64_Error = -40010 }; //构造函数 // @param sToken: 公众平台上,开发者设置的Token // @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey // @param sAppID: 公众帐号的appid public MsgCrypt(string sToken, string sEncodingAESKey, string sAppID) { m_sToken = sToken; m_sAppID = sAppID; m_sEncodingAESKey = sEncodingAESKey; } // 检验消息的真实性,并且获取解密后的明文 // @param sMsgSignature: 签名串,对应URL参数的msg_signature // @param sTimeStamp: 时间戳,对应URL参数的timestamp // @param sNonce: 随机串,对应URL参数的nonce // @param sPostData: 密文,对应POST请求的数据 // @param sMsg: 解密后的原文,当return返回0时有效 // @return: 成功0,失败返回对应的错误码 public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg) { if (m_sEncodingAESKey.Length != 43) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; } XmlDocument doc = new XmlDocument(); XmlNode root; string sEncryptMsg; try { doc.LoadXml(sPostData); root = doc.FirstChild; sEncryptMsg = root["Encrypt"].InnerText; } catch (Exception) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error; } //verify signature int ret = 0; ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature); if (ret != 0) return ret; //decrypt string cpid = ""; try { sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid); } catch (FormatException) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error; } catch (Exception) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error; } if (cpid != m_sAppID) return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error; return 0; } //将企业号回复用户的消息加密打包 // @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串 // @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce // @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串, // 当return返回0时有效 // return:成功0,失败返回对应的错误码 public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg) { if (m_sEncodingAESKey.Length != 43) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; } string raw = ""; try { raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID); } catch (Exception) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error; } string MsgSigature = ""; int ret = 0; ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature); if (0 != ret) return ret; sEncryptMsg = ""; string EncryptLabelHead = ""; string MsgSigLabelHead = " "; string TimeStampLabelHead = " "; string NonceLabelHead = " "; sEncryptMsg = sEncryptMsg + " " + EncryptLabelHead + raw + EncryptLabelTail; sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail; sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail; sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail; sEncryptMsg += " "; return 0; } public class DictionarySort : System.Collections.IComparer { public int Compare(object oLeft, object oRight) { string sLeft = oLeft as string; string sRight = oRight as string; int iLeftLength = sLeft.Length; int iRightLength = sRight.Length; int index = 0; while (index < iLeftLength && index < iRightLength) { if (sLeft[index] < sRight[index]) return -1; else if (sLeft[index] > sRight[index]) return 1; else index++; } return iLeftLength - iRightLength; } } //Verify Signature private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture) { string hash = ""; int ret = 0; ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash); if (ret != 0) return ret; //System.Console.WriteLine(hash); if (hash == sSigture) return 0; else { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error; } } public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, ref string sMsgSignature) { ArrayList AL = new ArrayList(); AL.Add(sToken); AL.Add(sTimeStamp); AL.Add(sNonce); AL.Add(sMsgEncrypt); AL.Sort(new DictionarySort()); string raw = ""; for (int i = 0; i < AL.Count; ++i) { raw += AL[i]; } SHA1 sha; ASCIIEncoding enc; string hash = ""; try { sha = new SHA1CryptoServiceProvider(); enc = new ASCIIEncoding(); byte[] dataToHash = enc.GetBytes(raw); byte[] dataHashed = sha.ComputeHash(dataToHash); hash = BitConverter.ToString(dataHashed).Replace("-", ""); hash = hash.ToLower(); } catch (Exception) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error; } sMsgSignature = hash; return 0; } }}
在处理程序中,首先获取到公众平台服务器发送过来的数据,并转成字符串。代码如下
string postStr = ""; Stream s = VqiRequest.GetInputStream();//此方法是对System.Web.HttpContext.Current.Request.InputStream的封装,可直接代码 byte[] b = new byte[s.Length]; s.Read(b, 0, (int)s.Length); postStr = Encoding.UTF8.GetString(b);
然后再分别获取url中的参数:timestamp,nonce,msg_signature,encrypt_type。可以看到,在明文模式下是没有encrypt_type参数的。如图:
明文模式
兼容模式和安全模式
兼容模式和安全模式则加入了消息体的签名与加密类型两个参数。
由于在实际的运营中,兼容模式不太可能使用,所以在此不做详细介绍了。
接着上面的讲。获取到url中的参数后,判断encrypt_type的值是否为aes,如果是则说明是使用的兼容模式或安全模式,此时则需调用解密相关的方法进行解密。
if (encrypt_type == "aes") { requestXML.IsAes = true; requestXML.EncodingAESKey = aeskey; requestXML.token = token; requestXML.appid = appid; var ret = new MsgCrypt(token, aeskey, appid); int r = ret.DecryptMsg(msg_signature, timestamp, nonce, postStr, ref data); if (r!=0) { WxApi.Base.WriteBug("消息解密失败"); return null; } }
否则则直接解析接收到的xml字符串。
下图是接收到的密文:
解密之后的内容如下:
此时就可以解析此xml了。
当需要回复加密的请求时,回复的内容也是需要加密的,所以在回复前需要判断此条接收到的消息是否加密,如果是加密的,则需要将需要回复的内容加密后,再进行回复。回复消息的方法将在下一篇中具体讲解,此篇只讲解加密的过程。
处理代码如下:
private static void Response(WeiXinRequest requestXML, string data) { if (requestXML.IsAes) { var wxcpt = new MsgCrypt(requestXML.token, requestXML.EncodingAESKey, requestXML.appid); wxcpt.EncryptMsg(data, Utils.ConvertDateTimeInt(DateTime.Now).ToString(), Utils.GetRamCode(), ref data); } Utils.ResponseWrite(data); }
将接收到的消息实体和需要回复的内容xml传递过来,如果是加密的,则加密后再Response,否则直接Response。
感谢你能够认真阅读完这篇文章,希望小编分享的"微信开发之如何实现消息体签名和加解密"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!
加密
模式
消息
参数
字符
开发
代码
公众
内容
大小
字符串
密文
方法
明文
篇文章
错误
安全
平台
用户
支持
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
手抄报网络安全合理使用手机
数据库文件怎么查看
重庆优质软件开发报价
大学生应注意那些网络安全
安卓第三方软件开发
大众id服务器在中国嘛
网络技术和网站区别
网络安全在心中作文600字
百瑞傲达软件如何导入数据库
怎么跟和平精英客服索要服务器
计算机网络技术发展探究
软件开发lbv
延边州网络安全管理局
网络技术捏造属于什么
佛山千牛网络技术
网络安全人防技防物防
麻将扑克软件开发
富文本框里的图片怎么保存数据库
怎么导出数据库的数据结构
北京互联网网络技术开发哪家好
adb数据库弹性和预留区别
洞口哪里有游戏软件开发公司
软件开发工程师职业内容
服务器怎么设置不登录
网络技术经济化
大理大学生网络安全
美国汽车网络技术
辰盾网络安全应急支撑
服务器运维安全手册
服务器如何开创造