设计安全HTTP接口方案

微信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,"参数");

你可能感兴趣的:(C#)