前言
由于學(xué)校要求,需要做一個與安全傳輸相關(guān)的小項目秋忙,項目大小不要緊彩掐,功能不完善不要緊,只要能體現(xiàn)安全傳輸灰追,主要功能實現(xiàn)堵幽,能跑起來就行,于是我決定基于socket弹澎,tcp來做一個以.net為服務(wù)端朴下,ios為客戶端的簡單聊天室,然后使用rsa與aes來模仿https的加密過程苦蒿。一開始我以為很簡單殴胧,以為加密算法是知道的直接套用就好,結(jié)果,發(fā)現(xiàn)坑好多 = =
Demo地址
關(guān)于aes和rsa
aes是對稱加密团滥,rsa是非對稱加密竿屹,什么是對稱加密和非對稱加密自己去網(wǎng)上了解了解,這里就不多說灸姊,rsa原理可以看這里,ase可以看這里拱燃。
rsa和ase的加密原理可以不懂,但什么是對稱加密和非對稱加密一定要知道是什么概念厨钻,不然這篇文章你是看不懂的扼雏。
加密流程
主要是模仿https的過程,只是我把服務(wù)器和客戶端的加密角色反過來了夯膀。
- 首先在客戶端連接服務(wù)器后生成rsa的公鑰和密鑰诗充,并把公鑰發(fā)送給服務(wù)器
- 服務(wù)器收到rsa的公鑰后對此客戶端的socket對象生成一個隨機的對應(yīng)的aes密鑰,并使用rsa公鑰進行加密發(fā)送給客戶端诱建。
- 客戶端收到加密過的aes密鑰后使用rsa的密鑰進行解密得到aes的密鑰并保存起來
- 之后客戶端與服務(wù)端的數(shù)據(jù)傳輸都使用aes密鑰進行加密/解密蝴蜓。
加密順序圖:
第三方庫
那些坑!0吃场茎匠!
-
rsa
一開始我以為直接用網(wǎng)上的庫可以不使用openssl來生成證書來生成rsa的公鑰和密鑰,然后直接吧公鑰和發(fā)給服務(wù)器加密就好了押袍,但我簡單的試了一試诵冒,發(fā)現(xiàn)不行,無法相互加密解密谊惭,結(jié)果我發(fā)現(xiàn)ios那邊生成的公鑰是這樣子的……
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTbZ6cNH9PgdF60aQKveLz3FTalyzHQwbp601y77SzmGHX3F5NoVUZbdK7UMdoCLK4FBziTewYD9DWvAErXZo9BFuI96bAop8wfl1VkZyyHTcznxNJFGSQd/B70/ExMgMBpEwkAAdyUqIjIdVGh1FQK/4acwS39YXwbS+IlHsPSQIDAQAB
然后.net那邊是這樣的……
<RSAKeyValue>
<Modulus>veJTjzABfYl9/9YbOOw+EcfIZ1jajWiGPAXW+G/2uIUYh9L9WSyLPghl3oMZM2GDxp9f+chdc0k9lrNbJwsIaereleTiOcl2u/rq+jS8r2WhTfk8bIMWeAO/B5S0IjIbmKXdymRNZFw/KjuY99HdYk6nB1MZ1AJ/e3xJKwWJrXp55Y4DlX9sGTORS9kcy1q4fuxjJHUFAYsasV/vnsjlaau4sp6xBcDIfT1tRkxMZriDPW3J7qH7/qI2X4+iQZQIfAyFpSlqi+4GI9FajogEareCS7d1vc+OYdGc+jX1h0erHsRtlvFHzLcqBmZQ0Gz0XdZxOwJf5vhxUvvoK9BmBw==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
格式壓根不一樣好吧H1尽!為啥.net是xml扒谩L浮!V谡!握牧!
后來我把<Modulus>標簽里面的公鑰提出來發(fā)現(xiàn)還是不行…………
后來各種谷歌stackoverflow,發(fā)現(xiàn)原來在iOS上使用OpenSSL生成的公鑰私鑰編碼是X509的娩梨,而.NET的編碼是PKCS#8沿腰,所以并不兼容。于是繼續(xù)各種谷歌stackoverflow姚建,終于找到個ios端支持.net端那種的加密解密格式rsa框架(EncryptionForiOS)矫俺,但這個框架有個缺點,就是無法用服務(wù)端生成的公鑰進行加密,而是只能用自身生成的公鑰進行加密厘托,這就是為什么我的客戶端服務(wù)器和https的加密模式反過來的原因友雳。
不過依然需要注意的是,這個框架生成的公鑰并不是xml格式的铅匹,所以我們要使用字符串拼接成xml格式押赊,服務(wù)端才能加密。
NSString* publicKey=[[NSString alloc]initWithFormat:@"<RSAKeyValue><Modulus>%@</Modulus><Exponent>%@</Exponent></RSAKeyValue>",[self.rsaHelper getPublicKey],[self.rsaHelper getExponent]];
于是服rsa這塊就成功了………………
-
aes
我認為aes比rsa更坑包斑,首先一樣的我先在.net端生成aes的密鑰然后用rsa公鑰給加密發(fā)給ios端流礁,ios端再用rsa的密鑰解密得到aes的密鑰,因為rsa已經(jīng)成功了罗丰,所以到這步為止都是沒問題的神帅,所以說下一步問題就來了…………
首先還是在ios端用aes的密鑰加密,然后發(fā)送給服務(wù)端解密萌抵,結(jié)果無法解密找御,不過這也已經(jīng)預(yù)料到了,后來發(fā)現(xiàn)原因是在ios上我使用的aes128算法绍填,而.net上的是aes256算法霎桅,所以無法解密,于是還是各種谷歌stackoverflow讨永,找了n多的框架(上面的EncryptionForiOS雖然里面也有提供aes的解密滔驶,但各種原因讓我一開始以為他是無效的,但實質(zhì)上有待驗證)卿闹,最好我找到了個配套的ios端和.net端相互配合的框架揭糕,RNCryptor-objc和RNCryptor-cs,我想這下子沒錯了吧1仍2宸稹杠巡!都是同一個框架同一個作者寫的東西量窘,不會不兼容吧!G庥怠蚌铜!
結(jié)果,依然無法相互加密解密…………
后來我觀察的傳輸?shù)男畔?br> ios端傳輸前的base64string是這樣的
AwGNQlDqaaPn/xGqIm94l1sJYn7su/3wQiUMN/7h2VJeFVjB1NVycu/evTPz+IVuBfLbtY7Wzh0p4Jmlk1jzvc6Wd053+YxaZCpsKbbC3KuZMw==
到了.net端的base64string就變成這樣了………………
AwGNQlDqaaPn/xGqIm94l1sJYn7su/3wQiUMN/7h2VJeFVjB1NVycu/evTPz+IVuBfLbtY7Wzh0p4Jmlk1jzvc6Wd053+YxaZCpsKbbC3KuZMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
臥槽冬殃??叁怪?哪來的這么多A审葬???涣觉?痴荐?
我研究了好久…………網(wǎng)上也沒找到答案…………后來我把byte的長度輸出來看看,發(fā)現(xiàn)ios那邊是按我輸入的字符多少來的官册,而到了.net端就永遠都是1024………………然后我大概明白了…………
其實看看我拿來接收信息的byte[]就懂了……
byte[] recvBytes = new byte [1024];
int ReceiveCount = cs.clientSocket.Receive (recvBytes,recvBytes.Length,0);
對的生兆,沒錯我拿來接收信息的byte[]大小是1024,于是我把這個byte轉(zhuǎn)成base64string的時候膝宁,c#自動把空缺的部分用A填充了Q荒选!员淫!于是便變成這樣…………(==是代表結(jié)束合蔽,所以A填充在了==前面)
于是我的解決方法是這樣的
byte[] recvBytes = new byte [1024];
int ReceiveCount = cs.clientSocket.Receive (recvBytes,recvBytes.Length,0);
if(ReceiveCount!=0)
{
byte[] recvByte = new byte [ReceiveCount];
for(int t=0;t<ReceiveCount;t++)
{
recvByte[t]=recvBytes[t];
}
string ciphertext = Convert.ToBase64String(recvByte);
}
于是ciphertext就可以正常加密解密了…………于是aes也ok,整個程序就能正常運行了~
運行情況
直接上截圖吧
可以看到手機顯示的和電腦端的模擬器同步并且傳輸?shù)膬?nèi)容成功加密解密(控制臺上輸出的是密文)
至此介返,整項目到此為止辈末,我可以交作業(yè)了~
后記
也許有人說啊,直接用https不就得了嗎映皆?干嘛這么辛苦挤聘。但其實知識和錢一樣,永遠都不嫌多捅彻,難道不是嗎组去?
最后的最后,感謝github步淹,感謝stackoverflow从隆,感謝開源!