使用Swift中的CommonCrypto解密时出现问题

4
我正在为String和NSData编写仅使用Swift的加密/解密扩展程序,加密部分基于@Zaph在链接问题Issue using CCCrypt (CommonCrypt) in Swift中提供的答案。使用Objective-C中的NSData+AESCrypt.m类别测试了加密输出。我一直在处理一个解密问题:代码可以编译和运行,但结果不是最初加密的预期文本。
extension NSData {
    func AES256EncryptDataWithKey(key: String) -> NSData {
        let keyData: NSData! = (key as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafePointer<UInt8>(keyData.bytes)
        let keyLength        = size_t(kCCKeySizeAES256)
        let dataLength    = UInt(self.length)
        let dataBytes     = UnsafePointer<UInt8>(self.bytes)
        let bufferData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
        var bufferPointer = UnsafeMutablePointer<UInt8>(bufferData.mutableBytes)
        let bufferLength  = size_t(bufferData.length)
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
        var numBytesEncrypted: UInt = 0
        var cryptStatus = CCCrypt(operation,
            algoritm,
            options,
            keyBytes, keyLength,
            nil,
            dataBytes, dataLength,
            bufferPointer, bufferLength,
            &numBytesEncrypted)
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            bufferData.length = Int(numBytesEncrypted) // Requiered to adjust buffer size
            return bufferData as NSData
        } else {
            println("Error: \(cryptStatus)")
            return NSData()
        }
    }

    func AES256DecryptDataWithKey(key: String) -> NSData {
        let keyData: NSData! = (key as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafePointer<UInt8>(keyData.bytes)
        let keyLength        = size_t(kCCKeySizeAES256)
        let dataLength    = UInt(self.length)
        let dataBytes     = UnsafePointer<UInt8>(self.bytes)
        let string = self.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
        let bufferData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
        var bufferPointer = UnsafeMutablePointer<UInt8>(bufferData.mutableBytes)
        let bufferLength  = size_t(bufferData.length)
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
        var numBytesDecrypted: UInt = 0
        var cryptStatus = CCCrypt(operation,
            algoritm,
            options,
            keyBytes, keyLength,
            nil,
            dataBytes, dataLength,
            bufferPointer, bufferLength,
            &numBytesDecrypted)            
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            bufferData.length = Int(numBytesDecrypted) // Requiered to adjust buffer size
            return bufferData as NSData
        } else {
            println("Error: \(cryptStatus)")
            return NSData()
        }
    }
}

extension String {        
    func AES256EncryptStringWithKey(key: String) -> String {
        let data = (self as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let encryptedData = data.AES256EncryptDataWithKey(key)
        // Not all data is a UTF-8 string so Base64 is used
        let base64cryptString = encryptedData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
        return base64cryptString
    }

    func AES256DecryptStringWithKey(key: String) -> String {
        let data: NSData! = (self as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let decryptedData = data.AES256DecryptDataWithKey(key)            
        // Not all data is a UTF-8 string so Base64 is used
        let base64decryptString = decryptedData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
        return base64decryptString
    }
}

正如你所看到的,String.encryptStringWithKey() 调用了 NSData.encryptDataWithKey()。因此,这些扩展适用于 StringNSData 两种类型。
    let string: String = "Don´t try to read this text. Top Secret Stuff"
    let key = "12345678901234567890123456789012"
    println("Original String  : \(string)");
    let encryptedString = string.AES256EncryptStringWithKey(key)
    println("Encrypted String : \(encryptedString)")        
    let decryptedString = encryptedString.AES256DecryptStringWithKey(key)
    println("Decrypted String: \(decryptedString)")

任何帮助都将不胜感激。

1
您可以对输入和输出进行十六进制转储,并比较差异。十六进制转储:操作、算法、选项、密钥字节、密钥长度、数据字节、数据长度。某处存在差异。 - zaph
@eharo2,你能否发布一下你修改代码的方式以使其正常工作?你是继续使用CommonCrypto还是转向RNCryptor?我正在尝试在Swift 3.1中使用CommonCrypto。 - user2363025
3个回答

4
区别在于NSData+AESCrypt.m使用CBC模式(默认模式),初始向量为NULL。问题中的代码使用ECB模式。
最佳实践是使用带有随机初始向量的CBC模式。通常,初始向量会被预先添加到加密数据中,以便解密可以在解密之前分离初始向量和数据。
不要使用NSData+AESCrypt.m,它没有得到维护,是NSData的一个类别,不支持ARC。考虑使用Objective-C的RNCryptor,它正在积极维护。
这是我在"NSData+AESCrypt.m"中进行的更改,方法 。我只添加了。
这是我所做的调用: NSString *keyString = @"12345678901234567890123456789012";
NSString *message = @"Don´t try to read this text. Top Secret Stuff";
NSData   *data    = [message dataUsingEncoding:NSUTF8StringEncoding];

NSData *crypData = [data AES256EncryptWithKey:keyString];
NSLog(@"crypData: %@", crypData);

输出:
crypData: <118a32dc c23f7caa 883abc3c 1c7f0770 e200016b 2737acfa 17bb96fb a02b02a7 c147603b 06acd863 94bb8ff2 6cb14515>

这与上面的代码相同(与前一个问题相同):
cryptData = <118a32dc c23f7caa 883abc3c 1c7f0770 e200016b 2737acfa 17bb96fb a02b02a7 c147603b 06acd863 94bb8ff2 6cb14515>

这只是让所有输入保持一致的问题:操作、算法、选项、keyBytes、keyLength、dataBytes、dataLength和iv(如果非ECB模式)。CCCrypt只是一个函数调用,仅此而已。输入相同,输出相同。请添加NSLog()语句,最好是数据和字符串的十六进制转储。根据需要进行比较和修复。信不信由你,这是电子安全的简单部分。

嗨@Zaph。我有一个使用ECB模式和NULL iv的NSData + AESCrypt.m版本。鉴于您提供的用于在其他相关问题(https://dev59.com/eF8e5IYBdhLWcg3wcJ8q)中进行加密的Swift代码,我测试并能够使用Swift进行加密并使用Objective-C进行解密。您能帮我找出这个NSData Swift扩展中缺少了什么吗?...罗德斯.. - eharo2
1
我使用修改过的NSData+AESCrypt.m文件以ECB模式运行,并得到了与此问题中代码相同的结果。ECB模式不使用iv。使用NULL作为iv,第一个加密块与CBC或ECB相同,后续块则不同。 - zaph
感谢@Zaph指引我正确的方向...现在已经可以工作了。 - eharo2

1

Swift 2.0的AES128加密与解密。

应该将以下头文件导入Obj-c桥接头文件才能正常工作。

#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonKeyDerivation.h>


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)")
        }
    }

1
代码不能直接运行。你可以提及应该添加到Obj-c桥接中的内容,以使其正常工作。谢谢。 - Krystian
@Krystian 感谢您的评论。已更新答案。 - karthikPrabhu Alagu
@karthikPrabhu,我按照你的代码片段进行操作,但在解密时,我得到了utf8实际字符串= nil。而base64DecryptString是随机字符串值! - Mazen Kasser

-1

Swift4 AES128 解密和 JWT

注意:如果在加密明文时使用了 iv 和 key,请在解密时不要使用它,这样可以解决你的问题。 key 和 iv 的长度为16位

以下解密代码可能会对您有所帮助: 这里用于解密的加密字符串base64Encoded,我们得到的解密后的字符串是 JWT(Json Web Token)。

要配置 CommonCrypto,请参考链接: 如何在 SWIFT3 中使用 CommonCrypto?

https://www.devglan.com/online-tools/aes-encryption-decryption https://jwt.io/

导入UIKit 导入CommonCrypto

类ViewController:UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    testAES()
}


func testAES()  {

    let testMessSr  = "74oIlwieBbxGvHjibQoSxWWAL0zNfy7yaQboYizPc+ouMsAkXbvMNb0RagXklA2zOlo29J7Zr7zFiOalBfKSvV3pzw7KWCtTMw16SNeOIRWYpSWNcNXxgoQR7Jy33s0JP8elQCo/6G+inivE2hm3kl7QTr/Jz5bx/ho0LmWrirn/L4WAJlDFHue23vjhrKGIfEvwIdWHPNh1qd+hCY4pQA==:NjkzQUU1MkM5RTZERjNFMA=="

    //Here I'm separating iv from base64encoded string as while decrypting theres no need of iv
    let splitStr = testMessSr.components(separatedBy: ":")

    print("base64EncodedString: \(splitStr[0])")
    print("iv: \(splitStr[1])")

    let base64EncodedString = splitStr[0]

    let message = Data(base64Encoded: base64EncodedString ,options : Data.Base64DecodingOptions.ignoreUnknownCharacters)
    let key         = "EE99C7A159003D36"
    let ivString     = "693AE52C9E6DF3E0"   // 16 bytes for AES128

   // let messageData = message.data(using:String.Encoding.utf8)!
    let keyData     = key.data(using: .utf8)!
    let ivData      = ivString.data(using: .utf8)!

    let decryptedData = message?.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCDecrypt)
    print("decryptedData: \(decryptedData)")
    let decryptedString = String(data: decryptedData as! Data, encoding: String.Encoding.utf8)
    print("decryptedString: \(decryptedString)") //JWT token

}

}

扩展数据{

func aesEncrypt( keyData: Data, ivData: Data, operation: Int) -> Data {
    let dataLength = self.count
    let cryptLength  = size_t(dataLength + kCCBlockSizeAES128)
    var cryptData = Data(count:cryptLength)

    let keyLength = size_t(kCCKeySizeAES128)
    let options = CCOptions(kCCOptionPKCS7Padding)


    var numBytesEncrypted :size_t = 0

    let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
        self.withUnsafeBytes {dataBytes in
            ivData.withUnsafeBytes {ivBytes in
                keyData.withUnsafeBytes {keyBytes in
                    CCCrypt(CCOperation(operation),
                            CCAlgorithm(kCCAlgorithmAES),
                            options,
                            keyBytes, keyLength,
                            ivBytes,
                            dataBytes, dataLength,
                            cryptBytes, cryptLength,
                            &numBytesEncrypted)
                }
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)

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

    return cryptData;
}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接