Search code examples
swiftxcodeencryptionswift3commoncrypto

Decrypting DES with CommonCrypto in Swift 3


I want to decrypt a DES encrypted String with CommonCrypto.

I already imported CommonCrypto with a Bridging Header into my Project.

By try and error I managed to call CCCrypt function and it even returns kCCSuccess.

But after that my result is still empty.

Here is my code:

if let key = "12345678".data(using: .utf8), let data = "inMyOriginalCodeYouWouldSeeADESEncryptedStringHere/ahw==".data(using: .utf8) {
        var numBytesDecrypted: size_t = 0
        var result = Data(capacity: data.count)

        let err = result.withUnsafeMutableBytes {resultBytes in
            data.withUnsafeBytes {dataBytes in
                key.withUnsafeBytes {keyBytes in
                    CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmDES), CCOptions(kCCOptionPKCS7Padding), keyBytes, kCCKeySizeDES, nil, dataBytes, data.count, resultBytes, data.count, &numBytesDecrypted)
                }
            }
        }

        if err != CCCryptorStatus(kCCSuccess) {
            NSLog("Decryption failed! Error: \(err.description)")
        }

        print(numBytesDecrypted)
        print(result)

        return String(data: result, encoding: .utf8) ?? "???"
    }
    return "???"
}

The output of the two print lines currently is:

56
0 bytes

UPDATE:

Corrected code according to accepted answer:

let encrypted = "inMyOriginalCodeYouWouldSeeADESEncryptedStringHere/ahw=="
if let key = "12345678".data(using: .utf8), let data = Data(base64Encoded: encrypted) {
        var numBytesDecrypted: size_t = 0
        var result = Data(count: data.count)

        let err = result.withUnsafeMutableBytes {resultBytes in
            data.withUnsafeBytes {dataBytes in
                key.withUnsafeBytes {keyBytes in
                    CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmDES), CCOptions(kCCOptionECBMode), keyBytes, kCCKeySizeDES, nil, dataBytes, data.count, resultBytes, data.count, &numBytesDecrypted)
                }
            }
        }

        if err != CCCryptorStatus(kCCSuccess) {
            NSLog("Decryption failed! Error: \(err.description)")
        }

        return String(data: result, encoding: .utf8) ?? "???"
}

This code now returns the correct result. There is only one little problem left: the returned result String looks like the following now: "encryptedString\u{08}\u{08}\u{08}\u{08}\u{08}\u{08}\u{08}\u{08}"

How can I get rid of these last 8 bytes?

I tried to to initialise my result like this:

var result = Data(count: data.count - kCCBlockSizeDES)

but then i get a kCCBufferTooSmall error.


UPDATE2:

I now use CCOptions(kCCOptionPKCS7Padding|kCCOptionECBMode) but this only changed the "\u{08}" characters to "\0" characters. So now i simply call .trimmingCharacters(in: CharacterSet.controlCharacters) on my result String before returning it.


Solution

  • When you encrypt or decrypt from String to String, you need 3 steps, as modern encryption algorithms work on only binary data.

    Encoding:

    [original String]
    ↓(encode in UTF-8)
    [original binary]
    ↓(encrypt)
    [encrypted binary]
    ↓(encode in base64)
    [encrypted String]
    

    (I guess you have a base64 encoded String as your == in the encrypted String is suggesting.)

    So, when decoding, you need all these steps in reverse.

    Decoding:

    [encrypted String]
    ↓(decode in base64)
    [encrypted binary]
    ↓(decrypt)
    [original binary]
    ↓(decode in UTF-8)
    [original String]
    

    You are doing the first step of decoding in a wrong way. (See #1 of the code below.)


    One more, when you want to receive data into mutable (var) Data, set count (not only capacity) of the Data. (#2 and #3)


    UPDATED And, as told by zaph, you need to specify IV for CBC mode (default) or use ECB mode (#4).

    Your code should be something like this:

    if
        let key = "12345678".data(using: .utf8),
        let data = Data(base64Encoded: encrypted, options: .ignoreUnknownCharacters) //<-#1
    {
        var numBytesDecrypted: size_t = 0
        var result = Data(count: data.count)    //<-#2
        
        let err = result.withUnsafeMutableBytes {resultBytes in
            data.withUnsafeBytes {dataBytes in
                key.withUnsafeBytes {keyBytes in
                    CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmDES), CCOptions(kCCOptionPKCS7Padding|kCCModeECB), keyBytes, kCCKeySizeDES, nil, dataBytes, data.count, resultBytes, result.count, &numBytesDecrypted) //<-#4
                }
            }
        }
        
        if err != CCCryptorStatus(kCCSuccess) {
            NSLog("Decryption failed! Error: \(err.description)")
        }
        
        print(numBytesDecrypted)
        result.count = numBytesDecrypted //<-#3
        print(result as NSData) //`as NSData` is good for debugging.
        
        return String(data: result, encoding: .utf8) ?? "???"
    }
    return "???"