ASP.NET WebAPI 安全与身份验证 基础验证与非对称加密
因为安全需要,又没有申请HTTPS证书 只对密码进行了非对称加密 同时服务端验证了是否有证书
本文参考了:
MSDN 13年6月的期刊(启用和自定义 ASP.NET Web API 服务的安全性 ):https://msdn.microsoft.com/zh-cn/magazine/dn201748.aspx
与园子里(C#使用RSA证书文件加密和解密示例):http://www.cnblogs.com/eshizhan/archive/2012/10/07/2713680.html
根据实际使用环境做了一定修改:
服务端 HttpModel 验证:
1 /// <summary> 2 /// 身份验证 3 /// </summary> 4 public class PHVHttpAuthentication : IHttpModule, IDisposable 5 { 6 /// <summary> 7 /// 初始化 8 /// </summary> 9 /// <param name="context"></param> 10 public void Init(HttpApplication context) 11 { 12 context.AuthenticateRequest += AuthenticateRequests; 13 context.EndRequest += TriggerCredentials; 14 } 15 16 private static void TriggerCredentials(object sender, EventArgs e) 17 { 18 HttpResponse resp = HttpContext.Current.Response; 19 if (resp.StatusCode == 401) 20 { 21 resp.Headers.Add("WWW-Authenticate", @"Basic realm='PHVIS'"); 22 } 23 } 24 25 private static void AuthenticateRequests(object sender, EventArgs e) 26 { 27 System.Web.HttpClientCertificate cert = HttpContext.Current.Request.ClientCertificate; 28 29 if (cert != null && cert.IsValid) //验证证书是否存在并且有效 30 { 31 string authHeader = HttpContext.Current.Request.Headers["Authorization"]; 32 33 if (authHeader != null) 34 { 35 AuthenticationHeaderValue authHeaderVal = AuthenticationHeaderValue.Parse(authHeader); 36 if (authHeaderVal.Parameter != null) 37 { 38 byte[] unencoded = Convert.FromBase64String(authHeaderVal.Parameter); 39 string userpw = Encoding.GetEncoding("iso-8859-1").GetString(unencoded); 40 41 string[] creds = userpw.Split(':'); 42 string path = HttpContext.Current.Server.MapPath("~/App_Data/token.pfx"); 43 X509Certificate2 prvcrt = new X509Certificate2(path, "rootshell", X509KeyStorageFlags.Exportable); 44 RSACryptoServiceProvider prvkey = (RSACryptoServiceProvider)prvcrt.PrivateKey; 45 var passwordbits = SecurityHelper.RSADecrypt(Convert.FromBase64String(creds[1]), prvkey.ExportParameters(true), false); 46 var password = Encoding.GetEncoding("iso-8859-1").GetString(passwordbits); 47 if (creds[0] == "Peter" && password == "123") 48 { 49 GenericIdentity gi = new GenericIdentity(creds[0]); 50 Thread.CurrentPrincipal = new GenericPrincipal(gi, null); 51 HttpContext.Current.User = Thread.CurrentPrincipal; 52 53 //string[] roles = "manager,admin".Split(','); 54 //Thread.CurrentPrincipal = new GenericPrincipal(gi, roles); 55 } 56 } 57 } 58 } 59 else 60 { 61 62 } 63 64 65 } 66 67 /// <summary> 68 /// 销毁 69 /// </summary> 70 public void Dispose() 71 { 72 73 } 74 75 76 }
Web.Config配置:
<modules> <add name="EthanAuthorize" type="Security.PHVHttpAuthentication, Security"/> </modules>
完成这两步就已经可以实现对用户名和密码的验证了
接下来就是在需要调用的API上加上AuthorizeAttribute标记既可:
1 /// <summary> 2 /// 获取数据 3 /// </summary> 4 /// <param name="appId">微信AppD</param> 5 /// <returns></returns> 6 [Authorize(Users = "Peter")] 7 public string Get(string appId) 8 { 9 if (User.Identity.IsAuthenticated) 10 { 11 return "ok"; 12 } 13 else 14 { 15 return "error"; 16 } 17 }
附上非对称加密的类:
1 /// <summary> 2 /// 加密解密类 3 /// </summary> 4 public class SecurityHelper 5 { 6 7 /// <summary> 8 /// 非对称加密 9 /// </summary> 10 /// <param name="DataToEncrypt">待加密的数据</param> 11 /// <param name="RSAKeyInfo">公玥信息</param> 12 /// <param name="DoOAEPPadding"></param> 13 /// <returns></returns> 14 public static byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding) 15 { 16 try 17 { 18 byte[] encryptedData; 19 //Create a new instance of RSACryptoServiceProvider. 20 using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider()) 21 { 22 23 //Import the RSA Key information. This only needs 24 //toinclude the public key information. 25 RSA.ImportParameters(RSAKeyInfo); 26 27 //Encrypt the passed byte array and specify OAEP padding. 28 //OAEP padding is only available on Microsoft Windows XP or 29 //later. 30 encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding); 31 } 32 return encryptedData; 33 } 34 //Catch and display a CryptographicException 35 //to the console. 36 catch (CryptographicException e) 37 { 38 Console.WriteLine(e.Message); 39 40 return null; 41 } 42 43 } 44 45 /// <summary> 46 /// 非对称解密 47 /// </summary> 48 /// <param name="DataToDecrypt">待解密信息</param> 49 /// <param name="RSAKeyInfo">私钥信息</param> 50 /// <param name="DoOAEPPadding"></param> 51 /// <returns></returns> 52 public static byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding) 53 { 54 try 55 { 56 byte[] decryptedData; 57 //Create a new instance of RSACryptoServiceProvider. 58 using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider()) 59 { 60 //Import the RSA Key information. This needs 61 //to include the private key information. 62 RSA.ImportParameters(RSAKeyInfo); 63 64 //Decrypt the passed byte array and specify OAEP padding. 65 //OAEP padding is only available on Microsoft Windows XP or 66 //later. 67 decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding); 68 } 69 return decryptedData; 70 } 71 //Catch and display a CryptographicException 72 //to the console. 73 catch (CryptographicException e) 74 { 75 Console.WriteLine(e.ToString()); 76 77 return null; 78 } 79 80 } 81 }
客户端的使用代码:
1 X509Certificate2 pubcrt = new X509Certificate2(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/token.cer")); 2 string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; 3 HttpWebRequest request = WebRequest.Create(string.Format("http://api.xxxxxx.com/api/token?appid={0}", appid)) as HttpWebRequest; 4 request.Method = "GET"; 5 request.UserAgent = DefaultUserAgent; 6 request.ClientCertificates.Add(pubcrt); 7 8 RSACryptoServiceProvider pubkey = (RSACryptoServiceProvider)pubcrt.PublicKey.Key; 9 UTF8Encoding ByteConverter = new UTF8Encoding(); 10 byte[] dataToEncrypt = ByteConverter.GetBytes("123"); 11 string password = Convert.ToBase64String(SecurityHelper.RSAEncrypt(dataToEncrypt, pubkey.ExportParameters(false), false)); 12 13 request.Credentials= new NetworkCredential("Peter", password); 14 15 var result= request.GetResponse() as HttpWebResponse; 16 17 var sr = new StreamReader(result.GetResponseStream(), Encoding.UTF8); 18 19 //返回结果网页(html)代码 20 string content = sr.ReadToEnd();