使用Swift 3.0实现原生的3DES加密和解密

来源:互联网 发布:lol比赛视频软件 编辑:程序博客网 时间:2024/05/24 02:19

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


接着给ViewController类添加一个名为key的字符串属性,作为3DES加密的公钥和私钥,并对其进行初始化操作:

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),        UInt32(kCCAlgorithm3DES),         UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding),         keyBytes,         keyLength,        nil,         dataBytes,         dataLength,        bufferPointer,        bufferLength,         &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



p.p1 {margin: 0.0px 0.0px 2.0px 0.0px; font: 14.0px '.PingFang SC'; color: #454545}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; color: #e4af0a}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px '.PingFang SC'; color: #454545}span.s1 {font: 14.0px 'Helvetica Neue'}span.s2 {font: 12.0px '.PingFang SC'; color: #454545}span.s3 {font: 12.0px 'Helvetica Neue'; text-decoration: underline ; color: #e4af0a}span.s4 {text-decoration: underline ; color: #e4af0a}span.s5 {font: 12.0px 'Helvetica Neue'}table{width:100%}table td{padding-left:10px;}



新 作

iOS开发中的神兵利器

共140节课程,讲解GitHub中近百个过千star的iOS热门开源项目
市面上唯一大规模讲解GitHub中热门的iOS开源项目的教程
快速、优雅地解决iOS开发工作中遇到的各种棘手问题

视频观看地址:http://study.163.com/course/courseMain.htm?courseId=1003657013

互动教程下载地址:https://itunes.apple.com/cn/app/id1209739676





1 0
原创粉丝点击