最近在項(xiàng)目中需要對(duì)用戶信息及數(shù)據(jù)傳輸進(jìn)行加密验烧,網(wǎng)上搜索了下相關(guān)信息向叉,最后決定采用AES+RSA的方式。
AES:對(duì)稱加密眼五,加密與解密使用的是同樣的密鑰妆艘,所以速度快,但由于需要將密鑰在網(wǎng)絡(luò)傳輸看幼,所以安全性不高批旺。
RSA:非對(duì)稱加密,使用了一對(duì)密鑰诵姜,公鑰與私鑰汽煮,所以安全性高,但加密與解密速度慢棚唆。
所以我們結(jié)合兩者的特性暇赤,采用AES對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行加密,然后采用RSA對(duì)AES的秘鑰進(jìn)行加密宵凌。AES的秘鑰是客戶端生成的(iOS我采用的是UUID)鞋囊,然后把AES的秘鑰用不可逆的RSA加密,最后發(fā)送給服務(wù)器的是RSA加密過的AES的秘鑰和AES加密的數(shù)據(jù)(是不是有點(diǎn)暈~)瞎惫。服務(wù)器端保留RSA的私鑰溜腐,可以對(duì)AES的秘鑰進(jìn)行解密,然后用AES的秘鑰解密數(shù)據(jù)瓜喇。這樣就拿到客戶端的數(shù)據(jù)了挺益。服務(wù)器進(jìn)行數(shù)據(jù)處理過后,用傳過來(lái)的AES的秘鑰加密數(shù)據(jù)再傳給客戶端乘寒。
說(shuō)的比較籠統(tǒng)望众,具體的邏輯可以參考以下鏈接:http://www.reibang.com/p/ec7bb7325ff2
關(guān)于AES的集成,網(wǎng)上也是眾說(shuō)紛紜伞辛,以下是我的實(shí)現(xiàn)方式:
//MARK:AES加密
func aesEncrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = self.data(using: String.Encoding.utf8),
let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
return base64cryptString
}
else {
return nil
}
}
return nil
}
//MARK:AES解密
func aesDecrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = NSData(base64Encoded: self, options: .ignoreUnknownCharacters),
let cryptData = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
data.bytes, data.length,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8)
return unencryptedMessage
}
else {
return nil
}
}
return nil
}
關(guān)于aes key烂翰、iv的意義:參見:http://www.reibang.com/p/df828a57cb8f
集成后,可以到https://blog.zhengxianjun.com/online-tool/crypto/aes/進(jìn)行驗(yàn)證蚤氏。
RSA的集成就更惡心了甘耿。關(guān)于RSA的公鑰、私鑰的生成瞧捌,有iOS自己生成和服務(wù)器端生成兩種方式(具體的解釋請(qǐng)自行Google~)棵里,我們采用的是服務(wù)器端生成润文。拿到服務(wù)器給的公鑰后,怎么用呢殿怜?典蝌?
有人說(shuō),用SecrecySwift(參見:http://swiftcn.io/topics/84)头谜,集成BBRSACryptor過程中發(fā)現(xiàn)openssl怎么都報(bào)錯(cuò)骏掀,感謝http://www.reibang.com/p/1e5f214ad789這篇博客詳細(xì)的解釋,但是最后集成到項(xiàng)目中還是報(bào)錯(cuò)柱告。于是又搜索了番截驮。發(fā)現(xiàn)https://github.com/ideawu/Objective-C-RSA這個(gè)共享庫(kù)(人民群眾的力量強(qiáng)大啊~~),最后簡(jiǎn)單的集成际度,測(cè)試(測(cè)試地址:https://blog.zhengxianjun.com/online-tool/rsa/)葵袭,通過,阿彌陀佛9粤狻坡锡!
至此,可以開森的進(jìn)行加密了窒所。
不是所有的數(shù)據(jù)傳輸都需要這么嚴(yán)格的加密的鹉勒。一般只是對(duì)用戶名的密碼、支付等相關(guān)信息才需要這么復(fù)雜的加密的吵取。對(duì)于普通請(qǐng)求禽额,我們采用的是MD5。
MD5也是不可逆的加密皮官。主要用于服務(wù)器上存儲(chǔ)用戶的密碼脯倒。 用戶Login的時(shí)候,系統(tǒng)是把用戶輸入的密碼計(jì)算成MD5值臣疑,然后再去和系統(tǒng)中保存的MD5值進(jìn)行比較盔憨,而系統(tǒng)并不“知道”用戶的密碼是什么徙菠。
我們知道讯沈,如果直接對(duì)密碼進(jìn)行散列,那么黑客可以對(duì)通過獲得這個(gè)密碼散列值婿奔,然后通過查散列值字典(例如MD5密碼破解網(wǎng)站)缺狠,得到某用戶的密碼。
加Salt可以一定程度上解決這一問題萍摊。所謂加Salt方法挤茄,就是加點(diǎn)“佐料”。其基本想法是這樣的:當(dāng)用戶首次提供密碼時(shí)(通常是注冊(cè)時(shí))冰木,由系統(tǒng)自動(dòng)往這個(gè)密碼里撒一些“佐料”穷劈,然后再散列笼恰。而當(dāng)用戶登錄時(shí),系統(tǒng)為用戶提供的代碼撒上同樣的“佐料”歇终,然后散列社证,再比較散列值,已確定密碼是否正確评凝。
這里的“佐料”被稱作“Salt值”追葡,這個(gè)值是由系統(tǒng)隨機(jī)生成的,并且只有系統(tǒng)知道奕短。這樣宜肉,即便兩個(gè)用戶使用了同一個(gè)密碼,由于系統(tǒng)為它們生成的salt值不同翎碑,他們的散列值也是不同的谬返。即便黑客可以通過自己的密碼和自己生成的散列值來(lái)找具有特定密碼的用戶,但這個(gè)幾率太小了(密碼和salt值都得和黑客使用的一樣才行)日杈。
補(bǔ)充一點(diǎn):客戶端在向服務(wù)端接口進(jìn)行請(qǐng)求朱浴,如果請(qǐng)求信息進(jìn)行了加密處理,被第三方截取到請(qǐng)求包达椰,雖然第三方無(wú)法解密獲取其中的數(shù)據(jù)翰蠢,但是可以使用該請(qǐng)求包進(jìn)行重復(fù)的請(qǐng)求操作。如果服務(wù)端不進(jìn)行防重放攻擊啰劲,就會(huì)參數(shù)服務(wù)器壓力增大梁沧,數(shù)據(jù)紊亂的后果。而使用添加時(shí)間戳的方式可以解決這一問題蝇裤。在請(qǐng)求中廷支,除了時(shí)間戳,再添加一個(gè)nonce參數(shù)栓辜,這樣時(shí)間戳允許十五分鐘偏差恋拍,那么服務(wù)器只要記錄十五分鐘以內(nèi)的nonce參數(shù)就行了,這樣可以避免重放攻擊藕甩。
賦下MD5的加密方法施敢。
//MARK:MD5 加密方式 不可逆
var MD5: String! {
let str = self.cString(using: String.Encoding.utf8)
let strLen = CC_LONG(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
CC_MD5(str!, strLen, result)
let hash = NSMutableString()
for i in 0..<digestLen {
hash.appendFormat("%02x", result[i])
}
result.deallocate(capacity: digestLen)
return hash as String
}
本人QQ:297959735 郵箱:zgsddzwj@163.com,歡迎提意見狭莱。