Issue decrypting with CommonCrypto in Swift

孤者浪人 提交于 2019-12-21 19:57:31

问题


I am working in a Swift-only crypt/decrypt Extension for String and NSData, and the crypt part is working based in the answer provided by @Zaph in the linked question: Issue using CCCrypt (CommonCrypt) in Swift

The crypt output was tested using the good old NSData+AESCrypt.m Category in Objective-C

I have been working in the decrypt part with an issue: The code compiles and runs fine, but the result is not the expected text originally encrypted.

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

As you can see the String.encryptStringWithKey() calls the NSData.encryptDataWithKey(). so the Extensions work for both types String and NSData

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

Any help will be appreciated


回答1:


The difference is that NSData+AESCrypt.m is using CBC mode (the default) with an iv of NULL. The code in the question is using ECB mode.

Best practice is to use CBC mode with a random iv. The iv is typically pre-pended to the encrypted data so the decryption can separate the iv and data prior to decryption.

Do not use NSData+AESCrypt.m, it has not been maintained, is a category on NSData, does not support ARC. Consider RNCryptor for Objective-C, it is actively maintained.

This is the change I made in "NSData+AESCrypt.m", method AES256EncryptWithKey: kCCOptionPKCS7Padding + kCCOptionECBMode. I added kCCOptionECBMode, that's all.

Here is the call I made: 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);

Output:

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

Which is the same as from the above code (the same as the previous question):

cryptData = <118a32dc c23f7caa 883abc3c 1c7f0770 e200016b 2737acfa 17bb96fb a02b02a7 c147603b 06acd863 94bb8ff2 6cb14515>

It is just a matter of getting all of the inputs the same: operation, algorithm, options, keyBytes, keyLength, dataBytes, dataLength and iv if non ECB mode. CCCrypt is just a function call, that's all. Put in the same input, get the same output.

Put in NSLog() statements, preferably hex dumps for data and strings. Compare and fix as needed.

Believe it or not, this is the easy part of electronic security.




回答2:


Swift 2.0 AES128 Encryption and Decryption.

Should import the following header into Obj-c bridge header inorder to work

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



回答3:


Swift4 AES128 Decryption and JWT

Note: If iv is used along with key while encrypting the plain text dont use it while decrypting , your issue will be resolved. key and iv length is 16bit

Following code for decryption may help you: Here the encrypted string used for decryption is base64Encoded and the decrypted string which we get is JWT(Json Web Token).

To Configure CommonCrypto refer link: How is CommonCrypto used in SWIFT3?

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

import UIKit import CommonCrypto

class 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

}

}

extension Data{

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;
}

}



来源:https://stackoverflow.com/questions/25776238/issue-decrypting-with-commoncrypto-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!