iPhone开发Swift基础08 加密与安全

iPhone开发Swift基础08 加密与安全,第1张

三种常见的加密技术:3DES、SHA1、MD5。其中3DES和SHA1为对称加密,MD5为非对称加密。

加密

我们可以做的加密措施有哪些?

对设备中存储的内容进行加密,开发者经常使用的UserDefaults、plist文件、CoreData框架等作为存储数据的载体,在存入数据之前做一些加密以增强数据的安全性是很有必要的。对网络传输的数据进行加密,大部分的引用都需要进行网络通讯,由于网络传输的过程中的一些中间节点并不可控,所以必然存在着数据泄漏的风险,对敏感信息进行加密以保证用户的隐私权是很有必要的。对账户密码进行加密:加盐(salt)在密码学中是指,通过密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这个过程称之为加盐。加盐后的账户密码再经过诸如MD5等方式加密,然后与服务器采用同样的加盐和加密的密码进行比较,若两个密码相同,则密码匹配成功。这样无论在客户端还是服务器端,当传输的密码被截获后,即便是解密成功,也无法还原出原始的密码。 3DES加密

DES数据加密标准。

3DES三重数据加密算法,即对每个数据块应用三次DES加密算法。

由于随着计算机的发展,计算机的算力有了很大的提升,而3DES通过增加DES的密钥长度来避免破解,而不是设计一种全新的块密码算法。

swift中使用3DES函数需要 import CommonCrypto。

private let randomStringArray:[Character] = "abcdefghijklmnopqrstuvwsyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".map({$0})
var key:String = ""

func randomStringOfLength(length:Int) -> String {
    var string = ""
    for _ in 1...length {
        string.append(randomStringArray[Int(arc4random_uniform(UInt32(randomStringArray.count)-1))])
    }
    print("randomStringLength:\(length)")
    print("randomStringArray:\(randomStringArray)")
    print("randomString:\(string)")
    return string
}

func encrypt(encryptData:String) -> () {
    //从key参数到bytesDecrypted参数,都是为了生成CCCrypt方法参数的值。
    key = randomStringOfLength(length: kCCKeySize3DES)
    let inputData:Data = encryptData.data(using: String.Encoding.utf8)!
    
    let keydata:Data = key.data(using: String.Encoding.utf8,allowLossyConversion: false)!
    let keyBytes = UnsafeMutableRawPointer(mutating: (keydata as NSData).bytes)
    let keyLength = size_t(kCCKeySize3DES)
    
    let datalength = Int(inputData.count)
    let dataBytes = UnsafeRawPointer((inputData as NSData).bytes)
    let bufferData = NSMutableData(length: Int(datalength)+kCCBlockSize3DES)!
    let bufferPointer = UnsafeMutableRawPointer(bufferData.mutableBytes)
    let bufferLength = size_t(bufferData.length)
    var bytesDecrypted = Int(0)
    
    let cryptStatus = CCCrypt(UInt32(kCCEncrypt), UInt32(kCCEncrypt), UInt32(kCCOptionECBMode+kCCOptionPKCS7Padding), keyBytes, keyLength, nil, dataBytes, datalength, bufferPointer, bufferLength, &bytesDecrypted)
    
    if Int32(cryptStatus)==Int32(kCCSuccess) {
        bufferData.length = bytesDecrypted
        decrypt(inputData: bufferData as Data)
    }else{
        print("加密过程出错")
    }
}
代码解释:CCCrypt参数准备

从key参数到bytesDecrypted参数,都是为了生成CCCrypt方法参数的值。

key = randomStringOfLength(length: kCCKeySize3DES)

上述代码,通过randomStringOfLength方法生成一个随机字符串作为3DES加解密的Key值,其中kCCKeySize3DES是指Triple DES 加解密的Key的大小,其值为24,因此这里将生成一个长度为24的、包含英文大小写字母和数字的随机字符串。

let inputData:Data = encryptData.data(using: String.Encoding.utf8)!

上述代码,将待加密的字符串转换为Data类型。

let keydata:Data = key.data(using: String.Encoding.utf8,allowLossyConversion: false)!

上述代码,将作为随机字符串的key值同样转换为Data类型。

let keyBytes = UnsafeMutableRawPointer(mutating: (keydata as NSData).bytes)

上述代码,将创建一个UnsafeMutableRawPointer指针。在Swift中,指针都是用一个特殊的类型来表示的,这个特殊的类型是UnsafeRawPointer。相对应的,同样有一个特殊的类型是UnsafeMutableRawPointer。在创建该指针时,向系统申请了个数为(keydata as NSData).bytes的UInt8泛型类型的内存。

let keyLength = size_t(kCCKeySize3DES)

上述代码,创建了一个名为keyLength的常量,使用size_t方法取出kCCKeySize3DES的长度。因为kCCKeySize3DES已知长度是24,所以keyLength的值就是24。

let datalength = Int(inputData.count)

上述代码,获得待加密对象inputData的长度

let dataBytes = UnsafeRawPointer((inputData as NSData).bytes)

上述代码,创建了一个UnsafeRawPointer指针,并从系统中分配相应的内存作为加密的 input buffer。

let bufferData = NSMutableData(length: Int(datalength)+kCCBlockSize3DES)!

上述代码,创建了一个指定长度的NSMutableData可变的二进制数据对象,该对象将作为加密的 output buffer,用来存储加密后的数据。其长度是 input buffer 的长度和 3DES 加密的kCCBlockSize3DES块大小之和,kCCBlockSize3DES块的大小是8。

let bufferPointer = UnsafeMutableRawPointer(bufferData.mutableBytes)

上述代码,创建一个UnsafeMutableRawPointer类型的指针,并根据 output buffer,也就是bufferData的大小分配相应的内存。

let bufferLength = size_t(bufferData.length)
var bytesDecrypted = Int(0)

上述代码,获得 output buffer 的长度。并创建了一个变量bytesDecrypted,用于存储加密后的 output buffer 的最终字节数。

代码解释:CCCrypt方法执行

在所有的参数都准备好了之后就可以调用CommonCrypto框架中的CCCrypt方法了。

let cryptStatus = CCCrypt(
UInt32(kCCEncrypt), 
UInt32(kCCEncrypt), UInt32(kCCOptionECBMode+kCCOptionPKCS7Padding),
keyBytes, 
keyLength, 
nil, 
dataBytes, 
datalength, 
bufferPointer, 
bufferLength, 
&bytesDecrypted)

CCCrypt方法中的第一个参数表示进行加密 *** 作还是解密 *** 作,kCCEncrypt表示的是加密 *** 作。

第二个参数表示进行加密的算法,在CommonCrypto框架中提供了:

kCCAlgorithmAESkCCAlgorithmAES128kCCAlgorithmDESkCCAlgorithm3DESkCCAlgorithmCASTkCCAlgorithmRC4kCCAlgorithmBlowfish
等多种加密算法。

第三个参数用来设置 block ciphers 的选项, block ciphers 表示在使用密钥和算法对文本进行加密的方法。该选项可以是kCCOptionPKCS7Padding或者kCCOptionECBMode两者其中之一。如果使用PKCS7Padding,它的密钥可以是8个字节,也可以不是。

判断CCCrypt执行的结果

当执行完CCCrypt方法后,会返回一个cryptStatus状态,通过cryptStatus能判断是否加密成功。
CCCrypt方法的cryptStatus的几种状态:

kCCSuccess 加密解密 *** 作正常结束kCCParamError 非法的参数值kCCMemoryFailure 内存分配失败kCCBufferTooSmall 选项设置的缓存不够大kCCAlignmentError 输入的大小不匹配kCCDecodeError 输入的数据没有正确的解码或解密kCCUnimplemented 函数没有正确执行当前的算法
if Int32(cryptStatus)==Int32(kCCSuccess) {
    bufferData.length = bytesDecrypted
    decrypt(inputData: bufferData as Data)
}else{
    print("加密过程出错")
}

当判断加密 *** 作完成后,设置output data 的length为 bytesDecrypted,以调整输出的buffer的大小为最终输出加密数据的尺寸。

至此就完成了数据的加密 *** 作。

然后我们编辑ViewDidLoad案例,让其运行起来尝试一下加密。

override func viewDidLoad() {
    super.viewDidLoad()
    
    print("kCCBufferTooSmall:\(kCCBufferTooSmall)")
    print("kCCAlignmentError:\(kCCAlignmentError)")
    print("kCCDecodeError:\(kCCDecodeError)")
    
    let string1 = "todayPerfect"
    encrypt(encryptData: string1)
    let string2 = "today is a good day"
    encrypt(encryptData: string2)
}

运行结果:

kCCBufferTooSmall:-4301
kCCAlignmentError:-4303
kCCDecodeError:-4304
todayPerfect加密后:{length = 16, bytes = 0xc09e74b86d0fa97fd6bb1831eba14c96}
加密过程出错 error: -4301

由此可知,string2"today is a good day"在加密的过程中出错了,通过其打印的反馈可知,因为选项中设置的缓存大小不够,所以调整一下缓存大小即可。

let bufferLength = size_t(bufferData.length)*2

运行结果:

kCCBufferTooSmall:-4301
kCCAlignmentError:-4303
kCCDecodeError:-4304
todayPerfect加密后:{length = 16, bytes = 0xcae3ed8d4ca2dbbaa41bf8957de39bae}
today is a good day加密后:{length = 32, bytes = 0xbf45b7f9 265d93bd 22cb08b1 c1eac78e ... 875f1f00 00000000 }

加密成功了,从打印信息可以看到string1的长度是16,而string2的长度是32.至此解决了参数设置中因缓存不足而导致加密失败的问题。

解密

对密文进行解密 *** 作,接着使用相同的密钥对密文进行解密 *** 作。

3DES不会解密😓💦

有时候可以有时候又不行真是伤脑筋

SHA1加密

SHA1是安全哈希算法。在swift中使用SHA1加密函数需要 import CommonCrypto。

对于长度小于2的64次方位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用于检验消息的完整性。

SHA1的特性是,并不能从消息摘要中复原信息,并且不同的信息会有不同的消息摘要。

代码:

extension Int {
    //将整型数据转换为字符类型,且是16进制的
    func hexedString()->String {
        return NSString(format: "%02x", self) as String
    }
}

extension NSData {
    //将2进制的数据转换为字符类型,且为16进制
    func hexedString()->String {
        var string = String()
        //bytes是NSData对象属性,此处将UnsafeRawPointer类型的bytes转换为UnsafePointer类型
        let unsafePointer = bytes.assumingMemoryBound(to: UInt8.self)
        for i in UnsafeBufferPointer<UInt8>(start: unsafePointer, count: length) {
            string += Int(i).hexedString()
        }
        return string
    }
    //对数据进行SHA1加密的拓展方法
    func SHA1()->NSData{
    	//创建一个长度为CC_SHA1_DIGEST_LENGTH的一个可变数据对象。
        let result = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH))!
        let unsafePoint = result.mutableBytes.assumingMemoryBound(to: UInt8.self)
        //CC_SHA1加密方法,
        CC_SHA1(bytes, CC_LONG(length), UnsafeMutablePointer<UInt8>(unsafePoint))
        return NSData(data: result as Data)
    }
}

extension String{
	//对字符串进行加密的拓展方法
    func SHA1()->String{
    	//使用utf-8的编码格式将字符串转化为NSData对象。
        let data = (self as NSString).data(using: String.Encoding.utf8.rawValue)! as NSData
        //再调用自己刚拓展的加密方法。
        return data.SHA1().hexedString()
    }
}

调用:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let string = "乐事派"
    print("加密结果:"+string.SHA1())
    print("密文长度:\(string.SHA1().lengthOfBytes(using: String.Encoding.utf8))")
}

运行结果:

加密结果:a7482271d4b63e594c75cbf739c76c6c35016a0d
密文长度:40
MD5 加密

MD5的全称是Message Digest Algorithm 5,也就是消息摘要算法第五版。是一种散列函数,以提供消息的完整性保护。
MD5可以将任意长度的字符串变成一个长度为128bit的大整数,并且是一个不可逆的字符串变换算法。也就是说无法回溯为原来的具体信息。
MD5经常被用在验证一段数据是否被篡改。
代码:基本与SHA1的加密过程类似。

extension Int {
    //将整型数据转换为字符类型,且是16进制的
    func hexedString()->String {
        return NSString(format: "%02x", self) as String
    }
}

extension NSData {
    //将2进制的数据转换为字符类型,且为16进制
    func hexedString()->String {
        var string = String()
        //bytes是NSData对象属性
        let unsafePointer = bytes.assumingMemoryBound(to: UInt8.self)
        for i in UnsafeBufferPointer<UInt8>(start: unsafePointer, count: length) {
            string += Int(i).hexedString()
        }
        return string
    }
    
    func MD5()->NSData{
        let result = NSMutableData(length: Int(CC_MD5_DIGEST_LENGTH))!
        let unsafePoint = result.mutableBytes.assumingMemoryBound(to: UInt8.self)
        CC_MD5(bytes, CC_LONG(length), UnsafeMutablePointer<UInt8>(unsafePoint))
        return NSData(data: result as Data)
    }
}

extension String{
    func MD5()->String{
        let data = (self as NSString).data(using: String.Encoding.utf8.rawValue)! as NSData
        return data.MD5().hexedString()
    }
}

验证代码:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let string = "乐事派"
    print("加密结果:"+string.MD5())
    print("密文长度:\(string.MD5().lengthOfBytes(using: String.Encoding.utf8))")
}

运行结果:

加密结果:2f0ef9512a21f5739d58680e4932c6ac
密文长度:32
越狱情况的判定:

数据在传输过程中安全很重要,但数据在本地存储上的安全同样也很重要,虽然苹果系统是封闭的,但是也仍有可破解的方法,这个方式就是越狱苹果手机。

越狱后的苹果手机由于没有了沙盒的保护,黑客可以对其中的内容进行修改,因此在内购支付的过程中,苹果返回的已付款凭证就有可能是伪造的。因此我们在这方面还需要做一下苹果手机是否已经越狱的判断。

苹果设备的越狱是通过Cydia来实现的。因此可以通过判断是否有相关的文件夹或程序来判断是否处于越狱。

通常越狱的文件、软件特征有以下:

Cydia.applimera 1 n.appgreenpois0n.appblacksn0n.appredsn0w.appAbsinthe.app

代码:

func isBroken()->Bool{
    let apps = ["Applications/Cydia.app",
                "Applications/limera 1 n.app",
                "Applications/greenpois0n.app",
                "Applications/blacksn0n.app",
                "Applications/redsn0w.app",
                "Applications/Absinthe.app",]
    for appPath in apps {
        if (FileManager.default.fileExists(atPath: appPath)) {
            return true
        }
    }
}

验证:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if isBroken() {
        print("您的设备已经过越狱,请留意支付安全")
    }else{
        //没有越狱则无需提醒用户
        print("无越狱")
    }
}

运行结果:

无越狱

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/996671.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存