Search code examples
iphoneencryption3des

iPhone 3DES encryption key length issue


I have been banging my head on a wall with this one. I need to code my iPhone application to encrypt a 4 digit "pin" using 3DES in ECB mode for transmission to a webservice which I believe is written in .NET.

+ (NSData *)TripleDESEncryptWithKey:(NSString *)key dataToEncrypt:(NSData*)encryptData {
NSLog(@"kCCKeySize3DES=%d", kCCKeySize3DES);
char keyBuffer[kCCKeySize3DES+1]; // room for terminator (unused)
bzero( keyBuffer, sizeof(keyBuffer) ); // fill with zeroes (for padding)

[key getCString: keyBuffer maxLength: sizeof(keyBuffer) encoding: NSUTF8StringEncoding];

// encrypts in-place, since this is a mutable data object
size_t numBytesEncrypted = 0;

size_t returnLength = ([encryptData length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);

// NSMutableData* returnBuffer = [NSMutableData dataWithLength:returnLength];
char* returnBuffer = malloc(returnLength * sizeof(uint8_t) );

CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt, kCCAlgorithm3DES , kCCOptionECBMode,
                                 keyBuffer, kCCKeySize3DES, nil,
                                 [encryptData bytes], [encryptData length], 
                                 returnBuffer, returnLength,
                                 &numBytesEncrypted);

if (ccStatus == kCCParamError) NSLog(@"PARAM ERROR");
else if (ccStatus == kCCBufferTooSmall) NSLog(@"BUFFER TOO SMALL");
else if (ccStatus == kCCMemoryFailure) NSLog(@"MEMORY FAILURE");
else if (ccStatus == kCCAlignmentError) NSLog(@"ALIGNMENT");
else if (ccStatus == kCCDecodeError) NSLog(@"DECODE ERROR");
else if (ccStatus == kCCUnimplemented) NSLog(@"UNIMPLEMENTED");

if(ccStatus == kCCSuccess) {
    NSLog(@"TripleDESEncryptWithKey encrypted: %@", [NSData dataWithBytes:returnBuffer length:numBytesEncrypted]);
    return [NSData dataWithBytes:returnBuffer length:numBytesEncrypted];
}
else 
    return nil;
} }

I do get a value encrypted using the above code, however it does not match the value from the .NET web service.

I believe the issue is that the encryption key I have been supplied by the web service developers is 48 characters long.

I see that the iPhone SDK constant "kCCKeySize3DES" is 24. So I SUSPECT, but don't know, that the commoncrypto API call is only using the first 24 characters of the supplied key.

Is this correct?

Is there ANY way I can get this to generate the correct encrypted pin? I have output the data bytes from the encryption PRIOR to base64 encoding it and have attempted to match this against those generated from the .NET code (with the help of a .NET developer who sent the byte array output to me). Neither the non-base64 encoded byte array nor the final base64 encoded strings match.


Solution

  • 3DES is a symmetric block cipher. Using a 24-byte key, 3DES encrypts an 8-byte block into another 8-byte block. With the same 24-byte key, the encryption is reversible (i.e. you can decrypt).

    The key is an arbitrary sequence of bytes. That's not the same as "characters". In particular, having one of those bytes with value zero is perfectly legal. Similarly, input and output may be arbitrary bytes.

    If the key you were given consists in "characters" then it must be transformed into an appropriate sequence of bytes in some way. Since you got a 48-character "key string" and 48 is exactly 24*2, a plausible guess is that the key is given in hexadecimal notation: see if it contains only digits, and letters from 'a' to 'f'.

    As for padding: 3DES encrypts only 8-byte blocks. When a "message" is to be encrypted and has some length distinct from 8 bytes, then it is customary to format and split and process the message so that it can be encrypted in a number of invocations to 3DES. The two keywords are padding and chaining. Padding is about adding some extra bytes at the end (in such a way that those byte can be unambiguously removed) so that the length is appropriate (e.g. multiple of 8). Chaining is about deciding what exactly goes into each 3DES invocation (simply splitting the padded message into independently encrypted blocks is known as "ECB" and has weaknesses).

    If your PIN code contains 4 digits, then there must be some convention on how these four digits become at least 8 bytes, to be fed to 3DES. If the iPhone behaves similarly to what this man page for MacOS X describes, then your code should not run successfully unless the length of encryptData is a multiple of eight. Which means that the code you do not show, which converts a 4-digit PIN into an 8-byte buffer, already does some non-trivial transformations. For instance, that code might put the four digits into four bytes (using ASCII encoding) and set the four other bytes to zero. Or maybe it fails to do so. Either way, each of the 64 input bits to 3DES is important, and you have to get it exactly in the same way than the server. You should inspect that code as well.