Problem with iPhone 3DES Encryption Key Length

I hit my head against the wall with this. I need to encode an iPhone app to encrypt a four-digit “pin” using 3DES in ECB mode for transmission to a web service that I think 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 get a value encrypted using the above code, however it does not match the value of the .NET web service.

I believe the problem is that the encryption key I provided to the web service developers is 48 characters.

I see that the iPhone SDK constant "kCCKeySize3DES" is 24. Therefore, I am SUSPECT, but I don’t know that the commoncrypto API call uses only the first 24 characters of the provided key.

Is it correct?

Is there any way to get this to create the correct encrypted output? I am outputting data bytes from PRIOR encryption in base64, encoding it, and tried to match it with those generated from .NET code (using the .NET developer who sent me an array of bytes). Neither base64 encoded byte array nor base64 encoded trailing strings match.

+6
iphone encryption 3des
source share
3 answers

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

The key is an arbitrary sequence of bytes. This is not the same as "characters." In particular, having one of these bytes with a value of zero is perfectly legal. Similarly, input and output can be arbitrary bytes.

If the key provided to you consists of "characters", it must be somehow converted to the corresponding sequence of bytes. Since you have a 48-character “key string” and 48 is exactly 24 * 2, the likely guess is that the key is in hexadecimal notation: see if it contains only numbers and letters from “a” to “f "

As for padding: 3DES only encrypts 8-byte blocks. When a "message" must be encrypted and has a length other than 8 bytes, then it is customary to format and split and process the message so that it can be encrypted in a number of calls in 3DES. Two keywords are fill and chain. Padding is adding some extra bytes to the end (so that this byte can be unambiguously deleted) so that the length is suitable (for example, a multiple of 8). A chain is a definition of what exactly happens in each 3DES call (just splitting a message through unencrypted blocks is called "ECB" and has weaknesses).

If your PIN code contains 4 digits, then there must be some agreement on how these four digits should be at least 8 bytes that must be served in 3DES. If the iPhone behaves in the same way that this manual page for MacOS X describes, then your code should not run successfully if the encryptData not a multiple of eight. This means that a code that you don’t show that converts a 4-digit PIN into an 8-byte buffer already performs some non-trivial conversions. For example, this code can put four digits in four bytes (using ASCII encoding) and set the other four bytes to zero. Or maybe it fails. In any case, each of the 64 input bits in 3DES is important, and you should get it just like a server. You should also check this code.

+3
source share

Well, I managed to solve this with a lot of reading and comments here on stackoverflow. Where a few questions. The key I gave the .NET developers was 48 characters. This, of course, had to be read as the sixth line and converted to 24 characters.

I added code to do this, and the full procedure is as follows. I am not sure if this will be useful, as it is very specific to our implementation.

 + (NSString *)doCipher3DES:(NSString *)sTextIn key:(NSString *)sKey { NSMutableData * dTextIn; CCCryptorStatus ccStatus = kCCSuccess; // need to add 4 zeros as sTextIn will be a 4 digit PIN sTextIn = [sTextIn stringByAppendingString:@"0000"]; // convert to data dTextIn = [[sTextIn dataUsingEncoding: NSASCIIStringEncoding] mutableCopy]; // key will be a 48 char hex stream, so process it down to 24 chars const char * bytes = [sKey cStringUsingEncoding: NSUTF8StringEncoding]; NSUInteger length = strlen(bytes); unsigned char * r = (unsigned char *) malloc(length / 2 + 1); unsigned char * index = r; while ((*bytes) && (*(bytes +1))) { *index = strToChar(*bytes, *(bytes +1)); index++; bytes+=2; } *index = '\0'; NSData *dKey = [NSData dataWithBytes: r length: length / 2]; free(r); NSLog(@"doCipher3DES - key: %@", dKey); uint8_t *bufferPtr1 = NULL; size_t bufferPtrSize1 = 0; size_t movedBytes1 = 0; uint8_t iv[kCCBlockSize3DES]; memset((void *) iv, 0x0, (size_t) sizeof(iv)); bufferPtrSize1 = ([sTextIn length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES -1); bufferPtr1 = malloc(bufferPtrSize1 * sizeof(uint8_t)); memset((void *)bufferPtr1, 0x00, bufferPtrSize1); ccStatus = CCCrypt(kCCEncrypt, // CCOperation op kCCAlgorithm3DES, // CCAlgorithm alg kCCOptionECBMode, // CCOptions options (const void *)[dKey bytes], // const void *key kCCKeySize3DES, // size_t keyLength nil, // const void *iv (const void *)[dTextIn bytes], // const void *dataIn [dTextIn length], // size_t dataInLength (void *)bufferPtr1, // void *dataOut bufferPtrSize1, // size_t dataOutAvailable &movedBytes1); // size_t *dataOutMoved 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"); NSString * sResult; NSData *dResult = [NSData dataWithBytes:bufferPtr1 length:movedBytes1]; NSLog(@"doCipher3DES encrypted: %@", dResult); sResult = [Base64 encode:dResult]; return sResult; } 

The code for strToChar is as follows:

 unsigned char strToChar (char a, char b) { char encoder[3] = {'\0','\0','\0'}; encoder[0] = a; encoder[1] = b; return (char) strtol(encoder,NULL,16); } 

I hope this helps someone ...

+2
source share

Perhaps you need to use a gasket? Try setting the following options:

 (kCCOptionPKCS7Padding | kCCOptionECBMode) 
+1
source share

All Articles