//此处走JAVA服务,签名数据
string HttpSignData(string action, string data)
{
var req = HttpWebRequest.Create("http://127.0.0.1:8080/myweb/hello-servlet?action=" + action);
req.Method = "POST";
req.ContentType = "appliction/json";
var buf = Encoding.UTF8.GetBytes(data);
using (var sw = req.GetRequestStream())
{
sw.Write(buf, 0, buf.Length);
}
var str = "";
using (var stream = req.GetResponse().GetResponseStream())
{
using (var sr = new StreamReader(stream))
{
str = sr.ReadToEnd();
}
}
return str;
}
private void button1_Click(object sender, EventArgs e)
{
var strLog = new StringBuilder();
//配置信息
var config = new WxConfig();
//当前页面路径
var urlpath = "https://api.xjshw.com/wxa/getuserriskrank";// "http://localhost:28085/Default2";// Request.Url.AbsoluteUri;
//额外数据
var aad = urlpath + "|" + WxConfig.appid + "|" + config.timestamp + "|" + WxConfig.aes_sn;
#region 日志
strLog.Append("\r\n-----------数据:\r\n");
strLog.Append("iv:" + config.iv + "\r\n");
strLog.Append("nonce:" + config.nonce + "\r\n");
strLog.Append("timestamp:" + config.timestamp + "\r\n");
strLog.Append("appid:" + WxConfig.appid + "\r\n");
strLog.Append("sn:" + WxConfig.aes_sn + "\r\n\r\n");
strLog.Append("额外数据:" + aad + "\r\n\r\n");
#endregion
#region 请求数据包明文
dynamic data = new ExpandoObject();
data.wx_store_id = "4000000000000650042";
data._n = config.nonce;
data._appid = WxConfig.appid;
data._timestamp = config.timestamp;
#endregion
//将请求的数据对象转成JSON字符串
var plaintext = Newtonsoft.Json.JsonConvert.SerializeObject(data);
strLog.Append("请求数据:" + plaintext + "\r\n\r\n");
#region 加密数据-AES256_GCM
//GCM模式输出的认证信息,使用base64编码
//AES256_GCM 方式加密数据
var aesResultBuff = AesGcmHelper.AesGcmEncrypt(plaintext, WxConfig.aes_key, config.iv, aad);// associatedData);
#region java方式加密
var sss = HttpSignData("encryption", Newtonsoft.Json.JsonConvert.SerializeObject(new { plaintext = plaintext, key = WxConfig.aes_key, iv = config.iv, aad = aad }));
var jsonObj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(sss);
var a = jsonObj.Value<String>("data").ToString();
var b = jsonObj.Value<String>("authtag").ToString();
var c = jsonObj.Value<String>("iv").ToString();
#endregion
#region 测试解密数据
var dataBuff = Convert.FromBase64String(a);// (aesResultBuff.EncryptedData);
var authBuff = Convert.FromBase64String(b);// (aesResultBuff.AuthTag);
var rawBuff = new byte[dataBuff.Length + authBuff.Length];
Array.Copy(dataBuff, 0, rawBuff, 0, dataBuff.Length);
Array.Copy(authBuff, 0, rawBuff, dataBuff.Length, authBuff.Length);
// var rawBase64Str = Convert.ToBase64String(rawBuff);
var decryptData = AesGcmHelper.AesGcmDecrypt(rawBuff, WxConfig.aes_key, config.iv, aad);// associatedData);
#endregion
strLog.Append("-----------加密:\r\n\r\n");// + Newtonsoft.Json.JsonConvert.SerializeObject(data) + "\r\n\r\n");
strLog.Append("加密数据:" + a + "\r\n\r\n");
strLog.Append("授权标签:" + b + "\r\n\r\n");
#endregion
#region 发送的数据包对象
var reqData = new
{
iv = config.iv,//加密向量
data = aesResultBuff.EncryptedData,//加密数据
authtag = aesResultBuff.AuthTag//授权标签
};
#endregion
//转JSON字符串
var reqDataJsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(reqData);
strLog.Append("-----------发送的数据包:" + reqDataJsonStr + "\r\n\r\n");
strLog.Append("-----------签名:\r\n\r\n");
#region 数量签名
//签名字段格式
//开发者需先拼接待签名串,使用 urlpath\n appid\n timestamp\n postdata 格式,字段之间使用换行符\n做分隔符。
//待签名数据
var payload = urlpath + "\n" + WxConfig.appid + "\n" + config.timestamp + "\n" + reqDataJsonStr;
strLog.Append("待签名数据:" + payload + "\r\n\r\n");
//开始签名
var payloadSigned = this.HttpSignData("sign", payload);
strLog.Append("签名结果:" + payloadSigned + "\r\n\r\n");
#endregion
#region 封装网络请求对象
var access_token = Senparc.Weixin.MP.CommonAPIs.AccessTokenContainer.TryGetToken(WxConfig.appid, WxConfig.appsecret, true);
var url = "https://api.weixin.qq.com/cgi-bin/express/intracity/querystore?access_token=" + access_token;
var req = WebRequest.CreateHttp(url);
req.Method = "POST";
req.ContentType = "application/json;charset=utf-8";
req.Accept = "application/json";
//req.Referer = urlpath;
#endregion
#region 设置请求头-添加签名信息
req.Headers.Add("Wechatmp-Appid", WxConfig.appid);
req.Headers.Add("Wechatmp-TimeStamp", config.timestamp.ToString());
req.Headers.Add("Wechatmp-Signature", payloadSigned);
strLog.Append("-----------请求头:\r\n\r\n");
strLog.Append("Wechatmp-Appid:" + WxConfig.appid + "\r\n\r\n");
strLog.Append("Wechatmp-TimeStamp:" + config.timestamp + "\r\n\r\n");
strLog.Append("Wechatmp-Signature:" + payloadSigned + "\r\n\r\n");
#endregion
//待发送的缓存区
var reqDataBytes = Encoding.UTF8.GetBytes(reqDataJsonStr);
//发送数据
using (var sr = req.GetRequestStream())
{
sr.Write(reqDataBytes, 0, reqDataBytes.Length);
}
var content = "";
using (var stream = req.GetResponseAsync().Result.GetResponseStream())
{
using (var sr = new StreamReader(stream))
{
content = sr.ReadToEnd();
}
}
strLog.Append("-----------微信返回:\r\n\r\n");
strLog.Append(content);
textBox1.Text = strLog.ToString();
// Label1.Text = strLog.ToString();
---------------------------------------------------aes加密解密
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
namespace Hidistro.Core
{
/// <summary>
/// 此加密算法和微信提供的加密算法结果一致
/// </summary>
public class AesGcmResult
{
/// <summary>
/// 加密数据
/// </summary>
public string EncryptedData { get; set; }
/// <summary>
/// 授权标签
/// </summary>
public string AuthTag { get; set; }
}
public class AesGcmHelper
{
/// <summary>
/// AES GCM加密
/// </summary>
/// <param name="plainText">加密数据</param>
/// <param name="base64Key">加密KEY,BASE64编码</param>
/// <param name="base64Iv">随机数,BASE64编码</param>
/// <param name="associatedData">aad 额外数据</param>
/// <returns></returns>
public static /*byte[]*/ AesGcmResult AesGcmEncrypt(string plainText, string base64Key, string base64Iv, string aad = null)// byte[] associatedData = null)
{
byte[] realPlaintext = Encoding.UTF8.GetBytes(plainText);
byte[] key = Convert.FromBase64String(base64Key);// Encoding.UTF8.GetBytes(aesKey);
var realIv = Convert.FromBase64String(base64Iv);// "aFYXNjnWubVjvhqh");
var cipher = new GcmBlockCipher(new AesEngine());// AesFastEngine());//
byte[] associatedData = null;
if (!string.IsNullOrWhiteSpace(aad))
{
associatedData = Encoding.UTF8.GetBytes(aad);
}
AeadParameters parameters = new AeadParameters(new KeyParameter(key), 128, realIv, associatedData);
cipher.Init(true, parameters);
byte[] ciphertext = new byte[cipher.GetOutputSize(plainText.Length)];
int len = cipher.ProcessBytes(realPlaintext, 0, realPlaintext.Length, ciphertext, 0);
//这个库加密完成会直接把tag放在密文最后面,因此,加解密不需要关注tag,跟其他库不太一致,需要注意下
cipher.DoFinal(ciphertext, len);
byte[] encryptedData = new byte[ciphertext.Length - 16];
//取出授权标签数据部分
byte[] authTag = new byte[16];
Array.Copy(ciphertext, 0, encryptedData, 0, ciphertext.Length - 16);
Array.Copy(ciphertext, ciphertext.Length - 16, authTag, 0, 16);
//base64编码
var encryptedDataStr = Convert.ToBase64String(encryptedData);
var authtag = Convert.ToBase64String(authTag);
return new AesGcmResult { EncryptedData = encryptedDataStr, AuthTag = authtag };
}
/// <summary>
/// 解密数据
/// </summary>
/// <param name="data"></param>
/// <param name="aesKey"></param>
/// <param name="iv"></param>
/// <param name="associatedData"></param>
/// <returns></returns>
public static String AesGcmDecrypt(/*string data,*/byte[] ciphertextbyte, string base64Key, string base64IV, string aad = null)// byte[] associatedData = null)
{
byte[] key = Convert.FromBase64String(base64Key);// Encoding.UTF8.GetBytes(aesKey);
byte[] nonce = Convert.FromBase64String(base64IV);// new byte[12];
byte[] associatedData = null;
if (!string.IsNullOrWhiteSpace(aad))
{
associatedData = Encoding.UTF8.GetBytes(aad);
}
GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine());
AeadParameters parameters = new AeadParameters(new KeyParameter(key), 128, nonce, associatedData);
cipher.Init(false, parameters);
byte[] decryptedData = new byte[cipher.GetOutputSize(ciphertextbyte.Length)];
int len = cipher.ProcessBytes(ciphertextbyte, 0, ciphertextbyte.Length, decryptedData, 0);
cipher.DoFinal(decryptedData, len);
//返回解密字符串
return Encoding.UTF8.GetString(decryptedData);
}
}
}
以上是我做的整个过程,为何老是报无效签名,签名算法也是用微信提供的,私钥也是转成PCKS8格式,求教!!!
建议用公钥和私钥都下载下来,自己用公钥签名,本地用私钥验签。确保公钥和私钥是成对的。
开源项目 SKIT.FlurlHttpClient.Wechat 现已支持 API 安全鉴权模式,欢迎广大 C#/.NET 开发者使用~
GitHub 项目地址:https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat
Gitee 项目地址:https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat