Swift保存RSA密钥到Keychain

最近项目的需求用到RSA的加密解密,并且需要把公钥信息保存到Keychain里面,网上很多文章都是用Keychain保存账号密码什么的,自己在实现保存的公钥过程中也踩了不少坑。现在来整理一下实现的方法。

首先肯定是先要导入 Security

import Security

然后定义一个常量 Identifier

// 密钥唯一标示
fileprivate let publicKeyIdentifier = "com.hhh.publicKey"
fileprivate let privateKeyIdentifier = "com.hhh.privateKey"
fileprivate let publicKeyTag = publicKeyIdentifier.data(using: .utf8)!
fileprivate let privateKeyTag = privateKeyIdentifier.data(using: .utf8)!

实现一个把SecKey转换成Data的方法

private static func getKeyDataFrom(secKey: SecKey, tag: Data) -> Data {
        var data: Data?
        
        var query = [String: Any]()
        query[kSecClass as String] = kSecClassKey
        query[kSecAttrApplicationTag as String] = tag
        query[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA
        
        var attributes = query
        attributes[kSecValueRef as String] = secKey
        attributes[kSecReturnData as String] = true
        var result: CFTypeRef?
        let status = SecItemAdd(attributes as CFDictionary, &result)
        
        if status == errSecSuccess {
            data = result as? Data
            SecItemDelete(query as CFDictionary)
        }
        return data!
    }

这个方法的原理其实就是先把SecKey存到Keychain里面然后再读取出Data类型的数据

接下来就是实现存到Keychain的方法

// keySize 就是RSA密钥的长度
// isPrivate 判断存储的是否为私钥(true为私钥、false为公钥)
static func saveRSAKeyToKeychain(key: SecKey, keySize: size_t, isPrivate: Bool) {
        var saveDictionary = [String: Any]()
        let keyClass = isPrivate ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic
        // 设置keychain字典
        saveDictionary[kSecClass as String] = kSecClassKey
        saveDictionary[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA
        saveDictionary[kSecAttrApplicationTag as String] = isPrivate ? privateKeyTag : publicKeyTag
        saveDictionary[kSecAttrKeyClass as String] = keyClass
        saveDictionary[kSecValueData as String] = getKeyDataFrom(secKey: key, tag: isPrivate ? privateKeyTag : publicKeyTag)
        saveDictionary[kSecAttrKeySizeInBits as String] = keySize
        saveDictionary[kSecAttrEffectiveKeySize as String] = keySize
        saveDictionary[kSecAttrCanDerive as String] = kCFBooleanFalse
        saveDictionary[kSecAttrCanEncrypt as String] = kCFBooleanTrue
        saveDictionary[kSecAttrCanDecrypt as String] = kCFBooleanTrue
        saveDictionary[kSecAttrCanVerify as String] = kCFBooleanTrue
        saveDictionary[kSecAttrCanSign as String] = kCFBooleanFalse
        saveDictionary[kSecAttrCanWrap as String] = kCFBooleanTrue
        saveDictionary[kSecAttrCanUnwrap as String] = kCFBooleanFalse
        saveDictionary[kSecAttrApplicationLabel as String] = isPrivate ? privateKeyIdentifier : publicKeyIdentifier
        // 删除旧数据
        SecItemDelete(saveDictionary as CFDictionary)
        let status = SecItemAdd(saveDictionary as CFDictionary, nil)
        assert(status == errSecSuccess, "keychain存储密钥失败")
    }

最后就是实现从Keychain取出密钥信息的方法

// isPrivate 判断存储的是否为私钥(true为私钥、false为公钥)
static func getRSAKeyFromKeychain(isPrivate: Bool) -> SecKey! {
        var queryDictionary = [String: Any]()
        let keyClass = isPrivate ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic
        
        queryDictionary[kSecClass as String] = kSecClassKey
        queryDictionary[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA
        queryDictionary[kSecAttrApplicationTag as String] = isPrivate ? privateKeyTag : publicKeyTag
        queryDictionary[kSecAttrKeyClass as String] = keyClass
        queryDictionary[kSecReturnRef as String] = kCFBooleanTrue
        queryDictionary[kSecAttrApplicationLabel as String] =  isPrivate ? privateKeyIdentifier : publicKeyIdentifier
        
        var key: CFTypeRef?
        let status = SecItemCopyMatching(queryDictionary as CFDictionary, &key)
        
        if status == errSecSuccess {
            // 强转SecKey类型
            return key as! SecKey
        }
        assert(false, "keychain读取密钥失败")
    }

因为在开头我们已经定义了公钥私钥的 Identifier 所有我们只需要判断存/取的是私钥还是公钥就可以将密钥信息进行读写操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容