DES(Data EncryptionStandard)又叫数据加密标准,是1973年5月15日美国国家标准局(现在是美国标准技术研究所,即NIST)在联邦记录中公开征集密码体制时出现的。DES加密算法是一种对称加密技术,该算法的要求主要为以下四点:
- 提供高质量的数据保护,防止数据未经授权的泄露和未被察觉的修改;
- 具有较高的复杂性,使得破译的开销超过可能获得的利益,同时又要便于理解和掌握;
- 密码体制的安全性应该不依赖于算法的保密,其安全性仅以加密密钥的保密为基础;
- 实现经济,运行有效,并且适用于多种完全不同的应用。
而3DES(或称为TripleDES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)的通称。它相当于是对每个数据块应用三次DES加密算法。由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解;3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
创建项目并建立桥接文件
接着我们来创建一个示例项目,对一个字符串进行3DES加密和解密。首先创建一个基于[Single View Applicaton]模板的空白项目,然后在左侧的项目名称文件夹上点击鼠标右键,并选择右键菜单中的[New File...]选项,创建一个桥接头文件,这是因为我们需要使用到Object-C的CommonCrypto。
在弹出的模板选择窗口中,依次选择[Source > Header File]选项,以创建一个.h的头文件,如图1所示:
图 1
桥接头文件创建完成后,打开并编辑该文件,以引入相关的头文件,如图2所示:
图 2
接着还需要在Build Settings面板中设置Objective-C Bridging Header,在进入Build Settings面板之后,在搜索输入框内输入objective-c bridging进行搜索,以定位Objective-C Bridging Header选项,并设置该选项的值为桥接文件所在的位置,如图3所示:
图 3
实现3DES加解密功能
完成项目的配置之后,在左侧的项目导航区打开并编辑[ViewController.swift]文件,开始3DES加解密功能的实现。在该文件中首先创建一个集合,用来生成随机的公用key值:
private let randomStringArray: [Character] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".characters.map({$0})
var key:String = ""
然后添加一个名为randomStringOfLength方法,用来获得指定长度的随机字符串,该字符串从randomStringArray集合中获得英文大小写字母和0至9之间的数字,然后由这些字符通过一个for–in 循环组成指定长度的随机字符串。其中uniform方法可以用来产生0~(n-1)范围内的随机数,不需要再进行取模运算。如果要生成1至n的随机数,可以这么写:arc4random_uniform(n)+1:
func randomStringOfLength(_ length:Int) -> String { var string = "" for _ in (1...length) { string.append(randomStringArray[Int(arc4random_uniform( UInt32(randomStringArray.count) - 1))]) } return string }
然后添加一个名为encrypt方法,对字符串参数encryptData进行加密:
func encrypt(encryptData:String){ key = randomStringOfLength(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(kCCAlgorithm3DES),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("加密过程出错: \(cryptStatus)") } }
CCCrypt参数的配置
在该方法中的第2行至14行的代码,都是用来生成CCCrypt方法中的各种参数的值。
首先在第2行的代码中,通过randomStringOfLength方法,生成一个随机字符串作为3DES加解密的key值。其中kCCKeySize3DES是指Triple DES加解密的key的大小,其值为24,因此这里将生成一个长度为24的包含英文大小写字母和数字的随机字符串。
接着在第3行的代码中,将待加密的字符串转换为Data类型。
在第5行的代码中,将作为随机字符串的key值同样转换为Data类型。
在第6行的代码中,创建一个UnsafeMutableRawPointer指针。在 Swift 中,指针都使用一个特殊的类型来表示,那就是UnsafeRawPointer。对应地它还有一个可变变体, UnsafeMutableRawPointer 。在创建该指针时,向系统申请了个数为(keyData as NSData).bytes的UInt8泛型类型的内存。
在第7行的代码中,创建一个名为keyLength的常量,表示key值的长度。根据kCCKeySize3DES的大小可以得知,keyLenght的值为24。
然后在第9行的代码中,获得待加密的Data类型对象的长度。并在第20行的代码中,创建一个UnsafePointer指针,并从系统中分配相应的内存作为加密的input buffer。
在第11行的代码中,创建一个指定长度的NSMutableData可变的二进制数据对象,该对象将作为加密的outputbuffer,用来存储加密后的数据。其长度为input buffer的长度和3DES加密的kCCBlockSize3DES块大小之和,kCCBlockSize3DES块的大小为8。
接着在第12行的代码中,创建一个UnsafeMutablePointer类型的指针,并根据output buffer即bufferData的大小分配相应的内存。
然后在第13行的代码中,获得outputbuffer的长度。最后在第14行的代码中,创建一个变量bytesDecrypted,用来存储加密后的output buffer的最终字节数。
执行CCCrypt方法
完成所有的参数设置之后,在第16至27行的代码中,调用CommonCryptor.h中的CCCrypt方法对数据进行加密操作。
其中CCCrypt方法的第一个参数表示进行加密操作,还是进行解密操作,这里使用kCCEncrypt表示进行加密操作。
第二个参数表示进行加密的算法,在CommonCryptor.h中提供了kCCAlgorithmAES128、kCCAlgorithmAES、kCCAlgorithmDES、kCCAlgorithm3DES、kCCAlgorithmCAST、kCCAlgorithmRC4、kCCAlgorithmRC2、kCCAlgorithmBlowfish等多种类型的加密算法。
第三个参数用来设置block ciphers(block cipher表示在使用密钥和算法对文本进行加密时的方法)的选项,该选项可以是kCCOptionPKCS7Padding或kCCOptionECBMode两者中的任一个。假如使用PKCS7Padding,它的密钥可以是8个字节,也可以不是。
CCCrypt方法的其它几个参数就是我们在第2行至14行号配置好的参数。
判断CCCrypt方法执行的结果
当执行完CCCrypt方法后,会返回一个cryptStatus状态,通过cryptStatus判断是否加密成功。其中kCCSuccess表示加密成功,除此之外还有其它几种状态,如表1所示:
表1 CCCrypt结果状态列表 |
|
状态值 |
说明 |
kCCSuccess |
加解密操作正常结束。 |
kCCParamError |
非法的参数值。 |
kCCBufferTooSmall |
选项设置的缓存不够大。 |
kCCMemoryFailure |
内存分配失败。 |
kCCAlignmentError |
输入大小匹配不正确。 |
kCCDecodeError |
输入数据没有正确解码或解密。 |
kCCUnimplemented |
函数没有正确执行当前的算法。 |
当判断加密操作正确完成后,设置output data的length为bytesDecrypted,以调整输出buffer的大小为最终输出加密数据的尺寸。
对密文进行解密操作
至此就完成了数据的加密操作,接着使用相同的密钥对密文进行解密操作。该功能通过调用decrypt方法来实现:
func decrypt(inputData : Data){ 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(kCCDecrypt),&bytesDecrypted) if Int32(cryptStatus) == Int32(kCCSuccess) { bufferData.length = bytesDecrypted let clearDataAsString = NSString(data: bufferData as Data,encoding: String.Encoding.utf8.rawValue) print("解密后的内容:\(clearDataAsString as! String)") } else { print("解密过程出错: \(cryptStatus)") } }
解密的方法与加密的方法大体相似,需要注意不同的地方是,在第13行使用kCCDecrypt进行解密运算。然后在72行的代码中,将解密后的数据转换为NSString对象,并通过print语句在日志区进行打印输出。
修改viewDidLoad方法
最后一步是修改ViewController类的viewDidLoad方法,在该方法中调用加密方法encrypt,对明文coolketang.com进行加密操作:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view,typically from a nib. encrypt(encryptData: "coolketang.com"); }
现在已经完成了所有的加解密的编码工作,点击Xcode界面左上角的[编译并运行]按钮 ,打开模拟器运行项目。项目运行后,将弹出一个空白的模拟器,并在日志区输出解密后的结果,如图4所示:
图 4
新 作
iOS开发中的神兵利器
共140节课程,讲解GitHub中近百个过千star的iOS热门开源项目
市面上唯一大规模讲解GitHub中热门的iOS开源项目的教程
快速、优雅地解决iOS开发工作中遇到的各种棘手问题
视频观看地址:http://study.163.com/course/courseMain.htm?courseId=1003657013