1

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.

ndreisg
  • 1,119
  • 13
  • 33
  • 1
    1. Is the key correct, that is it the same key the data was encrypted with? 2. Is the encryption mode CBC, that is the default for `CCCrypt` but no IV is supplied? 3. The `result` is not large enough it must be large enough to handle the padding an resized using `numBytesDecrypted`. 4.Supply all sample input and output in hex. – zaph May 18 '17 at 11:03
  • Thanks for your help! I just found out that in the original Android app that I'm porting, ECB mode was used. – ndreisg May 18 '17 at 11:48
  • 1
    For your last problem, they are the PKCS7 padding bytes. Check `CCOptions` in my updated code. – OOPer May 18 '17 at 12:07
  • 1
    Notes: 1. DES is not secure and should not be used in new code and has been superseded by AES. 2. Do not use ECB mode, it is not secure, see [ECB mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29), scroll down to the Penguin. Instead use CBC mode with a random IV, just prefix the encrypted data with the IV for use in decryption, it does not need to be secret. – zaph May 18 '17 at 12:08
  • 1
    Have read your UPDATE2. Have you added `result.count = numBytesDecrypted` (#3 in my answer) into your code? – OOPer May 18 '17 at 12:36
  • i had... but somehow it disappeared again ^^ i added it again, now it works without trimming. Thank you very much! – ndreisg May 18 '17 at 12:57

1 Answers1

2

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 "???"
Community
  • 1
  • 1
OOPer
  • 47,149
  • 6
  • 107
  • 142
  • This ignores the encryption mode, ECB or CBC. CBC is the default but there is no IV in the answer code. – zaph May 18 '17 at 11:05
  • @zaph, thanks again, but the encrypted binary contains padding in its size so _add a block size_ is not needed for decrypting, I believe. – OOPer May 18 '17 at 11:23
  • Thanks for your answer! It helped me very much. I now get a result. However my result is still not correct but interestingly the first 8 bytes are correct... will update the question soon :) – ndreisg May 18 '17 at 11:37
  • @ndreisg, you are too early to _accept_ my answer. I'll update my answer including specifying ECB mode. Please check it. – OOPer May 18 '17 at 11:55
  • Right, decryption, my bad. – zaph May 18 '17 at 12:06
  • @zaph, I really appreciate your help. Without your help I couldn't finish this answer. – OOPer May 18 '17 at 12:13