使用iOS安全API进行数据加密、解密、签名与验证完整指南

4 阅读5分钟
按下回车或点击查看完整尺寸图片

1. 加密与解密的基础知识

进行加密和解密操作,需要公钥和私钥。这些密钥通常通过安全隔离区、随机字符串、密码生成,或者从证书中获取。

2. 什么是签名和验证?

通过对数据进行签名,可以创建出一个数字签名。通过提供原始数据和签名,可以验证数据的完整性。

数字签名是接收方验证发送方身份真实性、数据完整性的保证。简单来说,它相当于手写签名。数字签名使用的是与加密/解密不同的另一套密钥对。

数字签名广泛应用于许多电子领域,例如当你要将应用提交测试或上传至App Store时,Xcode会对你的代码进行数字签名。通常,发送方对要发送的数据进行签名的流程如下:

  • 发送方使用特定的哈希算法(例如:sha256)对数据进行加密。哈希算法的输出称为"摘要"(例如:6e04f289)。
  • 然后,发送方使用自己的私钥摘要进行加密。这个加密后的摘要就是数据的"数字签名"。
  • 发送方将原始数据加密后的摘要一起发送出去。请注意,加密后的摘要仅用于验证发送方身份,而消息本身并未使用签名和验证所用的同一私钥进行加密。消息的加密是通过类似于上一节所述方式创建的私钥来完成的。

接收方验证接收到的数据的流程如下:

  • 接收方收到消息及其数字签名。
  • 接收方使用发送方的公钥解密数字签名 -> 得到的结果是摘要
  • 接收方使用与发送方相同的哈希算法,对收到的明文数据进行哈希运算 -> 这将输出一个摘要
  • 如果步骤2(数字签名解密后)输出的摘要 = 步骤3(对接收到的明文数据哈希后)输出的摘要 -> 接收方就可以验证数据的完整性。

在本教程中,用于数字签名的私钥将存储在安全隔离区中,而公钥则存储在安全隔离区之外,并与接收方共享。

从证书中获取公钥和私钥

class CryptoGraphicOperationViewModel {
    var p12Data: Data
    var password: String

    init(fileName: String = "Certificate", ext: String = "p12", password: String = "1233") {
        self.p12Data = Self.insertCertificate() ?? Data()
        self.password = password
    }

    static func insertCertificate(fileName: String, ext: String, password: String) -> Data? {
        return Utils.dataFromFile(name: fileName, withExtension: ext)
    }

    public func encrypt(data: Data, algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512AESGCM) -> Data? {
        guard let result = Utils.certificateDataFromP12(p12Data: self.p12Data, password: self.password) else { return nil }
        guard let publicAndPrivateKey = Utils.publicAndPrivateKey(pkcs12Entry: result.pkcs12Entry) else { return nil }
        return self.encrypt(value: data, publicKey: publicAndPrivateKey.publicKey, algorithm: algorithm)
    }

    public func decrypt(data: Data, algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512AESGCM) -> Data? {
        guard let result = Utils.certificateDataFromP12(p12Data: self.p12Data, password: self.password) else { return nil }
        guard let publicAndPrivateKey = Utils.publicAndPrivateKey(pkcs12Entry: result.pkcs12Entry) else { return nil }
        return self.decrypt(encrypted: data, privateKey: publicAndPrivateKey.privateKey, algorithm: algorithm)
    }

    public func sign(dataToSign: Data, algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA512) -> Data? {
        guard let result = Utils.certificateDataFromP12(p12Data: self.p12Data, password: self.password) else { return nil }
        guard let publicAndPrivateKey = Utils.publicAndPrivateKey(pkcs12Entry: result.pkcs12Entry) else { return nil }
        return self.signThe(data: dataToSign, privateKey: publicAndPrivateKey.privateKey, algorithm: algorithm)
    }

    public func verify(signedData: Data, signature: Data, algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA512) -> Bool {
        guard let result = Utils.certificateDataFromP12(p12Data: self.p12Data, password: self.password) else { return false }
        guard let publicAndPrivateKey = Utils.publicAndPrivateKey(pkcs12Entry: result.pkcs12Entry) else { return false }
        return self.verifySigned(data: signedData, signature: signature, publicKey: publicAndPrivateKey.publicKey, algorithm: algorithm)
    }

    internal func encrypt(value: Data, publicKey: SecKey, algorithm: SecKeyAlgorithm) -> Data? {
        guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else {
            debugPrint("不支持的加密算法")
            return nil
        }
        var errorRef: Unmanaged<CFError>?
        guard let encryptedData = SecKeyCreateEncryptedData(publicKey, algorithm, value as CFData, &errorRef) else {
            let error = errorRef?.takeRetainedValue()
            debugPrint("加密错误:\(String(describing: error))")
            return nil
        }
        return encryptedData as Data
    }

    internal func decrypt(encrypted: Data, privateKey: SecKey, algorithm: SecKeyAlgorithm) -> Data? {
        guard SecKeyIsAlgorithmSupported(privateKey, .decrypt, algorithm) else {
            debugPrint("不支持的解密算法")
            return nil
        }
        var errorRef: Unmanaged<CFError>?
        guard let decryptedData = SecKeyCreateDecryptedData(privateKey, algorithm, encrypted as CFData, &errorRef) as Data? else {
            let error = errorRef?.takeRetainedValue()
            debugPrint("解密错误:\(String(describing: error))")
            return nil
        }
        return decryptedData
    }

    internal func signThe(data: Data, privateKey: SecKey, algorithm: SecKeyAlgorithm) -> Data? {
        guard SecKeyIsAlgorithmSupported(privateKey, .sign, algorithm) else {
            debugPrint("不支持的签名算法")
            return nil
        }
        var errorRef: Unmanaged<CFError>?
        guard let signature = SecKeyCreateSignature(privateKey, algorithm, data as CFData, &errorRef) as Data? else {
            let error = errorRef?.takeRetainedValue()
            debugPrint("签名错误:\(String(describing: error))")
            return nil
        }
        return signature as Data
    }

    internal func verifySigned(data: Data, signature: Data, publicKey: SecKey, algorithm: SecKeyAlgorithm) -> Bool {
        guard SecKeyIsAlgorithmSupported(publicKey, .verify, algorithm) else {
            debugPrint("不支持的验证算法")
            return false
        }
        guard data.isNotEmpty else {
            debugPrint("无法使用空签名数据进行验证")
            return false
        }
        var errorRef: Unmanaged<CFError>?
        guard SecKeyVerifySignature(publicKey, algorithm, data as CFData, signature as CFData, &errorRef) else {
            debugPrint("验证错误:\(String(describing: errorRef?.takeRetainedValue() as Error?))")
            return false
        }
        return true
    }
}

从iOS安全隔离区生成密钥

// 创建访问控制对象
let access = SecAccessControlCreateWithFlags(
    kCFAllocatorDefault,                         // 1
    kSecAttrAccessibleWhenUnlockedThisDeviceOnly, // 2
    [.privateKeyUsage, .biometryAny],            // 3
    nil                                           // 4
)!

let secEnclaveTag = "YourKey".data(using: .utf8)! // 5

let privateKeyParams: [String: AnyObject] = [
    kSecAttrIsPermanent as String: true as AnyObject,     // 6
    kSecAttrApplicationTag as String: secEnclaveTag as AnyObject, // 7
    kSecAttrAccessControl as String: access               // 8
]

let attributes = [
    kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, // 9
    kSecAttrKeySizeInBits as String: 256,                 // 10
    kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,    // 11
    kSecPrivateKeyAttrs as String: privateKeyParams       // 12
] as CFDictionary

var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
    throw error!.takeRetainedValue() as Error
}

guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
    print("公钥生成错误")
    return ""
}

创建签名

let message = "AshishAwasthi"
guard let messageData = message.data(using: String.Encoding.utf8) else {
    print("无效的待签名消息。")
    return nil
}

// 1. 创建签名
guard let signData = SecKeyCreateSignature(privateKey, 
                                          SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256,
                                          messageData as CFData,
                                          nil) else {
    print("签名错误")
    return nil
}

// 2. 获取签名数据
let signedData = signData as Data
let signedString = signedData.base64EncodedString(options: [])
print("签名字符串", signedString)

// 3. 验证签名
let messageToVerify = "AshishAwasthi"
guard let messageDataToVerify = messageToVerify.data(using: String.Encoding.utf8) else {
    print("ECC 无效的待验证消息")
    return false
}

guard let signatureData = signedData else {
    print("无效的待验证消息。")
    return false
}

let verify = SecKeyVerifySignature(publicKey,
                                  SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256,
                                  messageDataToVerify as CFData,
                                  signatureData as CFData,
                                  nil)

总结

在本教程中,您学到了:

  • 对称加密与非对称加密及解密的区别。
  • 如何在iOS应用中使用Swift安全API实现非对称加密。
  • iOS安全元件/隔离区。
  • 数字签名和验证。
  • 如何通过安全隔离区对数据进行数字签名和验证。

结语

如果您觉得本文信息量大,请考虑分享并留下反馈,以便它能触达更广泛的读者 👏👏👏👏👏 !!!!FINISHED CSD0tFqvECLokhw9aBeRqun7QRQBiR11shbIyj7WeHfnF6U3xpa+0XjfU5DHXhvAyl5l+jExjFG4yiYEgxlUn78vQpdt/iTlM7OZ+q6Yb8t99Q5GsOMP/Y5LZ6+0EoZbl1FbJH3na1nVAFh5ucwQIyIF+TLXD5Sz9UGvbCO1uQo=