RSA算法是一種非對稱加密算法,要了解RSA算法,首先要知道什麼是對稱加密算法,什麼是非對稱加密算法。
1,對稱加密算法
密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密。
特點:算法公開、計算量小、加密速度快、加密效率高特點。但交易雙方都使用同樣鑰匙,安全性得不到保證。
具體算法有:DES算法,3DES算法,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;
(4)由於RSA的安全性依賴於大數分解,為保證安全,目前來說RSA密鑰(即n)長度至少要為1024位。
(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引入(頭文件記得配置)
#import <CommonCrypto/CommonCrypto.h>
(2)把Heimdall.swift添加到項目裡,如果有import CommonCrypto,將其去掉
//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類型)
//公鑰標簽
let publicKeyTag = "com.111cn.public"
//私鑰標簽
let privateKeyTag = "com.111cn.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.111cn.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進行數字簽名和驗證
(1)服務端給消息簽名(使用的是私鑰)。把消息連同簽名發給客戶端
//公鑰標簽
let publicKeyTag = "com.111cn.public"
//私鑰標簽
let privateKeyTag = "com.111cn.private"
print("****** 服務器端把消息進行簽名 ******")
let localHeimdall = Heimdall(publicTag: publicKeyTag, privateTag: privateKeyTag,
keySize: 1024)
var publicKeyString:String = ""
let message = "歡迎訪問www.111cn.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)整個流程控制台輸出如下: