Mac和 iOS 下的对称和非对称加密算法的使用
分享在Mac 和 iOS 上使用到的對稱和非對稱加密算法. 包括RSA,DSA, AES, DES, 3DES 和 blowfish 等等.
因?yàn)橐獙?shí)現(xiàn)ssh協(xié)議, 所以用到了這些算法, 這些算法在mac和ios上的接口比較難用, 我在這折騰了好長時(shí)間, 這里分享出來, 希望對大家有幫助.
(這里不提 openssl 在 apple 上的接口了)
主要參考了apple的文檔:
Cryptographic Services Guide
Apple Encrypting and Hashing Data
先大致概括一下整體情況:
基本上這兩個(gè)平臺上的加密解密都和 keychain service 密不可分. keychain是mac和ios上用來保存證書,密碼,key 等等敏感信息的存儲區(qū). 有專門的api訪問這些接口. 有時(shí)候我們?yōu)榱双@得一個(gè) key 的實(shí)例,必須想辦法把數(shù)據(jù)導(dǎo)入到keychain中,然后才能通過keychain獲得一個(gè)key的實(shí)例. 后面再說.
在mac 上, 有三個(gè)方式實(shí)現(xiàn)加密解密簽名服務(wù):
Security Transforms API —a Core-Foundation-level API that provides support for signing and verifying, symmetric cryptography, and Base64 encoding and decoding.
是 CoreFoundation 級別的api, 提供了, 最全的功能和算法支持. 包括對稱,非對稱算法. 實(shí)現(xiàn)加密,簽名功能. 但遺憾的是這個(gè)接口只在mac上有效. iOS上沒有. 但有些功能必須要用到, 所以要實(shí)現(xiàn)跨平臺的代碼,需要一些補(bǔ)丁.
Common Crypto—a C-level API that can perform most symmetric encryption and decryption tasks
這是一個(gè) C 風(fēng)格的接口. 好消息是它在mac和ios上都有, 可以跨平臺. 但壞消息是, 它只包含了, 對稱加密算法, 卻沒有非對稱算法. 因此只能加密解密,而不能簽名和驗(yàn)證. 其實(shí)之前 Apple上還有一個(gè) ComonCryptoRSA 模塊, 但后來不知為何消失了.
CDSA/CSSM —a legacy API that should be used only to perform tasks not supported by the other two APIs, such as asymmetric encryption
這個(gè)名字比較嚇人,?Common Data Security Architecture (CDSA)?通用數(shù)據(jù)安全架構(gòu). 很奇怪,它被apple接受后不久, 就被廢棄了. 現(xiàn)在已經(jīng)不建議使用了. 所以就不提它了.
在 iOS 上, 基本上有兩種方式:
Common Crypto. 這個(gè)在上面已經(jīng)說過了. 對稱算法接口.
使用系統(tǒng)提供的特有api實(shí)現(xiàn)加密,解密, 簽名和驗(yàn)證:
系統(tǒng)提供了下面4個(gè)函數(shù):
SecKeyEncrypt—encrypts a block of data using the specified key.SecKeyDecrypt—decrypts a block of data using the specified key.SecKeyRawSign—signs a block of data using the specified key.SecKeyRawVerify—verifies a signature against a block of data and a specified key.基于上面的分析, 我們秉著盡可能減少代碼重復(fù), 跨平臺開發(fā)的原則: 對稱算法就使用 “Common Crypto” 模塊了. 因?yàn)閮蓚€(gè)平臺都有. 而非對稱則需要分別實(shí)現(xiàn)了.
下面詳細(xì)分享一些細(xì)節(jié):
一, 非對稱加密算法, 簽名和驗(yàn)證.(RSA/DSA signature and verity)
這需要在兩個(gè)平臺獨(dú)立開發(fā).
在 mac 平臺上, 我們使用它的 Security Transforms API.
參考這里:?Security Transforms Programming Guide-Signing and Verifying
上面有很好的 代碼 片段. 需要注意的是如何把 RSA 的參數(shù) 變成 api 需要的 SecKeyRef 對象.
這是它的導(dǎo)入片段.
params.keyUsage = NULL;params.keyAttributes = NULL;SecExternalItemType itemType = kSecItemTypeCertificate;SecExternalFormat externalFormat = kSecFormatPEMSequence;int flags = 0;oserr = SecItemImport(cfdataprivatekey,NULL, // filename or extension&externalFormat, // See SecExternalFormat for details&itemType, // item typeflags, // See SecItemImportExportFlags for details¶ms,NULL, // Don't import into a keychain&temparray);if (oserr) {fprintf(stderr, "SecItemImport failed (oserr=%d)\n", oserr);CFShow(temparray);exit(-1);}privatekey = (SecKeyRef)CFArrayGetValueAtIndex(temparray, 0);這里是為了創(chuàng)建 SecKeyRef 實(shí)例. 通過 SecItemImport 把數(shù)據(jù)導(dǎo)入.變成SecKeyRef實(shí)例. 數(shù)據(jù)放在 cfdataprivatekey 中. 這個(gè)數(shù)據(jù)必須是 Pem格式的證書. 因?yàn)檫@個(gè)case下需要私鑰, 所以這個(gè)證書需要包含私鑰, 都是pem格式.
這里特別介紹一下, 如何從ssh 的公鑰格式導(dǎo)入. 以RSA為例, RSA的公鑰其實(shí)是一個(gè)底數(shù)e, 和一個(gè)大整數(shù) m ,
e = [int32(len), bytes(value)] m = [int32(len), bytes(value)]e 和 m 的結(jié)構(gòu)一樣. 先是4個(gè)字節(jié)的長度, 然后緊跟上字節(jié)序列. len是 大端在前的, 跟通常的小端是有區(qū)別的.
完整的機(jī)構(gòu)大概是這個(gè)樣子的:
這個(gè)keydata 就可以用來構(gòu)建上面用到的參數(shù)cfdataprivatekey了.
對于DSA, 結(jié)構(gòu)跟上面類似:
p = [int32(len), bytes(value)] q = [int32(len), bytes(value)] g = [int32(len), bytes(value)] y = [int32(len), bytes(value)]Binary = [0x00, 0x00, 0x00, 0x07, 'ssh-dss', p, q, g, y]keydata = 'ssh-dss' + Base64Encode(Binary)這兩個(gè)函數(shù)要命的是都需要一個(gè) SecKeyRef 參數(shù), iOS 上還真沒有直接的方式可以通過大整數(shù)直接創(chuàng)建 SecKeyRef的實(shí)例.
要么通過 keychain 讀取. 或者通過 SecPKCS12Import() 函數(shù)導(dǎo)入 pkcs12 格式的包含私鑰的證書, 然后獲得 SecIdentityRef 實(shí)例. 然后再通過 SecIdentityCopyPrivateKey() 函數(shù)把其中的 私鑰導(dǎo)出成 SecKeyRef實(shí)例.
OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,SecIdentityRef *outIdentity,SecTrustRef *outTrust,CFStringRef keyPassword) {OSStatus securityError = errSecSuccess;const void *keys[] = { kSecImportExportPassphrase };const void *values[] = { keyPassword };CFDictionaryRef optionsDictionary = NULL;/* Create a dictionary containing the passphrase if onewas specified. Otherwise, create an empty dictionary. */optionsDictionary = CFDictionaryCreate(NULL, keys,values, (keyPassword ? 1 : 0),NULL, NULL); // 1CFArrayRef items = NULL;securityError = SecPKCS12Import(inPKCS12Data,optionsDictionary,&items); // 2//if (securityError == 0) { // 3CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);const void *tempIdentity = NULL;tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);CFRetain(tempIdentity);*outIdentity = (SecIdentityRef)tempIdentity;const void *tempTrust = NULL;tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);CFRetain(tempTrust);*outTrust = (SecTrustRef)tempTrust;}if (optionsDictionary) // 4CFRelease(optionsDictionary);if (items)CFRelease(items);return securityError; }另外一個(gè)方法是, 蘋果官方給的示例代碼, 強(qiáng)制拼湊 SecKeyRef示例.
這里有 SecKeyWrapper 的實(shí)例代碼:?SecKeyWrapper 的實(shí)例代碼
并且可以在這里直接下載到 源碼:?SecKeyWrapper 源碼
這個(gè)源碼里面有很多 蘋果寫的例子. 非常好. 對劍使用這里面的代碼實(shí)現(xiàn).
二, 對于對稱加密算法.
這個(gè)比較簡單了, 我們直接使用 Common Crypto 模塊. 在mac 和ios上可以跨平臺.
請參考這里:?Apple Common Crypto library
然后不斷的調(diào)用 CCCryptorUpdate. 進(jìn)行加密/解密.
最后調(diào)用:CCCryptorFinal. 獲取最后一塊加密方法.
建議使用 CCCryptorCreateWithMode 方法.因?yàn)樗苤付ǜ嗟膮?shù). 比如 加密算法的padding 和ciphermode.
最后再順便分享一下Mac 和iOS上生成 密碼學(xué)安全的隨機(jī)數(shù)的方法:?Generating Random Numbers
簡單的來說. 在mac上, 可以通過 fopen 讀取?/dev/random?設(shè)備獲得密碼學(xué)安全的隨機(jī)數(shù).
FILE *fp = fopen("/dev/random", "r");if (!fp) {perror("randgetter");exit(-1); }uint64_t value = 0; int i; for (i=0; i<sizeof(value); i++) {value <<= 8;value |= fgetc(fp); }fclose(fp);而在 iOS 上, 由于不能讀取設(shè)備, 它提供了 專門的 方法:?SecRandomCopyBytes?, 用起來非常簡單.
歡迎訪問我的獨(dú)立博客?https://blog.byneil.com
轉(zhuǎn)載于:https://www.cnblogs.com/shuidao/p/4392312.html
總結(jié)
以上是生活随笔為你收集整理的Mac和 iOS 下的对称和非对称加密算法的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FastDFS安装、配置、部署(三)-S
- 下一篇: (转)CSS样式表继承详解