谈谈iOS加密:AES+RSA

最近在项目中需要对用户信息及数据传输进行加密,网上搜索了下相关信息,最后决定采用AES+RSA的方式。
AES:对称加密,加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。
RSA:非对称加密,使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。
所以我们结合两者的特性,采用AES对传输的数据进行加密,然后采用RSA对AES的秘钥进行加密。AES的秘钥是客户端生成的(iOS我采用的是UUID),然后把AES的秘钥用不可逆的RSA加密,最后发送给服务器的是RSA加密过的AES的秘钥和AES加密的数据(是不是有点晕~)。服务器端保留RSA的私钥,可以对AES的秘钥进行解密,然后用AES的秘钥解密数据。这样就拿到客户端的数据了。服务器进行数据处理过后,用传过来的AES的秘钥加密数据再传给客户端。
说的比较笼统,具体的逻辑可以参考以下链接:http://www.jianshu.com/p/ec7bb7325ff2

关于AES的集成,网上也是众说纷纭,以下是我的实现方式:

//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
    }

关于aes key、iv的意义:参见:http://www.jianshu.com/p/df828a57cb8f
集成后,可以到https://blog.zhengxianjun.com/online-tool/crypto/aes/进行验证。

RSA的集成就更恶心了。关于RSA的公钥、私钥的生成,有iOS自己生成和服务器端生成两种方式(具体的解释请自行Google~),我们采用的是服务器端生成。拿到服务器给的公钥后,怎么用呢??

有人说,用SecrecySwift(参见:http://swiftcn.io/topics/84),集成BBRSACryptor过程中发现openssl怎么都报错,感谢http://www.jianshu.com/p/1e5f214ad789这篇博客详细的解释,但是最后集成到项目中还是报错。于是又搜索了番。发现https://github.com/ideawu/Objective-C-RSA这个共享库(人民群众的力量强大啊~~),最后简单的集成,测试(测试地址:https://blog.zhengxianjun.com/online-tool/rsa/),通过,阿弥陀佛!!

至此,可以开森的进行加密了。

不是所有的数据传输都需要这么严格的加密的。一般只是对用户名的密码、支付等相关信息才需要这么复杂的加密的。对于普通请求,我们采用的是MD5。

MD5也是不可逆的加密。主要用于服务器上存储用户的密码。 用户Login的时候,系统是把用户输入的密码计算成MD5值,然后再去和系统中保存的MD5值进行比较,而系统并不“知道”用户的密码是什么。

我们知道,如果直接对密码进行散列,那么黑客可以对通过获得这个密码散列值,然后通过查散列值字典(例如MD5密码破解网站),得到某用户的密码。

加Salt可以一定程度上解决这一问题。所谓加Salt方法,就是加点“佐料”。其基本想法是这样的:当用户首次提供密码时(通常是注册时),由系统自动往这个密码里撒一些“佐料”,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的“佐料”,然后散列,再比较散列值,已确定密码是否正确。

这里的“佐料”被称作“Salt值”,这个值是由系统随机生成的,并且只有系统知道。这样,即便两个用户使用了同一个密码,由于系统为它们生成的salt值不同,他们的散列值也是不同的。即便黑客可以通过自己的密码和自己生成的散列值来找具有特定密码的用户,但这个几率太小了(密码和salt值都得和黑客使用的一样才行)。

补充一点:客户端在向服务端接口进行请求,如果请求信息进行了加密处理,被第三方截取到请求包,虽然第三方无法解密获取其中的数据,但是可以使用该请求包进行重复的请求操作。如果服务端不进行防重放攻击,就会参数服务器压力增大,数据紊乱的后果。而使用添加时间戳的方式可以解决这一问题。在请求中,除了时间戳,再添加一个nonce参数,这样时间戳允许十五分钟偏差,那么服务器只要记录十五分钟以内的nonce参数就行了,这样可以避免重放攻击。

赋下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.allocate(capacity: digestLen)

        CC_MD5(str!, strLen, result)

        let hash = NSMutableString()
        for i in 0..

本人QQ:297959735 邮箱:[email protected],欢迎提意见。

你可能感兴趣的:(谈谈iOS加密:AES+RSA)