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 所有我们只需要判断存/取的是私钥还是公钥就可以将密钥信息进行读写操作。

你可能感兴趣的:(Swift保存RSA密钥到Keychain)