#region Apache License Version 2.0
/*----------------------------------------------------------------
Copyright 2019 Jeffrey Su & Suzhou Senparc Network Technology Co.,Ltd.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
Detail: https://github.com/JeffreySu/WeiXinMPSDK/blob/master/license.md
----------------------------------------------------------------*/
#endregion Apache License Version 2.0
/*----------------------------------------------------------------
文件名:WXBizMsgCrypt.cs
文件功能描述:加解密算法
创建标识:Senparc - 20150211
修改标识:Senparc - 20150303
修改描述:整理接口
----------------------------------------------------------------*/
using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
//using System.Web;
//-40001 : 签名验证错误
//-40002 : xml解析失败
//-40003 : sha加密生成签名失败
//-40004 : AESKey 非法
//-40005 : appid 校验错误
//-40006 : AES 加密失败
//-40007 : AES 解密失败
//-40008 : 解密后得到的buffer非法
//-40009 : base64加密异常
//-40010 : base64解密异常
namespace Senparc.Weixin.Tencent
{
public class WXBizMsgCrypt
{
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 WXBizMsgCrypt(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 Senparc.CO2NET.ExtensionEntities.XmlDocument_XxeFixed();
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 : 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;//-40001
}
}
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
{
#if NET35 || NET40 || NET45
sha = new SHA1CryptoServiceProvider();
#else
sha = SHA1.Create();
#endif
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;//-40003
}
sMsgSignature = hash;
return 0;
}
}
}