【问题标题】:Issue using CCCrypt (CommonCrypt) in Swift在 Swift 中使用 CCCrypt (CommonCrypt) 的问题
【发布时间】:2019-12-19 21:16:57
【问题描述】:

我正在将一个有效的 Objective-C 类别 (NSData+AESCrypt.m) 移植到 Swift,我发现使用指针时存在问题。 Swift 中加密部分的代码可以正确编译,但会产生运行时 EXEC_BAD_ACCES 错误。

我到目前为止的代码是(我试图尽可能地剖析代码):

let key = "123456789012345678901234567890120"
let keyLength = UInt(kCCKeySizeAES256 + 1)
let keyPointer = strdup(key)    // Convert key to <UnsafeMutablePointer<Int8>

let message = "Don´t try to read this text. Top Secret Stuff"
let data = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding)
let dataBytes = data?.bytes
let length = data?.length
let dataLength = UInt(length!)
let dataPointer = UnsafePointer<UInt8>(dataBytes!)

let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)

let cryptBufferSize = UInt(dataLength + kCCBlockSizeAES128)
var cryptBuffer = [UInt8](count: Int(cryptBufferSize), repeatedValue: 0)
var cryptBufferPointer = UnsafeMutablePointer<UInt8>(cryptBuffer)

var numBytesEncrypted = UnsafeMutablePointer<UInt>()

var cryptStatus = CCCrypt(operation, algoritm, options, keyPointer, keyLength, nil, dataPointer, dataLength, cryptBufferPointer, cryptBufferSize, numBytesEncrypted)

if UInt32(cryptStatus) == UInt32(kCCSuccess) {
    let size = NSInteger(cryptBufferSize)
    let encryptedData = NSData(bytes: cryptBufferPointer, length: size)
    let encryptedString = NSString(data: encryptedData, encoding: NSUTF8StringEncoding)
    println("Encrypted String = \(encryptedString)") // EXEC_BAD_ACCESS error
} else {
    println("Error: \(cryptStatus)")
}

encryptedData 对象显示以下信息:

<279c2d0f d3ce2200 0dc10cc1 9df46e76 cb26f423 7c9bde76 f9d8d0e2 632acef9 74fb0614 4717422b 684d1889 e3ce882c 00000000 00000000 00000000 0000>

但 encryptedString 在调试器中显示0x0000000000,并尝试println() 它会生成EXEC_BAD_ACCESS 错误

知道缺少什么吗?

Rgds....

【问题讨论】:

  • 这里有几个问题。 1.密钥长度不正确。 2. 输出未调整为 numBytesEncrypted。 3. 不能从任意数据中创建UTF-8字符串,并不是所有数据都可以形成UTF-8字符串。
  • 从安全的角度来看,假设安全是期望的结果,使用String作为密钥也是一个糟糕的主意,如果使用字符串,它应该用于派生密钥与 PBKFD2。最后:不要使用 ECB 模式,它不安全,请参阅ECB mode,向下滚动到 Penguin。而是使用带有随机IV的CBC模式,只需在加密数据前加上IV用于解密即可,它不需要不保密。
  • UInt32(cryptStatus) 出错会崩溃

标签: ios objective-c swift cryptography commoncrypto


【解决方案1】:

斯威夫特 2.0

这是一个例子
如果这不是所需要的,那么这些方法应该是一个很好的例子
注意:密钥字符串转换为数据

将 Security.framework 添加到项目中
#import &lt;CommonCrypto/CommonCryptor.h&gt; 添加到桥接头。

let keyString        = "12345678901234567890123456789012"
let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
print("keyLength   = \(keyData.length), keyData   = \(keyData)")

let message       = "Don´t try to read this text. Top Secret Stuff"
let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
print("data length = \(data.length), data      = \(data)")

let cryptData    = NSMutableData(length: Int(data.length) + kCCBlockSizeAES128)!

let keyLength              = size_t(kCCKeySizeAES256)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options:   CCOptions   = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)

var numBytesEncrypted :size_t = 0

var cryptStatus = CCCrypt(operation,
    algoritm,
    options,
    keyData.bytes, keyLength,
    nil,
    data.bytes, data.length,
    cryptData.mutableBytes, cryptData.length,
    &numBytesEncrypted)

if UInt32(cryptStatus) == UInt32(kCCSuccess) {
    cryptData.length = Int(numBytesEncrypted)
    print("cryptLength = \(numBytesEncrypted), cryptData = \(cryptData)")

    // Not all data is a UTF-8 string so Base64 is used
    let base64cryptString = cryptData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
    print("base64cryptString = \(base64cryptString)")

} else {
    print("Error: \(cryptStatus)")
}

输出:

keyLength   = 32, keyData   = <31323334 35363738 39303132 33343536 37383930 31323334 35363738 39303132>
dataLength  = 46, data      = <446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666>
cryptLength = 48, cryptData = <118a32dc c23f7caa 883abc3c 1c7f0770 e200016b 2737acfa 17bb96fb a02b02a7 c147603b 06acd863 94bb8ff2 6cb14515>
base64cryptString = EYoy3MI/fKqIOrw8HH8HcOIAAWsnN6z6F7uW+6ArAqfBR2A7BqzYY5S7j/JssUUV

斯威夫特 3

iv 是加密数据的前缀

aesCBC128Encrypt 将创建一个随机 IV 并作为加密代码的前缀。
aesCBC128Decrypt 将在解密期间使用前缀 IV。

输入是数据,键是数据对象。如果需要在调用方法中转换为和/或从编码形式(如 Base64)。

密钥的长度应为 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。如果使用其他密钥大小,则会引发错误。

PKCS#7 padding是默认设置的。

此示例需要 Common Crypto
项目必须有桥接头:
#import &lt;CommonCrypto/CommonCrypto.h&gt;
Security.framework 添加到项目中。

这是示例,不是生产代码。

enum AESError: Error {
    case KeyError((String, Int))
    case IVError((String, Int))
    case CryptorError((String, Int))
}

// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
    let keyLength = keyData.count
    let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
    if (validKeyLengths.contains(keyLength) == false) {
        throw AESError.KeyError(("Invalid key length", keyLength))
    }

    let ivSize = kCCBlockSizeAES128;
    let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
    var cryptData = Data(count:cryptLength)

    let status = cryptData.withUnsafeMutableBytes {ivBytes in
        SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
    }
    if (status != 0) {
        throw AESError.IVError(("IV generation failed", Int(status)))
    }

    var numBytesEncrypted :size_t = 0
    let options   = CCOptions(kCCOptionPKCS7Padding)

    let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
        data.withUnsafeBytes {dataBytes in
            keyData.withUnsafeBytes {keyBytes in
                CCCrypt(CCOperation(kCCEncrypt),
                        CCAlgorithm(kCCAlgorithmAES),
                        options,
                        keyBytes, keyLength,
                        cryptBytes,
                        dataBytes, data.count,
                        cryptBytes+kCCBlockSizeAES128, cryptLength,
                        &numBytesEncrypted)
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.count = numBytesEncrypted + ivSize
    }
    else {
        throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
    }

    return cryptData;
}

// The iv is prefixed to the encrypted data
func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? {
    let keyLength = keyData.count
    let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
    if (validKeyLengths.contains(keyLength) == false) {
        throw AESError.KeyError(("Invalid key length", keyLength))
    }

    let ivSize = kCCBlockSizeAES128;
    let clearLength = size_t(data.count - ivSize)
    var clearData = Data(count:clearLength)

    var numBytesDecrypted :size_t = 0
    let options   = CCOptions(kCCOptionPKCS7Padding)

    let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
        data.withUnsafeBytes {dataBytes in
            keyData.withUnsafeBytes {keyBytes in
                CCCrypt(CCOperation(kCCDecrypt),
                        CCAlgorithm(kCCAlgorithmAES128),
                        options,
                        keyBytes, keyLength,
                        dataBytes,
                        dataBytes+kCCBlockSizeAES128, clearLength,
                        cryptBytes, clearLength,
                        &numBytesDecrypted)
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        clearData.count = numBytesDecrypted
    }
    else {
        throw AESError.CryptorError(("Decryption failed", Int(cryptStatus)))
    }

    return clearData;
}

示例用法:

let clearData = "clearData0123456".data(using:String.Encoding.utf8)!
let keyData   = "keyData890123456".data(using:String.Encoding.utf8)!
print("clearData:   \(clearData as NSData)")
print("keyData:     \(keyData as NSData)")

var cryptData :Data?
do {
    cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData)
    print("cryptData:   \(cryptData! as NSData)")
}
catch (let status) {
    print("Error aesCBCEncrypt: \(status)")
}

let decryptData :Data?
do {
    let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData)
    print("decryptData: \(decryptData! as NSData)")
}
catch (let status) {
    print("Error aesCBCDecrypt: \(status)")
}

示例输出:

clearData:   <636c6561 72446174 61303132 33343536>
keyData:     <6b657944 61746138 39303132 33343536>
cryptData:   <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0>
decryptData: <636c6561 72446174 61303132 33343536>

注意事项:

CBC 模式示例代码的一个典型问题是,它将随机 IV 的创建和共享留给用户。此示例包括生成 IV、为加密数据添加前缀并在解密期间使用前缀 IV。这将临时用户从CBC mode 所需的详细信息中解放出来。

为了安全起见,加密数据也应该具有身份验证,此示例代码不提供此功能,以便更小并允许与其他平台更好的互操作性。

还缺少从密码中导出密钥的密钥,建议使用PBKDF2,将文本密码用作密钥材料。

有关强大的生产就绪多平台加密代码,请参阅RNCryptor

【讨论】:

  • 谢谢扎夫。这完美地工作。关于解密过程,我还有一个问题。我使用您的代码作为参考,但似乎缺少一些东西。我注意到,在使用 cryptData.base64EncodedStringWithOptions 将 NSMutableData 对象 (crypData) 转换为 String 之后,如果使用 string.dataUsingEncoding(NSDatabase64EncodingOptions) 将结果字符串转换回 NSData,则会得到与 cryptData 不同的东西。
  • 如果是小事问这里,它涉及代码创建一个新的问题。
  • 对不起,我按了回车键,所以我不得不进入评论编辑模式......我将在一个新问题中详细说明这个问题
  • 这确实应该是一个新问题。 1:AES 是基于块的,因此需要填充,除非数据总是是块大小的倍数(AES 为 16 字节)。 2:请注意,CBC 模式是默认设置,因此要删除所有模式和填充,您需要传递 kCCOptionECBMode,这只是基本加密,如果您不想添加意味着 CBC 模式和无填充的选项,请使用 0。 3:CBC模式使用iv,ECB模式不需要iv但也不是很安全。以与创建 keyDatadata 相同的方式创建 iv,并在 CCCrypt 调用中使用 ivData.bytes。
  • 对于 CBC 模式,需要 iv,请参阅 Cipher Block Chaining。 CCCrypt 方法将提供一个默认值:“如果选择了 CBC 模式(由于选项标志中没有任何模式位)并且不存在 IV,则将使用 NULL(全零)IV。”但是为了安全起见,应该提供一个随机的 iv,通常的约定是在加密数据之前添加 iv,这样它就可以用于 decryption.eroes。
【解决方案2】:

Swift 2.0 加密和解密。刚刚更新了上面的答案以支持 Swift 2.0。

static func AES128Encryption()
    {
        let keyString        = "12345678901234567890123456789012"
        let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafeMutablePointer<Void>(keyData.bytes)
        print("keyLength   = \(keyData.length), keyData   = \(keyData)")

        let message       = "Don´t try to read this text. Top Secret Stuff"
        let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let dataLength    = size_t(data.length)
        let dataBytes     = UnsafeMutablePointer<Void>(data.bytes)
        print("dataLength  = \(dataLength), data      = \(data)")

        let cryptData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
        let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
        let cryptLength  = size_t(cryptData!.length)

        let keyLength              = size_t(kCCKeySizeAES256)
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(kCCOptionPKCS7Padding + kCCOptionECBMode)

        var numBytesEncrypted :size_t = 0

        let cryptStatus = CCCrypt(operation,
            algoritm,
            options,
            keyBytes, keyLength,
            nil,
            dataBytes, dataLength,
            cryptPointer, cryptLength,
            &numBytesEncrypted)

        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
          //  let x: UInt = numBytesEncrypted
            cryptData!.length = Int(numBytesEncrypted)
            print("cryptLength = \(numBytesEncrypted), cryptData = \(cryptData)")

            // Not all data is a UTF-8 string so Base64 is used
            let base64cryptString = cryptData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
            print("base64cryptString = \(base64cryptString)")

        } else {
            print("Error: \(cryptStatus)")
        }
    }

    static func AES128Decryption(data:NSData) //data = cryptData
    {
        let keyString        = "12345678901234567890123456789012"
        let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafeMutablePointer<Void>(keyData.bytes)
        print("keyLength   = \(keyData.length), keyData   = \(keyData)")

        //let message       = "Don´t try to read this text. Top Secret Stuff"
       // let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let dataLength    = size_t(data.length)
        let dataBytes     = UnsafeMutablePointer<Void>(data.bytes)
        print("dataLength  = \(dataLength), data      = \(data)")

        let cryptData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
        let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
        let cryptLength  = size_t(cryptData!.length)

        let keyLength              = size_t(kCCKeySizeAES256)
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(kCCOptionPKCS7Padding + kCCOptionECBMode)

        var numBytesEncrypted :size_t = 0

        let cryptStatus = CCCrypt(operation,
            algoritm,
            options,
            keyBytes, keyLength,
            nil,
            dataBytes, dataLength,
            cryptPointer, cryptLength,
            &numBytesEncrypted)

        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            //  let x: UInt = numBytesEncrypted
            cryptData!.length = Int(numBytesEncrypted)
            print("DecryptcryptLength = \(numBytesEncrypted), Decrypt = \(cryptData)")

            // Not all data is a UTF-8 string so Base64 is used
            let base64cryptString = cryptData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
            print("base64DecryptString = \(base64cryptString)")
               print( "utf8 actual string = \(NSString(data: cryptData!, encoding: NSUTF8StringEncoding))");
        } else {
            print("Error: \(cryptStatus)")
        }
    }

【讨论】:

    【解决方案3】:

    zaph 被现代 swift 采用(又一次弃用了另一个 swift api 导致另一组没有人真正关心的警告,即使他们应该关心)

    extension Data
    {
        func aes256CBCEncrypt(keyData: Data) -> Data? {
            let keyLength = keyData.count
            if keyData.count != kCCKeySizeAES256 {
                return nil
            }
            let data = self;
            let ivSize = kCCBlockSizeAES128;
            let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
            var cryptData = Data(count:cryptLength)
    
            let status: Int32 = cryptData.withUnsafeMutableBytes {
                guard let ivBytes = $0.baseAddress else {
                    return 0
                }
                return SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
            }
            if (status != 0) {
    // who cares, really?            return nil 
            }
    
            var numBytesEncrypted :size_t = 0
            let options   = CCOptions(kCCOptionPKCS7Padding)
    
            let cryptStatus: CCCryptorStatus = cryptData.withUnsafeMutableBytes {
                guard let cryptBytes = $0.baseAddress else {
                    return CCCryptorStatus(kCCMemoryFailure) // sadly kCCMoronSwiftLanguageSpecDeveloperDetected does not exist
                }
                let cryptStatus: CCCryptorStatus = data.withUnsafeBytes {
                    guard let dataBytes = $0.baseAddress else {
                        return CCCryptorStatus(kCCMemoryFailure)
                    }
                    return keyData.withUnsafeBytes {
                        guard let keyBytes = $0.baseAddress else {
                            return CCCryptorStatus(kCCMemoryFailure)
                        }
                        return CCCrypt(CCOperation(kCCEncrypt),
                                CCAlgorithm(kCCAlgorithmAES),
                                options,
                                keyBytes, keyLength,
                                cryptBytes,
                                dataBytes, data.count,
                                cryptBytes+kCCBlockSizeAES128, cryptLength,
                                &numBytesEncrypted)
                    }
                }
                return cryptStatus
            }
    
            if UInt32(cryptStatus) == UInt32(kCCSuccess) {
                cryptData.count = numBytesEncrypted + ivSize
            }
            else {
                return nil
            }
            return cryptData;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      • 2021-09-24
      相关资源
      最近更新 更多