using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Collections.Generic;
namespace Alipay.EasySDK.Kernel.Util
{
///
/// SHA256WithRSA签名器
///
public class Signer
{
///
/// 计算签名
///
/// 待签名的内容
/// 私钥
/// 签名值的Base64串
public static string Sign(string content, string privateKeyPem)
{
try
{
using (RSACryptoServiceProvider rsaService = BuildRSAServiceProvider(Convert.FromBase64String(privateKeyPem)))
{
byte[] data = AlipayConstants.DEFAULT_CHARSET.GetBytes(content);
byte[] sign = rsaService.SignData(data, "SHA256");
return Convert.ToBase64String(sign);
}
}
catch (Exception e)
{
string errorMessage = "签名遭遇异常,content=" + content + " privateKeySize=" + privateKeyPem.Length + " reason=" + e.Message;
Console.WriteLine(errorMessage);
throw new Exception(errorMessage, e);
}
}
///
/// 验证签名
///
/// 待验签的内容
/// 签名值的Base64串
/// 支付宝公钥
/// true:验证成功;false:验证失败
public static bool Verify(string content, string sign, string publicKeyPem)
{
try
{
using (RSACryptoServiceProvider rsaService = new RSACryptoServiceProvider())
{
rsaService.PersistKeyInCsp = false;
rsaService.ImportParameters(ConvertFromPemPublicKey(publicKeyPem));
return rsaService.VerifyData(AlipayConstants.DEFAULT_CHARSET.GetBytes(content),
"SHA256", Convert.FromBase64String(sign));
}
}
catch (Exception e)
{
string errorMessage = "验签遭遇异常,content=" + content + " sign=" + sign +
" publicKey=" + publicKeyPem + " reason=" + e.Message;
Console.WriteLine(errorMessage);
throw new Exception(errorMessage, e);
}
}
///
/// 对参数集合进行验签
///
/// 参数集合
/// 支付宝公钥
/// true:验证成功;false:验证失败
public static bool VerifyParams(Dictionary parameters, string publicKeyPem)
{
string sign = parameters[AlipayConstants.SIGN_FIELD];
parameters.Remove(AlipayConstants.SIGN_FIELD);
parameters.Remove(AlipayConstants.SIGN_TYPE_FIELD);
string content = GetSignContent(parameters);
return Verify(content, sign, publicKeyPem);
}
private static string GetSignContent(IDictionary parameters)
{
// 把字典按Key的字母顺序排序
IDictionary sortedParams = new SortedDictionary(parameters, StringComparer.Ordinal);
IEnumerator> iterator = sortedParams.GetEnumerator();
// 把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder("");
while (iterator.MoveNext())
{
string key = iterator.Current.Key;
string value = iterator.Current.Value;
query.Append(key).Append("=").Append(value).Append("&");
}
string content = query.ToString().Substring(0, query.Length - 1);
return content;
}
private static RSAParameters ConvertFromPemPublicKey(string pemPublickKey)
{
if (string.IsNullOrEmpty(pemPublickKey))
{
throw new Exception("PEM格式公钥不可为空。");
}
//移除干扰文本
pemPublickKey = pemPublickKey.Replace("-----BEGIN PUBLIC KEY-----", "")
.Replace("-----END PUBLIC KEY-----", "").Replace("\n", "").Replace("\r", "");
byte[] keyData = Convert.FromBase64String(pemPublickKey);
bool keySize1024 = (keyData.Length == 162);
bool keySize2048 = (keyData.Length == 294);
if (!(keySize1024 || keySize2048))
{
throw new Exception("公钥长度只支持1024和2048。");
}
byte[] pemModulus = (keySize1024 ? new byte[128] : new byte[256]);
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, (keySize1024 ? 29 : 33), pemModulus, 0, (keySize1024 ? 128 : 256));
Array.Copy(keyData, (keySize1024 ? 159 : 291), pemPublicExponent, 0, 3);
RSAParameters para = new RSAParameters
{
Modulus = pemModulus,
Exponent = pemPublicExponent
};
return para;
}
private static RSACryptoServiceProvider BuildRSAServiceProvider(byte[] privateKey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
//set up stream to decode the asn.1 encoded RSA private key
//wrap Memory Stream with BinaryReader for easy reading
using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(privateKey)))
{
twobytes = binaryReader.ReadUInt16();
//data read as little endian order (actual data order for Sequence is 30 81)
if (twobytes == 0x8130)
{
//advance 1 byte
binaryReader.ReadByte();
}
else if (twobytes == 0x8230)
{
//advance 2 bytes
binaryReader.ReadInt16();
}
else
{
return null;
}
twobytes = binaryReader.ReadUInt16();
//version number
if (twobytes != 0x0102)
{
return null;
}
bt = binaryReader.ReadByte();
if (bt != 0x00)
{
return null;
}
//all private key components are Integer sequences
elems = GetIntegerSize(binaryReader);
MODULUS = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
E = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
D = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
P = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
Q = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
DP = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
DQ = binaryReader.ReadBytes(elems);
elems = GetIntegerSize(binaryReader);
IQ = binaryReader.ReadBytes(elems);
//create RSACryptoServiceProvider instance and initialize with public key
RSACryptoServiceProvider rsaService = new RSACryptoServiceProvider();
RSAParameters rsaParams = new RSAParameters
{
Modulus = MODULUS,
Exponent = E,
D = D,
P = P,
Q = Q,
DP = DP,
DQ = DQ,
InverseQ = IQ
};
rsaService.ImportParameters(rsaParams);
return rsaService;
}
}
private static int GetIntegerSize(BinaryReader binaryReader)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binaryReader.ReadByte();
//expect integer
if (bt != 0x02)
{
return 0;
}
bt = binaryReader.ReadByte();
if (bt == 0x81)
{
//data size in next byte
count = binaryReader.ReadByte();
}
else if (bt == 0x82)
{
//data size in next 2 bytes
highbyte = binaryReader.ReadByte();
lowbyte = binaryReader.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
//we already have the data size
count = bt;
}
while (binaryReader.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
//last ReadByte wasn't a removed zero, so back up a byte
binaryReader.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
}
}