iOS 3DES with ECB return half correct data

2019-02-25 06:32发布

问题:

Got a problem with crypting password with 3DES + ECB algo. Here is the code I using:

class func encryptPassword(pass: String) -> String {

        let keyString        = "123456789012345678901234"
        let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafePointer<UInt8>(keyData.bytes)

        let data: NSData! = (pass as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let dataLength    = UInt(data.length)
        let dataBytes     = UnsafePointer<UInt8>(data.bytes)

        var cryptData    = NSMutableData(length: Int(dataLength) + kCCBlockSize3DES)!
        var cryptPointer = UnsafeMutablePointer<UInt8>(cryptData.mutableBytes)
        var cryptLength  = size_t(cryptData.length)

        let keyLength              = size_t(kCCKeySize3DES)
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithm3DES)
        let options:   CCOptions   = UInt32(kCCOptionECBMode | kCCOptionPKCS7Padding)

        var numBytesEncrypted :UInt = 0

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

        var base64cryptString = ""
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            let x: UInt = numBytesEncrypted
            cryptData.length = Int(numBytesEncrypted)
            println("cryptLength = \(numBytesEncrypted),\n cryptData = \(cryptData)\n")

            base64cryptString = cryptData.base64EncodedStringWithOptions(nil)

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

        return base64cryptString
    }

}

That works, but failed verifying. I used online-encryptor for that like http://www.tools4noobs.com/online_tools/encrypt/ Select TripleDES and ECB

For code

let encrypted = Utils.encryptPassword("123456789")

Console shows

cryptData = <1dd50935 b702084b d164ce3e 9427c493>

Online converter shows

1dd50935b702084bf9fbee67c9643874

i.e first 8 bytes are correct but the last ones - not. How could be that? What could be wrong with code?

=========== EDIT ============

As @Artjom said - there is should be zeroes values added to complete block.

This code in start adds zero values:

        // Trim password
        var password = pass.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())

        // Adding null padding
        let count = 8 - countElements(password) % 8
        for i in 1...count {
            password += "\0"
        }

Then use "password" var instead incoming "pass" to generate "data"

And also remove Padding for options

let algoritm:  CCAlgorithm = UInt32(kCCAlgorithm3DES)

Thanks

回答1:

It is probably a different padding being used. DES has a block size of 8 byte. So the first block is 12345678 and the second block is 9. Since DES is a block cipher the plaintext must be padded to the next block size.

The online tool probably uses zero padding or no padding which basically means that the other bytes of the block are set to 0x00. You use in your code on the other hand PKCS#7 padding. Remove the PKCS#7 flag, to see if the output matches.

You will have to do the zero padding yourself if the library doesn't provide it. Fill up the password with \0 bytes until a multiple of the block size is reached during encryption and remove those zero bytes during decryption.

It is not recommended to use encryption without padding.

Also, password are not usually encrypted, but rather hashed with a random salt and multiple iterations. When your user database gets "lost", you don't want the one who found it, to be able to easily reverse the passwords and log in as any user (the assumption being that the encryption key is also easily "loseable"). Using a strong cryptographic hash function on the other hand has the advantage that it is really infeasible to reverse the hash.