在正文開(kāi)始之前先吐個(gè)槽,看自己簡(jiǎn)書(shū)上一篇發(fā)文已經(jīng)是好幾個(gè)月之前的了。之前的設(shè)想是每周輸出一篇技術(shù)方面的文檔,接著就被硬生生拖成了一個(gè)月,然后就不知道幾個(gè)月去了......其間還是有有不少可以寫(xiě)的題材,都是因?yàn)檫@該死的拖延~~~好了進(jìn)入正題。
1.前言
契機(jī)是公司換了一套新接口,要求進(jìn)行全報(bào)文加密。以前公司項(xiàng)目基本上都使用的對(duì)稱加密的模式3DES
、AES
,由于對(duì)稱加密的密鑰只有一對(duì),有很大的密鑰泄露風(fēng)險(xiǎn)。身處金融這個(gè)極為敏感的行業(yè),對(duì)安全的要求也是極高。趁著這個(gè)機(jī)會(huì),把項(xiàng)目中的加密模式統(tǒng)一替換成RSA
非對(duì)稱加密。
2.關(guān)于加密算法
本篇不會(huì)對(duì)RSA
加密算法原理進(jìn)行詳細(xì)的解釋。在互聯(lián)網(wǎng)異常發(fā)達(dá)的今天,RSA
算法詳細(xì)的資料很容易就能獲取到。安全領(lǐng)域也是一個(gè)能夠深挖的領(lǐng)域,本篇文章偏向工程向,僅對(duì)一些基本基本概念進(jìn)行簡(jiǎn)單的解釋。
對(duì)稱加密和非對(duì)稱加密
對(duì)稱加密 :加密和解密用的是同一套密鑰,缺陷是密鑰管理存在風(fēng)險(xiǎn)。常用的加密方式有:DES
、3DES
、AES
等。
非對(duì)稱加密 :加密和解密用的不同的密鑰,公鑰加密私鑰解密。常用的加密方式有RSA
。RSA常見(jiàn)用法:
1.公鑰加密,私鑰解密;
2.私鑰簽名;
3.公鑰驗(yàn)簽。
3.實(shí)踐
1、生成密鑰:
使用終端openssl命令生成密鑰
1).生成私鑰,密鑰長(zhǎng)度為2048bit,base64編碼。
openssl genrsa -out rsa_private_key.pem 2048
關(guān)于密鑰長(zhǎng)度,這里要進(jìn)行一下特別說(shuō)明。每次加密的數(shù)據(jù)不能超出密鑰的長(zhǎng)度,2048bit長(zhǎng)度的密鑰,只能單次只能加密(2048 / 8 - 11) = 245byte長(zhǎng)度的數(shù)據(jù)。(那11byte是RSA預(yù)留的長(zhǎng)度)。若待加密的數(shù)據(jù)長(zhǎng)度超過(guò)了245byte,就需要對(duì)數(shù)據(jù)進(jìn)行分段加密。
2).根據(jù)之前生成的私鑰生成公鑰:
openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
3).轉(zhuǎn)換公鑰為PKCS8
格式,這種格式可以直接在iOS項(xiàng)目中使用:
openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt
我司的情況是由公司后臺(tái)生成私鑰和公鑰,提供.pem
格式的公鑰給移動(dòng)端,因此我直接使用的是pkcs8格式的公鑰。
注意:不建議將私鑰保存在客戶端,私鑰泄露后果會(huì)很?chē)?yán)重!
2、在項(xiàng)目中使用:
Demo地址:https://github.com/Hstripe/RHRSAUtil
以上是我使用的RSA
工具類,支持RSA
在移動(dòng)端的各種日常用法:加密、解密、加簽、驗(yàn)簽。跟網(wǎng)上很多工具類不同的是不需要導(dǎo)入p12
、der
格式的密鑰文件,支持字符串形式的密鑰文件非常方便。而且使用的是Apple
自家的Security.framework
。網(wǎng)上很多例程都是使用的openSSL
那一套加密工具類,實(shí)現(xiàn)也很方便就是包體積略微有點(diǎn)大。
若使用IDE
是Xcode8
及以上,請(qǐng)?jiān)?code>Capabilities中將KeyChain sharing
設(shè)置為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.)公鑰驗(yàn)簽
BOOL result = [IPNRSAUtil rsaVerifySignature:signature plainString:string WithPublicKey:publickey];
5.)分段加密
若待加密的數(shù)據(jù)長(zhǎng)度大于密鑰長(zhǎng)度,就需要對(duì)待加密的數(shù)據(jù)進(jìn)行分段加密
// n為密鑰單次加密長(zhǎng)度,這里使用的是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數(shù)據(jù),不要將data轉(zhuǎn)換成字符串再拼接,這樣會(huì)導(dǎo)致結(jié)果錯(cuò)誤。
[preData appendData:encryptData];
}
NSData *finalData = [[NSData alloc] initWithData:preData];
finalData = [finalData base64EncodedDataWithOptions:0];
NSString *result = [[NSString alloc] initWithData:finalData encoding:NSUTF8StringEncoding];
NSLog(@"result:%@",result);
}
4.后記
為了搞這個(gè)工具類前前后后花了也有半個(gè)多月的時(shí)間了,看了很多文檔和例程加上自己的實(shí)踐調(diào)試才有了這個(gè)工具類。一開(kāi)始使用的是openSSL
那一套加密工具實(shí)現(xiàn)的,但是覺(jué)得openSSL
占用的空間略大,還是用Security.framework
來(lái)實(shí)現(xiàn)的。平時(shí)總是忙于業(yè)務(wù)需求的實(shí)現(xiàn),忽視了客戶端方面的安全,網(wǎng)上對(duì)客戶端安全這一塊的資源也比較有限,但愿我這篇文章能對(duì)后來(lái)人有所幫助吧。
5.參考資料
看的資料比較多就不一一列舉了,主要還是通過(guò)
GitHub
、StackOverFlow
、簡(jiǎn)書(shū)
、知乎
等平臺(tái)來(lái)獲取相關(guān)資源的。最后,就在這里統(tǒng)一感謝一下相關(guān)資源的貢獻(xiàn)者吧。