⽀付v3签名与验签
⽀付v3
新需求为了在⽹页上进⾏Native⽀付,在开发过程中⾛了很多弯路,⽹上的代码很多运⾏⽆法正常加解密,经过⼏天的读⽂档,百度,终于调通.⽂档详见
签名
HTTP请求⽅法\n URL\n 请求时间戳\n 请求随机串\n 请求报⽂主体\n
对上述串进⾏SHA256 WITH RSA加密得到signature再填充到HTTP头中
‘Authorization: WECHATPAY2-SHA256-RSA2048
mchid=“1900009191”,nonce_str=“593BEC0C930BF1AFEB40B4A08C8FB242”,signature=“uOVRnA4qG/MNn YzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk 3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4x ZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8Xy Ugx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==”,timestamp=“1554208460”,serial_no=“1DDE 55AD98ED71D6EDD4A4A16996DE7B47773A8C”’
/// <summary>
/// 签名
/// </summary>
/// <param name="filePath">证书地址</param>
param name/// <param name="body">请求的报⽂内容若为Get则为空</param>
/// <param name="method">请求⽅法</param>
/// <param name="url">链接绝对路径</param>
/// <returns></returns>
public string CreateSign(string filePath,string body,string method,string url){
//0时间戳 1随机串 2报⽂主题
string seconds =(DateTime.Now - DateTime.Parse("1970-01-01 08:00:00")).TotalSeconds.ToString().Split('.')[0];
string randoms =new Random().Next().ToString("X")+new Random().Next().ToString("X");
string s =string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", method, url, seconds, randoms, body);
s = Encryption.SHA256WithRSA(filePath, h_id,s);
string sign =string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",serial_no=\"{2}\",timestamp=\"{3}\",signature=\"{4}\"", h_id,randoms, um, seconds,s);
return sign;
}
/// <summary>
/// SHA256WithRSA加密签名
/// </summary>
/// <param name="pfxFilePath">证书路径</param>
/// <param name="pfxPassword">证书密码默认为商家的商户号</param>
/// <param name="s">需要加密的明⽂</param>
/// <returns></returns>
public static string SHA256WithRSA(string pfxFilePath,string pfxPassword,string s)
{
X509Certificate2 privateCert =new X509Certificate2(pfxFilePath, pfxPassword, X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider privateKey =(RSACryptoServiceProvider)privateCert.PrivateKey;
RSACryptoServiceProvider privateKey1 =new RSACryptoServiceProvider();
privateKey1.ImportParameters(privateKey.ExportParameters(true));
byte[] data = Encoding.UTF8.GetBytes(s);
byte[] signature = privateKey1.SignData(data,"SHA256");
//密⽂
string sign = Convert.ToBase64String(signature);
return sign;
}
验签
应答时间戳\n 应答随机串\n 应答报⽂主体\n
对上述的明⽂使⽤⽀付平台公钥对验签名串和签名进⾏SHA256 with RSA签名验证
/// <summary>
/// 验证签名
/// </summary>
/// <param name="filePath">证书⽂件地址</param>
/
// <param name="time">时间戳</param>
/// <param name="randoms">随机字符串</param>
/// <param name="body">报⽂主题</param>
/// <param name="sign">签名</param>
/// <param name="certnum">所使⽤的证书编号</param>
/// <returns></returns>
public bool VerifiedSign(string filePath ,string time,string randoms,string body,string sign,string certnum){
//先验证平台证书是否使⽤的⼀致如果不⼀致则重新获取证书
if(certnum != WeChatKey.PtzsNum){
string tosign =CreateSign(filePath,"","GET", ApiUrlList.wqmscUrl.Substring(ApiUrlList.wqmscUrl.IndexOf("com")+3));
string ptzs = HttpUtils.Get(ApiUrlList.wqmscUrl, tosign);
JObject o = JObject.Parse(ptzs);
//返回的证书json是⼀个数组
JArray data =(JArray)o["data"];
//解密证书所需要的数据随机字符串解密类型
string nonce = data[0]["encrypt_certificate"]["nonce"].ToString();
string associated_data = data[0]["encrypt_certificate"]["associated_data"].ToString();
//密⽂
string ciphertext= data[0]["encrypt_certificate"]["ciphertext"].ToString();
//更新平台证书
WeChatKey.PtzsNum = data[0]["serial_no"].ToString();
WeChatKey.PtzspublicKey = Encryption.AesGcmDecrypt(associated_data, nonce, ciphertext);
//获取最新证书依旧不相等则可能为恶意操作不予处理
if(certnum != WeChatKey.PtzsNum){
return false;
}
}
//验签
string s =string.Format("{0}\n{1}\n{2}\n", time, randoms, body);
return Encryption.VerifySign(WeChatKey.PtzspublicKey, sign, s);
}
/// <summary>
/
// SHA256WithRSA加密验签
/// </summary>
/// <param name="certPublickey">公钥</param>
/// <param name="sign">密⽂</param>
/// <param name="s">明⽂</param>
/// <returns></returns>
public static bool VerifySign(string certPublickey,string sign,string s)
{
X509Certificate2 publicCert =new X509Certificate2(Encoding.UTF8.GetBytes(certPublickey));
RSACryptoServiceProvider publicKey =(RSACryptoServiceProvider)publicCert.PublicKey.Key;
SHA256CryptoServiceProvider sHA =new SHA256CryptoServiceProvider();
return publicKey.VerifyData(Encoding.UTF8.GetBytes(s), sHA, Convert.FromBase64String(sign));
}
/// <summary>
/// AEAD_AES_256_GCM ⽤于解密⽀付回调的密⽂ciphertext
/// </summary>
/// <param name="associatedData">原数据类型</param>
/// <param name="nonce">随机字符串</param>
/// <param name="ciphertext">密⽂</param>
/// <returns></returns>
public static string AesGcmDecrypt(string associatedData,string nonce,string ciphertext)
{
GcmBlockCipher gcmBlockCipher =new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters =new AeadParameters(
new KeyParameter(Encoding.UTF8.GetBytes(WeChatKey.Apiv3Key)),
128,
Encoding.UTF8.GetBytes(nonce),
Encoding.UTF8.GetBytes(associatedData));
gcmBlockCipher.Init(false, aeadParameters);
byte[] data = Convert.FromBase64String(ciphertext);
byte[] plaintext =new byte[gcmBlockCipher.GetOutputSize(data.Length)];
int length = gcmBlockCipher.ProcessBytes(data,0, data.Length, plaintext,0);
gcmBlockCipher.DoFinal(plaintext, length);
return Encoding.UTF8.GetString(plaintext);
}
低版本的不到GcmBlockCipher AeadParameters 需要引⼊BouncyCastle.Crypto.dll
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论