iOS-在專案中引入RSA演算法

Doliant發表於2019-05-02

封面圖

1.前言

契機是公司換了一套新介面,要求進行全報文加密。以前公司專案基本上都使用的對稱加密的模式3DESAES,由於對稱加密的金鑰只有一對,有很大的金鑰洩露風險。身處金融這個極為敏感的行業,對安全的要求也是極高。趁著這個機會,把專案中的加密模式統一替換成RSA非對稱加密。

2.關於加密演算法

本篇不會對RSA加密演算法原理進行詳細的解釋。在網際網路異常發達的今天,RSA演算法詳細的資料很容易就能獲取到。安全領域也是一個能夠深挖的領域,本篇文章偏向工程向,僅對一些基本基本概念進行簡單的解釋。

  • 對稱加密和非對稱加密

  1. 對稱加密 :加密和解密用的是同一套金鑰,缺陷是金鑰管理存在風險。常用的加密方式有:DES3DESAES等。

  2. 非對稱加密 :加密和解密用的不同的金鑰,公鑰加密私鑰解密。常用的加密方式有RSA

  • RSA常見用法

  1. 公鑰加密,私鑰解密;

  2. 私鑰簽名;

  3. 公鑰驗籤。

3.實踐

1、生成金鑰:

使用終端openssl命令生成金鑰

1).生成私鑰,金鑰長度為2048bit,base64編碼。

openssl genrsa -out rsa_private_key.pem 2048
複製程式碼

關於金鑰長度,這裡要進行一下特別說明。每次加密的資料不能超出金鑰的長度,2048bit長度的金鑰,只能單次只能加密(2048 / 8 - 11) = 245byte長度的資料。(那11byte是RSA預留的長度)。若待加密的資料長度超過了245byte,就需要對資料進行分段加密。

2).根據之前生成的私鑰生成公鑰:

openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
複製程式碼

3).轉換公鑰為PKCS8格式,這種格式可以直接在iOS專案中使用:

openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt
複製程式碼

我司的情況是由公司後臺生成私鑰和公鑰,提供.pem格式的公鑰給移動端,因此我直接使用的是pkcs8格式的公鑰。

注意:不建議將私鑰儲存在客戶端,私鑰洩露後果會很嚴重!

2、在專案中使用:

Demo地址:github.com/Hstripe/RHR…

以上是我使用的RSA工具類,支援RSA在移動端的各種日常用法:加密、解密、加簽、驗籤。跟網上很多工具類不同的是不需要匯入p12der格式的金鑰檔案,支援字串形式的金鑰檔案非常方便。而且使用的是Apple自家的Security.framework。網上很多例程都是使用的openSSL那一套加密工具類,實現也很方便就是包體積略微有點大。

若使用IDEXcode8及以上,請在Capabilities中將KeyChain sharing設定為YES

1.)公鑰加密

NSString *string = @"doRSAEncrypt";

NSString *publickey = @"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61sODmFj/OXnrHUYzams\
 c/6XLni9G0HYv9sBewaPjF6qlu845nwmYSA6dQ9zPk231o5l3tmHLpUQGNnp/5rH\
 +84iB/tM+Y+2kTI8uILGbmby2DL3rgzBG+I9h7e3w3QktpdcD8Z+ZuEVa/CY3Xez\
 8X1uknEVzIIhDKY7ipAoebchVdELbTlH1BRLz8RH6mQ+Z8REH4UL0TiQLfSfTotv\
 1G5ZerNxVZ7Toi4K9KFDA+1UD+LeDGg8PY/sdg0AJpR4o6bfDBko50wKLDz4UYyp\
 7EFZv661o2Mr7+KoQ6Tpb7w8bTl7wrRKz9ugB5+tM2F7aDvv1mzr7STIF+2c7tEx\
 DQIDAQAB";

NSString *encryptString = [IPNRSAUtil encryptString:string publicKey:publickey];
複製程式碼

2.)私鑰解密

NSString *privatekey = @"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDrWw4OYWP85ees\
 dRjNqaxz/pcueL0bQdi/2wF7Bo+MXqqW7zjmfCZhIDp1D3M+TbfWjmXe2YculRAY\
 2en/msf7ziIH+0z5j7aRMjy4gsZuZvLYMveuDMEb4j2Ht7fDdCS2l1wPxn5m4RVr\
 8Jjdd7PxfW6ScRXMgiEMpjuKkCh5tyFV0QttOUfUFEvPxEfqZD5nxEQfhQvROJAt\
 9J9Oi2/Ubll6s3FVntOiLgr0oUMD7VQP4t4MaDw9j+x2DQAmlHijpt8MGSjnTAos\
 PPhRjKnsQVm/rrWjYyvv4qhDpOlvvDxtOXvCtErP26AHn60zYXtoO+/WbOvtJMgX\
 7Zzu0TENAgMBAAECggEBAOMf6w+ror9y6sE9+6K1hEwoO6NIN06vm8mCQwqDiVIw\
 JTYlQ+cBllQSsvc24sMUYz32C48ko1Ur2u3wleXqa+Wvxp2nQWBw9QFn1rtE0NPI\
 G8DSZr0bZ9xN1406mWdQlQF0Tg6XQnJr8q1I8WyAUTHSFzvRT/Uc+2Hmpf0RI05Y\
 t0dt5bsGn/g+ijGbCm63Z2U8u5yWXidxWfU/KyYf1Y3mw9lGLR6IJc/q9N+TO4ih\
 JM5pCraMFI4zWblGobkN2WKy7MrQ45FLSKul4W00+VyM/rVivW/fYUaqFEnlBog3\
 /4hgI6Bsw2IuSk2Ubhbc4fp//146vJf6oL4WAJHmAiECgYEA+e3AFph8joC5gC1Q\
 ok97tLJqt95fZCx4VCw6lPPbWxOHG6TJlvlo7kZIeUfKrGIlOWn38yuw5thEZKwW\
 bzE8kn5WGlUgkOQ7hJ6Iiw/TzCFPRHxV63WBKa8OnyFIn3w91zI8ZTcUgrgyns+F\
 gE5uxkEjb6iIyxxnpqC7Fk3lnikCgYEA8RKtn7kqoe0T/Yv7UsPLm7KzuWn3/01r\
 LGA+x+GCp4rP2Lf0u1K+7VY6Dv/ceTBuA/2Yujenkjt0LaF+Bz0tLWFB5BTw3n+u\
 6QshVdP3O1im4w6p3e8O9mfBCSV/CX5oBkbamemyQ7DTB9VtYNNmtGTs2aySuoel\
 zPU0czETEEUCgYAjVhwclb62nzibCM0nxbkl2TwBdy1hinAQ5pf5y2iuPdqSbAAc\
 mnLdjY5dp2reaJn+vh7SgNDoMpeo7DPX0MxRog8mdfa+xaYsoAWKM9isOeFtO28i\
 dWCnthqJITmVYwmTTYUAgoMh4E036vtjIrPC0B7kgJ2mqgN1qbAJ/UWD0QKBgFSO\
 U53hacWwDUHydm2aRXFQJd/T/mtq8Tt4aqzbOWOgubRvGYUWyecfRm/6aI+NYBlA\
 OvCeEsWk2uQib70ERTNUmLLycWXpbSVKhR/AoEgNmUOs4gH5FstwqvGVWFCxKLWC\
 5qvzn1ZE0FBAGQRMQgrmF3lmIXURnSMdoo8A2IntAoGAVCFmPpXvI8rMk2N3CvQ4\
 dkDfP3W6w1KpyMzuQRZE9N1IUBYh3KN25HfzeW1OIFHPuxInMm/6zaU/rUHJSy/b\
 ynVdQ6jvM4ZIt3rYUXZN6+a14AeA/MNNrY2LzCYlCxWIbVyNj9UN8/uda0zEtZ73\
 RWYX1BlKVMSIx5Bf7eNH4fI=";

NSString *decryptString = [IPNRSAUtil decryptString:encryptString privateKey:privatekey];
複製程式碼

3.)私鑰加簽

 NSString *signature = [IPNRSAUtil rsaSignString:string WithPrivateKey:privatekey];
複製程式碼

4.)公鑰驗籤

BOOL result = [IPNRSAUtil rsaVerifySignature:signature plainString:string WithPublicKey:publickey];
複製程式碼

5.)分段加密

若待加密的資料長度大於金鑰長度,就需要對待加密的資料進行分段加密

// n為金鑰單次加密長度,這裡使用的是2048位的金鑰,因此n的值為245(2048/8 - 11)。
NSInteger n = 245;
NSMutableData *preData = [[NSMutableData alloc] init];
for (NSInteger i=0; i<=ceilf(string.length / n); i++) {
    NSString *subString = [string substringWithRange:NSMakeRange(i * n, MIN(n, string.length - i * 245))];
    NSData *encryptData = [IPNRSAUtil encryptData:[subString dataUsingEncoding:NSUTF8StringEncoding] publicKey:publickey];
// 分段加密需要拼接加密後的data資料,不要將data轉換成字串再拼接,這樣會導致結果錯誤。
    [preData appendData:encryptData];
}

NSData *finalData = [[NSData alloc] initWithData:preData];
finalData = [finalData base64EncodedDataWithOptions:0];
NSString *result = [[NSString alloc] initWithData:finalData encoding:NSUTF8StringEncoding];
複製程式碼

4.後記

為了搞這個工具類前前後後花了也有半個多月的時間了,看了很多文件和例程加上自己的實踐除錯才有了這個工具類。一開始使用的是openSSL那一套加密工具實現的,但是覺得openSSL佔用的空間略大,還是用Security.framework來實現的。平時總是忙於業務需求的實現,忽視了客戶端方面的安全,網上對客戶端安全這一塊的資源也比較有限,但願我這篇文章能對後來人有所幫助吧。

5.參考資料

看的資料比較多就不一一列舉了,主要還是通過GoogleGitHubStackOverFlow簡書知乎等平臺來獲取相關資源的。最後,就在這裡統一感謝一下相關資源的貢獻者吧。

相關文章