Swift - 使用RSA算法进行数据加密,解密以及数字签名
作者:hangge | 2015-10-18 10:13
RSA算法是一种非对称加密算法,要了解RSA算法,首先要知道什么是对称加密算法,什么是非对称加密算法。
1,对称加密算法
密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密。
特点:算法公开、计算量小、加密速度快、加密效率高特点。但交易双方都使用同样钥匙,安全性得不到保证。
具体算法有:DES算法,3DES算法,AES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法。
2,非对称加密算法
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。
特点:由于有两种密钥,其中一个是公开的,这样就消除了最终用户交换密钥的需要,更安全。但由于算法复杂,加密解密速度没有对称加密解密的速度快。
主要算法有:RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)。
3,RSA算法
(1)RSA算法是第一个能同时用于加密和数字签名的算法。
(2)RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
(3)RSA的算法涉及三个参数,n、e1、e2。
(4)由于RSA的安全性依赖于大数分解,为保证安全,目前来说RSA密钥(即n)长度至少要为1024位。
(5)由于RSA算法进行的都是大数计算,使得RSA最快的情况也比DES慢上好几倍。
4,RSA加密内容长度限制的问题
通常1024位key的最多只能加密127位数据,要解决这个问题有两种办法:
(1)自己把数据变成N个117字节的数据段来分别加密,解密也需要自己一个个解密再完成字节拼装
(2)加密的时候:使用对称加密(AES/DES etc)加密数据,然后用RSA公钥加密对称加密的密钥。
解密的时候:用RSA的私钥解密得到对称加密的密钥,然后完成反向操作得到明文。 (推荐这种方式)
7,Heimdall的配置
1,对称加密算法
密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密。
特点:算法公开、计算量小、加密速度快、加密效率高特点。但交易双方都使用同样钥匙,安全性得不到保证。
具体算法有:DES算法,3DES算法,AES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法。
2,非对称加密算法
非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。
特点:由于有两种密钥,其中一个是公开的,这样就消除了最终用户交换密钥的需要,更安全。但由于算法复杂,加密解密速度没有对称加密解密的速度快。
主要算法有:RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)。
3,RSA算法
(1)RSA算法是第一个能同时用于加密和数字签名的算法。
(2)RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
(3)RSA的算法涉及三个参数,n、e1、e2。
其中,n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。
e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质;再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。
(n,e1),(n,e2)就是密钥对。其中(n,e1)为公钥,(n,e2)为私钥。
RSA加解密的算法完全相同,设A为明文,B为密文,则:A=B^e2 mod n;B=A^e1 mod n;(公钥加密体制中,一般用公钥加密,私钥解密)
e1和e2可以互换使用,即:
A=B^e1 mod n;B=A^e2 mod n;
(5)由于RSA算法进行的都是大数计算,使得RSA最快的情况也比DES慢上好几倍。
4,RSA加密内容长度限制的问题
通常1024位key的最多只能加密127位数据,要解决这个问题有两种办法:
(1)自己把数据变成N个117字节的数据段来分别加密,解密也需要自己一个个解密再完成字节拼装
(2)加密的时候:使用对称加密(AES/DES etc)加密数据,然后用RSA公钥加密对称加密的密钥。
解密的时候:用RSA的私钥解密得到对称加密的密钥,然后完成反向操作得到明文。 (推荐这种方式)
5,RSA的使用场合
1,用于加密数据:比如我是公司领导,我希望员工A,B,C给我发消息的时候能够加密。那么我这边可以生成一对密钥,把公钥给ABC。A把消息使用公钥加密后发给我,我收到后使用私钥即可解密。这过程中如果加密消息被BC拦截了,虽然他们有一样的公钥,但是无法解密的。
2,用于数字签名:比如我要给A,B,C发送一段数据,但为了保证这个是我发送的,而不是别人伪造的,那么我可以附上我的数字签名。做法是对数据进行MD5之类的运算以取得数据的"指纹",再对"指纹"进行加密,加密将使用密钥对中的不公开的私钥。A,B,C收到数据后,用同样的运算获得数据指纹,再用公钥对加密指纹进行解密,比较解密结果与他自己计算出来的指纹是否一致,即可确定数据是否的确是我发送的、以及在传输过程中是否被篡改。
6,好用的RSA封装库 - Heimdall
Heimdall是一个Swift写的RSA封装类,使用它我们可以很方便进行数据加密,解密,数字签名,签名验证等操作。
地址是:GitHub/Heimdall
7,Heimdall的配置
(1)由于Heimdall使用到了CommonCrypto,所以首先要建立桥接头文件bridge.h并把CommonCrypto引入(头文件记得配置)
(2)把Heimdall.swift添加到项目里,如果有import CommonCrypto,将其去掉
加密时:先生成一个随机的AES密钥,用这个密钥以AES方式加密数据。再用RSA的公钥加密AES密钥。整个加密生成的数据实际上包含两部分:前半部分是用RSA的公钥加密后的AES密钥,后半部分是使用AES加密的原始数据。
解密时:先使用RSA私钥把前面部分的AES密钥给解出来,再用这个AES密钥把后半部分的真实数据给解出来。
(1)服务端这边定义好publicTag和privateTag,这样就可以生成一对密钥。
#import <CommonCrypto/CommonCrypto.h>
//import CommonCrypto
8,使用Heimdall进行数据加密,解密
由于RSA对加密数据长度有限制,所以Heimdall实际上是混合了AES和RSA这两种加密方式。加密时:先生成一个随机的AES密钥,用这个密钥以AES方式加密数据。再用RSA的公钥加密AES密钥。整个加密生成的数据实际上包含两部分:前半部分是用RSA的公钥加密后的AES密钥,后半部分是使用AES加密的原始数据。
解密时:先使用RSA私钥把前面部分的AES密钥给解出来,再用这个AES密钥把后半部分的真实数据给解出来。
(1)服务端这边定义好publicTag和privateTag,这样就可以生成一对密钥。
为便于传输,我们把生成的publicKeyData用Base64编码成publicKeyString(String类型)
9,使用Heimdall进行数字签名和验证
(1)服务端给消息签名(使用的是私钥)。把消息连同签名发给客户端
//公钥标签 let publicKeyTag = "com.hangge.public" //私钥标签 let privateKeyTag = "com.hangge.private" print("****** 服务器端开始生成一对密钥(公钥和私钥)******") let localHeimdall = Heimdall(publicTag: publicKeyTag, privateTag: privateKeyTag, keySize: 1024) var publicKeyString:String = "" if let heimdall = localHeimdall, publicKeyData = heimdall.publicKeyDataX509() { publicKeyString = publicKeyData.base64EncodedStringWithOptions( NSDataBase64EncodingOptions(rawValue: 0)) // 把/和+替换成下滑线,中划线是为了URL安全, // 接收方那边要记得转回来 publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("/", withString: "_") publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("+", withString: "-") print("\n--- publicKeyString是:\n\(publicKeyString)") } print("\n--- 把公钥传给客户端(publicKeyTag和publicKeyString)\n\n")(2)把publicTag和publicKeyString作为公钥分发给客户端,客户端使用公钥加密数据
print("****** 客户端使用公钥加密数据 ******") //先把特殊字符转回来 publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("_", withString: "/") publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("-", withString: "+") let keyData = NSData(base64EncodedString: publicKeyString , options: NSDataBase64DecodingOptions(rawValue: 0)) var encryptedMessage:String = "" // 创建公钥 if let partnerHeimdall = Heimdall(publicTag: publicKeyTag, publicKeyData: keyData) { // 使用公钥加密需要传输的数据 let message = "我访问过www.hangge.com" encryptedMessage = partnerHeimdall.encrypt(message)! print("\n--- 加密后的数据是是:\n\(encryptedMessage)") //print(partnerHeimdall.decrypt(encryptedMessage)) //使用公钥无法解密 } print("\n--- 把加密后的数据传给服务端\n\n")(3)服务端收到数据后使用密钥解密数据
print("****** 服务器端收到加密数据 ******") if let localHeimdall2 = Heimdall(publicTag: publicKeyTag, privateTag: privateKeyTag, keySize: 1024) { //开始解密 if let decryptedMessage = localHeimdall2.decrypt(encryptedMessage) { print("\n--- 解密后的数据是是:\n\(decryptedMessage)") } }(4)整个流程控制台输出如下:
9,使用Heimdall进行数字签名和验证
//公钥标签 let publicKeyTag = "com.hangge.public" //私钥标签 let privateKeyTag = "com.hangge.private" print("****** 服务器端把消息进行签名 ******") let localHeimdall = Heimdall(publicTag: publicKeyTag, privateTag: privateKeyTag, keySize: 1024) var publicKeyString:String = "" let message = "欢迎访问www.hangge.com" //消息 var signString:String = "" //签名 if let heimdall = localHeimdall, publicKeyData = heimdall.publicKeyDataX509() { publicKeyString = publicKeyData.base64EncodedStringWithOptions( NSDataBase64EncodingOptions(rawValue: 0)) // 把/和+替换成下滑线,中划线是为了URL安全, // 接收方那边要记得转回来 publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("/", withString: "_") publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("+", withString: "-") signString = localHeimdall!.sign(message)! print("\n--- 消息是:\n\(message)") print("\n--- 签名是:\n\(signString)") } print("\n--- 把消息和签名都传给客户端\n\n")(2)客户端收到后用公钥进行验证,确认是不是服务端发送的
print("****** 客户端验证消息和签名 ******") //先把特殊字符转回来 publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("_", withString: "/") publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("-", withString: "+") let keyData = NSData(base64EncodedString: publicKeyString , options: NSDataBase64DecodingOptions(rawValue: 0)) // 创建公钥 if let partnerHeimdall = Heimdall(publicTag: publicKeyTag, publicKeyData: keyData) { //进行验证 let result = partnerHeimdall.verify(message, signatureBase64: signString) print("\n--- 验证结果是:\n\(result)") }(3)整个流程控制台输出如下:
全部评论(2)
java服务端的什么字段是publicTag?
站长回复:Java服务端我还没试过,暂时帮不了你了。
航哥,这个Java服务端怎么加密解密呀,有什么demo可以参考吗
站长回复:目前手头没有相关的java代码,这个帮不上你了。