iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 25
0
Security

看完眼眶濕濕的App開發者慘烈對抗險惡資安環境血與淚的控訴!系列 第 25

Day 25. 非對稱式加密演算法 - RSA (實戰篇)

大家好,我是羊小咩

前面介紹過 RSA 的觀念篇,今天來介紹 RSA 的使用,以及容易踩地雷陷阱

金鑰跟填充模式造成的問題,我用比較簡單扼要的方式講解,如果要細細說明,又要寫的漏漏長的一篇

有機會再來細談 RSA 金鑰格式跟Padding ,有興趣的可以看Wiki - PKCS

iOS / Swift

在 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-----

載入已有金鑰

With a PEM string
let publicKey = try PublicKey(pemEncoded: str)
let privateKey = try PrivateKey(pemEncoded: str)
With a Base64 string
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)

NodeJS / ES6

使用 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)

載入已有金鑰

With a PEM string

// 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格式輸入,即便輸入格式不同,系統可以自動轉換

可以觀察金鑰格式抬頭,確認使用的格式是哪種

pkcs1

-----BEGIN RSA PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----

pkcs8

-----BEGIN PUBLIC KEY-----
-----BEGIN PRIVATE KEY-----

openssh

-----BEGIN OPENSSH PRIVATE KEY-----

填充方式(padding)不同

剛說完金鑰的填充方式,同樣加解密也會有填充方式的問題,要確認雙方使用的填充方式相同

以上面範例 IOS 跟 NodeJS 都是使用 pkcs1進行 padding

注意:iOS 只能使用 PKCS1 padding

原始資料轉換錯誤

最常見的一種是base64 在傳送時,.net 自動把 + 當成空白,造成雙方一開始資料就不一樣

當然加解密結果也不同。

這個有兩種解決方案

  1. 自行處理.net URLEncode 轉換問題
  2. 資料改使用 HEX 傳送

還有一種問題也算是 URLEncode造成,開發人員錯誤URLEncode跟Decode 處理也會造成資料不一至問題

參考資料

https://www.npmjs.com/package/node-forge

https://github.com/TakeScoop/SwiftyRSA

https://en.wikipedia.org/wiki/PKCS


上一篇
Day 24. 非對稱式加密演算法 - 橢圓曲線密碼學 Elliptic Curve Cryptography , ECC (觀念篇)
下一篇
Day 26. 非對稱式加密演算法 - 橢圓曲線密碼 ECC (實戰篇)
系列文
看完眼眶濕濕的App開發者慘烈對抗險惡資安環境血與淚的控訴!31

尚未有邦友留言

立即登入留言