微信API接口传输:
Token(长度3-32字符)、
加密秘钥(43位字符组成)
加密签名(token、时间戳、随机数)
参数 描述
signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
获取access_token
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
参数说明
参数 说明
access_token 获取到的凭证
expires_in 凭证有效时间,单位:秒
使用RSA和AES设计http API接口传输:
发送方:
1.生成签名
(1)将传输对象转换成json字符串
(2)使用MD5生成json字符串摘要
(3)使用RSA公钥对摘要字符串作加密处理,生成签名。
2.加密请求报文
创建http请求时动态生成一个AESKey,用来对请求数据加密。
3.使用RSA公钥加密AES秘钥
4.构造http请求
(1)签名字段Authencation
(2)加密后的AES秘钥SecurityKey
(3)时间戳TimesTamp
(4)加密报文http body中
http://localhost:8080/api/login
?Authencation=111111&SecurityKey=222222
&TimesTamp=13265322¶m=http body中
接收方:
1.验证时间戳
请求时间和系统时间间隔在5分钟内请求为合法,否则非法。
2.获取被RSA加密的AES秘钥
使用RSA秘钥解密SecurtiyKey字段,获取解密后的AES秘钥
3.获取请求报文
从httpbody中获取请求报文,使用解密后的AES秘钥对报文解密
4.验签
(1)对解密的报文使用MD5生成摘要
(2)使用RSA秘钥对Authencation字段解密,与MD5生成的摘要对比是否一致,一致则通过验签,否则不通过
5.返回处理结果
使用第二步解密后的AES秘钥加密返回结果,请求方使用AES解密即可,
这样真实的AES秘钥传输中不会被知道。
代码示例:
1.AESHelper
public class AESHelper
{
///
/// AES加密
///
/// 明文
/// 密钥
///
public static string Encrypt(string encryptStr, string key)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(encryptStr);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.ECB;
rDel.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = rDel.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
///
/// AES解密
///
/// 密文
/// 密钥
///
public static string Decrypt(string decryptStr, string key)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toEncryptArray = Convert.FromBase64String(decryptStr);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.ECB;
rDel.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return UTF8Encoding.UTF8.GetString(resultArray);
}
}
2.HttpHelper
public class HttpHelper
{
public static string Request(string data, string url)
{
return Request(Encoding.GetEncoding("UTF-8").GetBytes(data), url);
}
public static string Request(byte[] data, string url)
{
string result = string.Empty;
//创建一个客户端的Http请求实例
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
request.ContentLength = data.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
//获取当前Http请求的响应实例
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
Stream responseStream = response.GetResponseStream();
using (StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("UTF-8")))
{
result = reader.ReadToEnd();
}
responseStream.Close();
return result;
}
}
3.MD5Helper
public class MD5Helper
{
///
/// 32位MD5加密
///
///
///
public static string MD5Encrypt32(string password)
{
string cl = password;
string pwd = "";
MD5 md5 = MD5.Create(); //实例化一个md5对像
// 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择
byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
// 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
for (int i = 0; i < s.Length; i++)
{
// 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符
pwd = pwd + s[i].ToString("X");
}
return pwd;
}
public static string MD5Encrypt64(string password)
{
string cl = password;
//string pwd = "";
MD5 md5 = MD5.Create(); //实例化一个md5对像
// 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择
byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
return Convert.ToBase64String(s);
}
}
4.RSAHelper
public class RSAHelper
{
public static string privateKey = string.Empty;
public static string publicKey = string.Empty;
///
/// generate private key and public key arr[0] for private key arr[1] for public key
///
///
public static string[] GenerateKeys()
{
string[] sKeys = new String[2];
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
sKeys[0] = rsa.ToXmlString(true);
sKeys[1] = rsa.ToXmlString(false);
return sKeys;
}
///
/// RSA Encrypt
///
/// Source string
/// public key
///
public static string EncryptString(string sSource, string sPublicKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string plaintext = sSource;
rsa.FromXmlString(sPublicKey);
byte[] cipherbytes;
byte[] byteEn = rsa.Encrypt(Encoding.UTF8.GetBytes("a"), false);
cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(plaintext), false);
StringBuilder sbString = new StringBuilder();
for (int i = 0; i < cipherbytes.Length; i++)
{
sbString.Append(cipherbytes[i] + ",");
}
return sbString.ToString();
}
///
/// RSA Decrypt
///
/// Source string
/// Private Key
///
public static string DecryptString(String sSource, string sPrivateKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(sPrivateKey);
byte[] byteEn = rsa.Encrypt(Encoding.UTF8.GetBytes("a"), false);
string[] sBytes = sSource.Split(',');
for (int j = 0; j < sBytes.Length; j++)
{
if (sBytes[j] != "")
{
byteEn[j] = Byte.Parse(sBytes[j]);
}
}
byte[] plaintbytes = rsa.Decrypt(byteEn, false);
return Encoding.UTF8.GetString(plaintbytes);
}
}
5.TimeStampHelper
///
/// 时间戳
///
public class TimeStampHelper
{
///
/// 获取时间戳
///
///
public static string GetTimeStamp(System.DateTime time, int length = 13)
{
long ts = ConvertDateTimeToInt(time);
return ts.ToString().Substring(0, length);
}
///
/// 将c# DateTime时间格式转换为Unix时间戳格式
///
/// 时间
/// long
public static long ConvertDateTimeToInt(System.DateTime time)
{
System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0));
long t = (time.Ticks - startTime.Ticks) / 10000; //除10000调整为13位
return t;
}
///
/// 时间戳转为C#格式时间
///
///
///
public static DateTime ConvertStringToDateTime(string timeStamp)
{
DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
long lTime = long.Parse(timeStamp + "0000");
TimeSpan toNow = new TimeSpan(lTime);
return dtStart.Add(toNow);
}
///
/// 时间戳转为C#格式时间10位
///
/// Unix时间戳格式
/// C#格式时间
public static DateTime GetDateTimeFrom1970Ticks(long curSeconds)
{
DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
return dtStart.AddSeconds(curSeconds);
}
///
/// 验证时间戳
///
///
/// 差值(分钟)
///
public static bool IsTime(long time, double interval)
{
DateTime dt = GetDateTimeFrom1970Ticks(time);
//取现在时间
DateTime dt1 = DateTime.Now.AddMinutes(interval);
DateTime dt2 = DateTime.Now.AddMinutes(interval * -1);
if (dt > dt2 && dt < dt1)
{
return true;
}
else
{
return false;
}
}
///
/// 判断时间戳是否正确(验证前8位)
///
///
///
public static bool IsTime(string time)
{
string str = GetTimeStamp(DateTime.Now, 8);
if (str.Equals(time))
{
return true;
}
else
{
return false;
}
}
}
//请求方
UserModel userModel = new UserModel()
{
UserName = txtUserName.Text.Trim(),
Password = txtUserPassword.Text.Trim()
};
//1.请求对象转换成json
string jsonContent = JsonConvert.SerializeObject(userModel);
//2.使用MD5生成摘要
string md5Content = MD5Helper.MD5Encrypt64(jsonContent);
//生成RSA私钥和公钥
string[] generater = RSAHelper.GenerateKeys();
RSAHelper.privateKey = generater[0];
RSAHelper.publicKey = generater[1];
//3.使用RSA加密MD5摘要,生成签名
string signature = RSAHelper.EncryptString(md5Content, RSAHelper.publicKey);
//5.使用AES加密请求数据
string aeskey = Guid.NewGuid().ToString("N");
string requestData = AESHelper.Encrypt(jsonContent, aeskey);
//6.使用RSA加密AES秘钥
string securityKey = RSAHelper.EncryptString(aeskey, RSAHelper.publicKey);
//时间戳
long timesTamp = TimeStampHelper.ConvertDateTimeToInt(DateTime.Now);//TimeStampHelper.GetTimeStamp(DateTime.Now);
HttpHelper.Request(requestData,"参数");