大家好,我是羊小咩
前面介紹過 RSA 的觀念篇,今天來介紹 RSA 的使用,以及容易踩地雷陷阱
金鑰跟填充模式造成的問題,我用比較簡單扼要的方式講解,如果要細細說明,又要寫的漏漏長的一篇
有機會再來細談 RSA 金鑰格式跟Padding ,有興趣的可以看Wiki - PKCS
在 Swift 使用RSA 有個很棒的套件 SwiftyRSA
https://github.com/TakeScoop/SwiftyRSA
Cocoapods
pod 'SwiftyRSA'
Carthage
github "TakeScoop/SwiftyRSA"
let keyPair = try! SwiftyRSA.generateRSAKeyPair(sizeInBits: 1024)
let privateKey = keyPair.privateKey
let publicKey = keyPair.publicKey
print(try! privateKey.pemString())
print(try! publicKey.pemString())
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDfbg0VxZaetJAOMHQnECXTUSrsGW++UHa01OsJCmYJuSVSSlF0
j2LtlZ83PADeyq/wtjN1pdJG++SDUVbB0gH4lT+v3yXXQJabyaat4Eu3GwDJSx1s
X9U+hiDlPSxwNlZYASgPZnJYUXIn1rxCL4q+I9Tea9YjJEO5iLxIy9BArQIDAQAB
AoGAN5LbBFhSP4Vv82eP7ItyrR4aTAGDrtW6wP3YvDXu7Wo040QDALgNhkTaBlYR
bVkC7B+VqySkqe1LiHI53xnKTUBFvLw2x/gJ4pCilEY6IRN1B2ey0pD+7wLKmlV3
HjGkhGdPRLRtYIFMGDFXA2sdVcTcheQ7uDgoYvNEPB9iqU0CQQD8NiuLp8RX6cbO
EU4ZK+Ux9qIQ/9faJZ7FAa5q+QLR8tdiZIV4Qy3tt+bxIEX6LXcJWcoGtDAw+sOR
pppYrOOrAkEA4sk05KxKHP1C4AUM+IJAWewg1uwHMyKQytFVUXqvKqXGYe304hss
vBJtSXCg8n+MAw+J6oR87rieVtUdKDsVBwJBAPojJ5D8tMiUPO4HT7O7SzcAar/9
XjEm+o5dGoVRrjBXbrJRC+a4igaoS819uqljChomCS4Y62rIaco8t/uWjL0CQEUb
sV2Zzv/kgXOItnooHvoIBb3YsRz5BIx5m/k3XJevarO+8qfQRiKNrvX5N2KOUZlB
K/LmKrgIVF/7k2YGExMCQQDQBq4gJRP3xzwQYKhYjKUt/Q/bBsRkX09roPTh+/WX
4flxg1JLMZe7ZSeAscTw0MHwkyhRUUkCQ7LEjz10xtDs
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAN9uDRXFlp60kA4wdCcQJdNRKuwZb75QdrTU6wkKZgm5JVJKUXSPYu2V
nzc8AN7Kr/C2M3Wl0kb75INRVsHSAfiVP6/fJddAlpvJpq3gS7cbAMlLHWxf1T6G
IOU9LHA2VlgBKA9mclhRcifWvEIvir4j1N5r1iMkQ7mIvEjL0ECtAgMBAAE=
-----END RSA PUBLIC KEY-----
let publicKey = try PublicKey(pemEncoded: str)
let privateKey = try PrivateKey(pemEncoded: str)
let publicKey = try PublicKey(base64Encoded: base64String)
let privateKey = try PrivateKey(base64Encoded: base64String)
let plainText = "Clear Text"
let publicKey = try! PublicKey(pemEncoded:try! publicKey.pemString())
let clear = try! ClearMessage(string: plainText, using: .utf8)
let encrypted = try! clear.encrypted(with: publicKey, padding: .PKCS1)
let cipherText = encrypted.base64String
print(cipherText)
let encryptedDec = try! EncryptedMessage(base64Encoded: cipherText)
let clearDec = try! encryptedDec.decrypted(with: privateKey, padding: .PKCS1)
//let data = clearDec.data //二進位資料
//let base64String = clearDec.base64Encoded //base64
let orgistring = try! clearDec.string(encoding: .utf8)
print(orgistring)
使用 node-forge 套件
https://www.npmjs.com/package/node-forge
npm install node-forge
var rsa = forge.pki.rsa;
var pki = forge.pki;
// 使用 synchronously 產生 keypair
var keypair = rsa.generateKeyPair({bits: 1024, e: 0x10001});
// 轉換 Forge key to PEM-格式
var pem = pki.publicKeyToPem(keypair.publicKey);
var pem_pub = pki.privateKeyToPem(keypair.privateKey);
console.log(pem)
console.log(pem_pub)
// convert a PEM-formatted public key to a Forge public key
var publicKey = pki.publicKeyFromPem(pub_str);
var privateKey = pki.privateKeyFromPem(pri_str);
const text = 'Clear Text';
// 預設使用 RSAES PKCS#1 v1.5
var encrypted = publicKey.encrypt(text);
var cipherText = forge.util.encode64(encrypted)
//解密需要帶入 Bytes 資料,如果拿到的資料是 base64或 hex,需要先進行轉換
const cipherData = forge.util.decode64(text)
//const cipherData = forge.util.hexToBytes(hex);
var decrypted = privateKey.decrypt(cipherData);
console.log(decrypted)
以下最容易遇到的問題
可以先檢查雙方使用金鑰是否相同
iOS 匯出 base64 匯出是pkcs1 但是在.net 預設會當 pkcs8 匯入,這樣加解密資料就會不同
記得使用pem格式輸入,即便輸入格式不同,系統可以自動轉換
可以觀察金鑰格式抬頭,確認使用的格式是哪種
-----BEGIN RSA PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
-----BEGIN PRIVATE KEY-----
-----BEGIN OPENSSH PRIVATE KEY-----
剛說完金鑰的填充方式,同樣加解密也會有填充方式的問題,要確認雙方使用的填充方式相同
以上面範例 IOS 跟 NodeJS 都是使用 pkcs1
進行 padding
注意:iOS 只能使用 PKCS1 padding
最常見的一種是base64 在傳送時,.net 自動把 +
當成空白,造成雙方一開始資料就不一樣
當然加解密結果也不同。
這個有兩種解決方案
還有一種問題也算是 URLEncode造成,開發人員錯誤URLEncode跟Decode 處理也會造成資料不一至問題
https://www.npmjs.com/package/node-forge
https://github.com/TakeScoop/SwiftyRSA
https://en.wikipedia.org/wiki/PKCS