Swift RSA加密封装

作者: 屈涯 | 来源:发表于2019-06-17 15:59 被阅读0次
import Foundation
import Security

enum RSAKeySize: Int {
    case size512 = 512
    case size768 = 768
    case size1024 = 1024
    case size2048 = 2048
}

private let publicKeyIdentifier = "baseModule.publicKey"
private let privateKeyIdentifier = "baseModule.privateKey"
private let publicKeyTag = publicKeyIdentifier.data(using: .utf8)!
private let privateKeyTag = privateKeyIdentifier.data(using: .utf8)!

class RSAUtils {

    static let shared = RSAUtils()

    var publicSecKey: SecKey?
    var privateSecKey: SecKey?

    private let keySizeType: RSAKeySize = .size1024
    
    // RSA秘鍵を生成する
    // sizes for RSA keys are: 512, 768, 1024, 2048.
    func generateRSAKeyPair() {
        let keySize = keySizeType.rawValue
        publicSecKey = getRSAKeyFromKeychain(isPrivate: false, keySize: keySize)
        privateSecKey = getRSAKeyFromKeychain(isPrivate: true, keySize: keySize)
        if publicSecKey != nil {
            return
        }
        publicSecKey = nil
        privateSecKey = nil
        let parameters = [kSecAttrKeyType: kSecAttrKeyTypeRSA,
                          kSecAttrKeySizeInBits: keySize] as [CFString : Any]
        let ret = SecKeyGeneratePair(parameters as CFDictionary, &publicSecKey, &privateSecKey)
        assert(ret == errSecSuccess, "Error For SecKeyGeneratePair: \(ret)")

        saveRSAKeyToKeychain(key: publicSecKey!, keySize: keySize, isPrivate: false)
        saveRSAKeyToKeychain(key: privateSecKey!, keySize: keySize, isPrivate: true)
    }

    @objc public func encrypt(source: String) -> String {

        guard !source.isEmpty && self.publicSecKey != nil else {
            return ""
        }
        let data: NSData = (source.data(using: String.Encoding.utf8)! as NSData)
        var error: Unmanaged<CFError>?
        let resData =  SecKeyCreateEncryptedData(self.publicSecKey!, SecKeyAlgorithm.rsaEncryptionPKCS1, data as CFData, &error) as Data?
        if error != nil {
            DDLogError("res = \(String(describing: error?.takeUnretainedValue().localizedDescription))")
            return ""
        }
        return resData!.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: NSData.Base64EncodingOptions.lineLength64Characters.rawValue))
    }

    @objc public func decrypt(source: String) -> String {

        guard !source.isEmpty && self.privateSecKey != nil else {
            return ""
        }
        //base64 decode
        guard let data: Data = NSData(base64Encoded: (source as String), options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? else {
            return ""
        }

        var error: Unmanaged<CFError>?
        let resData =  SecKeyCreateDecryptedData(self.privateSecKey!, SecKeyAlgorithm.rsaEncryptionPKCS1, data as CFData, &error) as Data?
        if error != nil {
            DDLogError("res = \(String(describing: error?.takeUnretainedValue().localizedDescription))")
            return ""
        }
        return String(data: resData!, encoding: String.Encoding.utf8)!
    }

    private 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!
    }

    // keySize RSAキーの長さです
    // isPrivate 保管されている秘密鍵かどうかを判別します(trueは秘密鍵、falseは公開鍵)
    private 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] = SecKeyGetBlockSize(key)
        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, "SaveRSAKeyToKeychain Failure")
    }

    // isPrivate 保管されている秘密鍵かどうかを判別します(trueは秘密鍵、falseは公開鍵)
    // swiftlint:disable force_cast
    private func getRSAKeyFromKeychain(isPrivate: Bool, keySize: size_t) -> 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
        queryDictionary[kSecAttrKeySizeInBits as String] = keySize

        var key: CFTypeRef?
        let status = SecItemCopyMatching(queryDictionary as CFDictionary, &key)

        if status == errSecSuccess {
            let result = key as! SecKey
            return result
        }
        return nil
    }
    // swiftlint:enable force_cast
}

相关文章

网友评论

    本文标题:Swift RSA加密封装

    本文链接:https://www.haomeiwen.com/subject/yfaafctx.html