iOS中的安全與加密

iOS中的安全與加密


一。HTTPS雙向認(rèn)證

Charles是大家所熟悉的抓包工具,如果網(wǎng)絡(luò)請求未經(jīng)過雙向認(rèn)證,那么我們可以通過Charles拿到請求的參數(shù)和返回,具體的操作方法請看這里。它的原理簡單的概括為Charles偽裝為服務(wù)器與客戶端通信。那么雙向認(rèn)證要做的工作就是在服務(wù)器與客戶端之間相互驗證,避免數(shù)據(jù)被Charles這類的中間人截取。

準(zhǔn)備工作

1.服務(wù)端會向我們提供.pem證書和.key密鑰
2.將.pem導(dǎo)為.cer文件
openssl x509 -inform der -in certificate.cer -out certificate.pem
3.將.pem和.key導(dǎo)為p12文件
openssl pkcs12 -export -in certificate.pem -inkey chejinjia.key -out certificate.p12
4.這里的設(shè)置是以swift語言,并且Alamofire請求為基礎(chǔ)的,注意將代碼里的.cer和p12換成真實(shí)的名字
import Alamofire

class UtimesNetWorkConfig {
    static let shared = UtimesNetWorkConfig()
    
    //https認(rèn)證
    func HTTPSAuthentication() {
        SessionManager.default.delegate.sessionDidReceiveChallenge = { session, challenge in
                        //驗證服務(wù)端                                                                      
            if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
                return self.verifyServer(challenge: challenge)
            }
            //驗證客戶端
            else if challenge.protectionSpace.authenticationMethod ==   NSURLAuthenticationMethodClientCertificate {
                return self.sendClientP12()
            }
            return (.cancelAuthenticationChallenge, nil)
        }
    }
}

  • sessionDidReceiveChallenge是對urlSession(_:didReceive:completionHandler:)的重寫,也就是發(fā)生網(wǎng)絡(luò)請求時的回調(diào),我們在這里面設(shè)置雙向認(rèn)證

    extension UtimesNetWorkConfig {
          //驗證服務(wù)端發(fā)來的證書
        private func verifyServer(challenge: URLAuthenticationChallenge) -> SessionChallenge{
            let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
            let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
            let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
            let cerPath = Bundle.main.path(forResource: "你的證書文件名", ofType: "cer")!
            let cerUrl = URL(fileURLWithPath:cerPath)
            if let localCertificateData = try? Data(contentsOf: cerUrl), remoteCertificateData.isEqual(localCertificateData) {
                let credential = URLCredential(trust: serverTrust)
                challenge.sender?.use(credential, for: challenge)
                return (URLSession.AuthChallengeDisposition.useCredential,
                        URLCredential(trust: challenge.protectionSpace.serverTrust!))
            } else {
                return (.cancelAuthenticationChallenge, nil)
            }
        }
        
        //將本地證書發(fā)送到服務(wù)端認(rèn)證
        private func sendClientP12() -> SessionChallenge {
            var identityAndTrust:IdentityAndTrust!
            var securityError:OSStatus = errSecSuccess
            
            let path: String = Bundle.main.path(forResource: "chejinjia", ofType: "p12") ?? ""
            let PKCS12Data = NSData(contentsOfFile:path)!
            let key : NSString = kSecImportExportPassphrase as NSString
            let options : NSDictionary = [key : "你的P12文件的密碼"] //客戶端證書密碼
            var items : CFArray?
            
            securityError = SecPKCS12Import(PKCS12Data, options, &items)
            
            if securityError == errSecSuccess {
                //            let certItems:CFArray = items as! CFArray;
                let certItemsArray:Array = items! as Array
                let dict:AnyObject? = certItemsArray.first;
                if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> {
                    // grab the identity
                    let identityPointer:AnyObject? = certEntry["identity"];
                    let secIdentityRef:SecIdentity = identityPointer as! SecIdentity
                    // grab the trust
                    let trustPointer:AnyObject? = certEntry["trust"]
                    let trustRef:SecTrust = trustPointer as! SecTrust
                    // grab the cert
                    let chainPointer:AnyObject? = certEntry["chain"]
                    identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef,
                                                        trust: trustRef, certArray:  chainPointer!)
                }
            }
            
            let urlCredential:URLCredential = URLCredential(
                identity: identityAndTrust.identityRef,
                certificates: identityAndTrust.certArray as? [AnyObject],
                persistence: URLCredential.Persistence.forSession);
            
            return (.useCredential, urlCredential);
        }
    }
    

以上兩個方法可以看出,雙向認(rèn)證無非是將服務(wù)端的證書和客戶端的證書對比,看是否是同一本證書。是,則網(wǎng)絡(luò)請求成功,不是的話,網(wǎng)絡(luò)請求就會被取消。

由此,Charles一類的抓包工具已經(jīng)無法再抓取到網(wǎng)絡(luò)請求了。


二。AES

雖然用了HTTPS雙向認(rèn)證,可我們的安全系數(shù)還是不夠高。因為請求的參數(shù)實(shí)際上還是明文。比較敏感的參數(shù)例如密碼,我們還應(yīng)該將它再次加密。

這里介紹一下如果使用第三方庫CryptoSwift進(jìn)行AES加密

準(zhǔn)備工作:

1.安裝CryptoSwift
2.和服務(wù)器約定好AES的加密模式及參數(shù)(這里使用CBC模式)
let key = Array("ed3f91d05bbd77a5aea5c82c07f11a7b".utf8)
let iv = Array("ed3f91d05bbd77a5".utf8)
let input = Array("wodemima".utf8)
do {
    let encrypt: Array<UInt8> = try AES(key: key, blockMode: CBC(iv: iv),
padding: .pkcs5).encrypt(input)
    let decrypt = try AES(key: key, blockMode: CBC(iv: iv),
padding: .pkcs5).decrypt(encrypt)
    if let string = String(bytes: decrypt, encoding: .utf8) {
        print(string)
    }
} catch let error {
    print(error)
}
  • key是和服務(wù)端約定好的密鑰
  • iv是偏移量,這里取Key的前16位,必須和服務(wù)器統(tǒng)一
  • input是用來測試的輸入
  • 代碼里encryptdecrypt是加密和解密操作
3.CryptoSwift還包含了md5,sha等很多的加解密方法,功能很完善,可根據(jù)實(shí)際需要選取使用。

三。MD5

import CommonCrypto
extension String {
    func MD5() -> String {
        let messageData = self.data(using:.utf8)!
        var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))
        
        _ = digestData.withUnsafeMutableBytes {digestBytes in
            messageData.withUnsafeBytes {messageBytes in
                CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
            }
        }
        
        return digestData.hexString()
    }
}
extension Data {
    func hexString() -> String {
        return self.reduce("") { string, byte in
            string + String(format: "%02X", byte)
        }
    }
}

系統(tǒng)庫CommonCrypto為我們實(shí)現(xiàn)了MD5加密,這里我們通過extension為string添加一個加密的調(diào)用。

值的一提的是,直接對敏感文本MD5的操作已經(jīng)不是那么安全了,因為可以通過反解來暴力解密。那么比較通用的方式是對MD5加鹽。

加鹽的意思就是對敏感文本添加一段文本,再進(jìn)行MD5。添加的這段文本越復(fù)雜越好。

例如:

let input = "password"
let inputSalty = "password" + "asdfghjklpoiuytrewqzxcvbnm"
print(inputSalty.MD5())

當(dāng)然鹽的值如果是固定的,也有一定風(fēng)險,如果鹽被泄漏,那么MD5的安全系數(shù)就會降低,因此可以使用時間戳或隨機(jī)數(shù)作為鹽?;蛘呤荕D5后截取部分再進(jìn)行MD5。

這里注意一點(diǎn)的是。由于MD5是不可逆的,所以當(dāng)傳給服務(wù)器的參數(shù)需要解密時,不應(yīng)該用MD5,而應(yīng)該使用AES這一類的可逆加密方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容