.Net使用加密升級(jí)數(shù)據(jù)安全
最近又被【現(xiàn)場(chǎng)破解共享單車系統(tǒng)】刷了一臉,不得不開(kāi)始后怕:如何防止類似的情況發(fā)生?
想來(lái)想去,始終覺(jué)得將程序加密是最簡(jiǎn)單的做法。但是摩拜、ofo也有加密,為什么仍然被破解?那是因?yàn)檎?qǐng)求在傳輸過(guò)程中被篡改了關(guān)鍵參數(shù),從而導(dǎo)致服務(wù)器做出了錯(cuò)誤的響應(yīng)。
如何防止篡改?自然是使用加密,加密方式的不同會(huì)決定篡改的成本(開(kāi)頭故事里的哥們大概用了2天),目前大多數(shù)的加密方法是暴露其他參數(shù),只加密一個(gè)token,然后服務(wù)端驗(yàn)證token是否有效。從我的角度來(lái)講,這種加密方式雖然破解難度很高,但多半是用MD5、SHA1、hmacMd5或AES等方式進(jìn)行加密,結(jié)果往往是不可逆的,并且固定內(nèi)容加密——不管多少次結(jié)果總是相同的。
對(duì)于部分人來(lái)講,只要你暴露出來(lái)的數(shù)據(jù)就可以做很多事情了,而且如果使用這種加密手段,那么服務(wù)端和客戶端必須約定好加密手段和參與加密的字段(規(guī)則),這就相當(dāng)于:我(服務(wù)端)和你(客戶端)一起設(shè)計(jì)了鑰匙,然后在需要的時(shí)候(發(fā)起請(qǐng)求)你就往指定地方(服務(wù)器)丟一個(gè)上了鎖的箱子(加密后的數(shù)據(jù)),然后我制作鑰匙打開(kāi)鎖。我也可以丟箱子,然后你用同樣方式去打開(kāi)。
大家可以打開(kāi)的鎖安全么?想想都不靠譜。
更好的是什么?我們?cè)O(shè)想一下:我(服務(wù)端)給你(客戶端)制造鎖的圖紙,我有制造鑰匙的圖紙,在你需要的時(shí)候(發(fā)起請(qǐng)求)就做一把鎖,然后把貨物(原始數(shù)據(jù))放在箱子里鎖好(加密完成)丟在指定的地方(服務(wù)器),然后我制作出鑰匙,開(kāi)鎖拿走貨物(解密)。
現(xiàn)在感覺(jué)如何?就像情報(bào)機(jī)構(gòu),偵察員只負(fù)責(zé)按照規(guī)則傳遞密碼,而總部按照另一套規(guī)則解讀,這個(gè)過(guò)程中就算被監(jiān)聽(tīng)到,第三方也無(wú)法解讀——這種方式就是今天要說(shuō)的RSA。
該怎么做呢?很簡(jiǎn)單:基于現(xiàn)在基本都是用http交互數(shù)據(jù),相互傳輸數(shù)據(jù)用json再好不過(guò),所以我會(huì)要求客戶端將所有參數(shù)以json格式全部拼裝起來(lái),然后整串加密后傳輸?shù)椒?wù)器,請(qǐng)求內(nèi)容看上去是這樣的:

完全不知道這是一串什么鬼(如果我不是服務(wù)器的話):實(shí)際上這是一次登陸請(qǐng)求,里面包含賬戶、密碼、時(shí)間戳、執(zhí)行方法等,在服務(wù)端解密后是這樣的:
{"msg":{"method":"log_in","time":"2017-09-28 10:30:27","user_name":"test","user_pwd":"test"}}這就完成了一次正確的數(shù)據(jù)提交。并且如果有人修改了密文都會(huì)導(dǎo)致服務(wù)端解密失敗從而駁回請(qǐng)求。這種情況下,就算你破譯了整套客戶端,也僅僅能拿到一個(gè)公鑰,而遠(yuǎn)在服務(wù)端的私鑰還是好好的(除非你已經(jīng)攻破了人家的服務(wù)器),從而阻止參數(shù)在傳遞過(guò)程中被篡改(下面上點(diǎn)干貨)。
生成公私鑰:
/// <summary>
/// RSA 的密鑰產(chǎn)生 產(chǎn)生私鑰 和公鑰
/// </summary>
/// <param name="priKeys">私鑰</param>
/// <param name="pubKey">公鑰</param>
public static void rsaKey(out string priKeys, out string pubKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
priKeys = rsa.ToXmlString(true);
pubKey = rsa.ToXmlString(false);
}RSA加密:
/// <summary>
/// RSA加密(不限長(zhǎng)度)
/// </summary>
/// <param name="publicKey">公鑰</param>
/// <param name="text">要加密的文本</param>
/// <param name="enc">編碼方式</param>
/// <returns>密文</returns>
public static string rsaEncrypt(string pubKey, string text, Encoding enc)
{
using (var rsaProvider = new RSACryptoServiceProvider())
{
var inputBytes = enc.GetBytes(text);
rsaProvider.FromXmlString(pubKey);
int bufferSize = (rsaProvider.KeySize / 8) - 11;
var buffer = new byte[bufferSize];
using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream())
{
while (true)
{
int readSize = inputStream.Read(buffer, 0, bufferSize);
if (readSize <= 0)
{
break;
}
var temp = new byte[readSize];
Array.Copy(buffer, 0, temp, 0, readSize);
var encryptedBytes = rsaProvider.Encrypt(temp, false);
outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
}
return Convert.ToBase64String(outputStream.ToArray());
}
}
}RSA解密:
/// <summary>
/// rsa解密
/// </summary>
/// <param name="priKey">私鑰</param>
/// <param name="encrypted">要解密的字符串</param>
/// <param name="enc">編碼方式</param>
/// <returns>明文</returns>
public static string rsaDecrypt(string priKey, string encrypted, Encoding enc)
{
if (string.IsNullOrEmpty(encrypted))
{
return string.Empty;
}
if (string.IsNullOrWhiteSpace(priKey))
{
throw new ArgumentException("Invalid Private Key");
}
using (var rsaProvider = new RSACryptoServiceProvider())
{
var inputBytes = Convert.FromBase64String(encrypted);
rsaProvider.FromXmlString(priKey);
int bufferSize = rsaProvider.KeySize / 8;
var buffer = new byte[bufferSize];
using (MemoryStream inputStream = new MemoryStream(inputBytes),
outputStream = new MemoryStream())
{
while (true)
{
int readSize = inputStream.Read(buffer, 0, bufferSize);
if (readSize <= 0)
{
break;
}
var temp = new byte[readSize];
Array.Copy(buffer, 0, temp, 0, readSize);
var rawBytes = rsaProvider.Decrypt(temp, false);
outputStream.Write(rawBytes, 0, rawBytes.Length);
}
return enc.GetString(outputStream.ToArray());
}
}
}因?yàn)楸救藢?duì)js也有一些興趣,所以嘗試著利用js與服務(wù)器交互,然鵝發(fā)現(xiàn)js需要java格式的公私鑰,于是找到了轉(zhuǎn)換公私鑰的代碼,親測(cè)有效,在這里搬運(yùn)下,利己利他:
/// <summary>
/// RSA公鑰格式轉(zhuǎn)換(net轉(zhuǎn)java)
/// </summary>
/// <param name="pubKey">net格式公鑰</param>
/// <returns>java格式的公鑰</returns>
public static string rsaPubKeyNet2Java(string pubKey)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(pubKey);
BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
return Convert.ToBase64String(serializedPublicBytes);
}
/// <summary>
/// RSA私鑰格式轉(zhuǎn)換(net轉(zhuǎn)java)
/// </summary>
/// <param name="priKey">net格式私鑰</param>
/// <returns>java格式的私鑰</returns>
public static string rsaPriKeyNet2Java(string priKey)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(priKey);
BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
BigInteger exp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
BigInteger d = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText));
BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText));
BigInteger q = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText));
BigInteger dp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText));
BigInteger dq = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText));
BigInteger qinv = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText));
RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv);
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();
return Convert.ToBase64String(serializedPrivateBytes);
}進(jìn)行轉(zhuǎn)換需要用到一個(gè)三方類庫(kù),我的百度網(wǎng)盤(pán)里可以下載 http://pan.baidu.com/s/1b9Wy42,可以直接使用。
js的RSA加密也要用到一個(gè)庫(kù),在這里捎帶貼一下:
<script type="text/javascript" src="https://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
<script>
//實(shí)例化對(duì)象
var crypt = new JSEncrypt();
//設(shè)置私鑰
crypt.setPrivateKey('你的RSA公鑰');
$('#log_in').click(function () {
//拼裝發(fā)送數(shù)據(jù)
var post_data = crypt.encrypt('{"msg":{"method":"log_in","time":"' + Tool.getNowTime() + '","user_name":"' + $('.name').val() + '","user_pwd":"' + $('.pwd').val() + '"}}');
//在這里發(fā)起你的請(qǐng)求
});
</script>因?yàn)閖s可以被看到,所以你更需要RSA,因?yàn)槿勘豢吹揭矡o(wú)所謂了。
到此這篇關(guān)于.Net使用加密升級(jí)數(shù)據(jù)安全的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家
相關(guān)文章
ASP.NET使用HttpWebRequest讀取遠(yuǎn)程網(wǎng)頁(yè)源代碼
本文分享了一個(gè)使用HttpWebRequest讀取遠(yuǎn)程網(wǎng)頁(yè)的案例,供大家參考學(xué)習(xí)。2016-03-03
asp.net fileupload控件上傳文件與多文件上傳
這篇文章主要介紹了asp.net fileupload控件上傳文件的方法,fileupload控件多文件上傳,以及fileupload上傳時(shí)實(shí)現(xiàn)文件驗(yàn)證的方法,需要的朋友可以參考下2014-11-11
Asp.NEt郵箱驗(yàn)證修改密碼通過(guò)郵箱找回密碼功能
這篇文章主要介紹了Asp.NEt郵箱驗(yàn)證修改密碼通過(guò)郵箱找回密碼功能,需要的朋友可以參考下2017-10-10
通過(guò)HttpClient 調(diào)用ASP.NET Web API示例
本篇文章主要介紹了通過(guò)HttpClient 調(diào)用ASP.NET Web API示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03
.NET中獲取Access新增記錄Id怪現(xiàn)象解決方法
寫(xiě)了一個(gè)函數(shù)獲取Access表中指定用戶Id,要求當(dāng)傳入的用戶名不存在時(shí),則在表中新增一條記錄并返回Id2012-03-03
IdnentiyServer使用客戶端憑據(jù)訪問(wèn)API的實(shí)例代碼
這篇文章主要介紹了IdnentiyServer-使用客戶端憑據(jù)訪問(wèn)API的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-10-10
C# XML操作 代碼大全(讀XML,寫(xiě)XML,更新,刪除節(jié)點(diǎn),與dataset結(jié)合等)
C#操作XML(讀XML,寫(xiě)XML,更新,刪除節(jié)點(diǎn),與dataset結(jié)合等),以下就是操作XML的所有方法,相信可以滿足很大一部份的使用了。2009-06-06
C#實(shí)現(xiàn)HTTP協(xié)議迷你服務(wù)器(兩種方法)
用C#語(yǔ)言實(shí)現(xiàn)HTTP協(xié)議的服務(wù)器類本文將以兩種稍微有差別的方式用C#語(yǔ)言實(shí)現(xiàn);要完成高性能的Web服務(wù)功能,通常都是需要寫(xiě)入到服務(wù),如IIS,Apache Tomcat感興趣的朋友可以了解下,或許對(duì)你學(xué)習(xí)c#有所幫助2013-02-02

