2010-03-16 256 views
6

我一直在用這一個撞牆在我的頭上。我需要編寫我的iPhone應用程序來使用ECES模式下的3DES加密一個4位數的「引腳」,以便傳輸到我認爲是用.NET編寫的web服務。iPhone 3DES加密密鑰長度問題

+ (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; 
} } 

我確實使用上面的代碼獲得了一個加密的值,但它與.NET web服務的值不匹配。

我認爲問題在於Web服務開發人員提供的加密密鑰長度爲48個字符。

我看到iPhone SDK常量「kCCKeySize3DES」是24.所以我懷疑,但不知道,commoncrypto API調用只使用提供的鍵的前24個字符。

這是正確的嗎?

有沒有辦法我可以得到這個來產生正確的加密引腳?我已經將加密PRIOR中的數據字節輸出到base64編碼,並試圖將這些數據字節與.NET代碼生成的數據進行匹配(藉助向.NET發送字節數組輸出的.NET開發人員的幫助)。非base64編碼的字節數組和最終的base64編碼字符串都不匹配。

回答

3

3DES是一種對稱分組密碼。使用24字節密鑰,3DES將8字節塊加密爲另一個8字節塊。使用相同的24字節密鑰,加密是可逆的(即可以解密)。

密鑰是任意字節序列。這與「角色」不一樣。特別是,將這些字節中的一個賦值爲零是完全合法的。同樣,輸入和輸出可能是任意字節。

如果您提供的密鑰由「字符」組成,那麼它必須以某種方式轉換爲適當的字節序列。既然你得到了48個字符的「關鍵字符串」,而48個字符恰好是24 * 2,那麼合理的猜測是關鍵字是以十六進制表示的:查看它是否只包含數字,以及從'a'到'f'的字母。

至於填充:3DES只加密8個字節的塊。當「消息」被加密並且具有與8字節不同的一些長度時,通常格式化和分割並處理該消息,以便可以在對3DES的多次調用中對其進行加密。這兩個關鍵字是填充鏈接。填充是在最後添加一些額外的字節(以這種方式可以明確地去除那些字節),以便長度適當(例如8的倍數)。鏈接是關於決定每個3DES調用的具體內容(簡單地將填充的消息拆分爲獨立加密的塊稱爲「ECB」,並具有缺陷)。

如果您的PIN碼包含4位數字,那麼這四個 數字如何變成至少8個字節將被送入3DES,這一定是有約定的。如果iPhone的行爲與man page for MacOS X描述的行爲類似,那麼除非encryptData的長度是8的倍數,否則您的代碼不應成功運行。這意味着你不顯示的代碼將4位PIN轉換爲8字節緩衝區,已經做了一些非平凡的轉換。例如,該代碼可能會將四位數字轉換爲四個字節(使用ASCII編碼)並將另外四個字節設置爲零。或者也許它沒有這樣做。無論哪種方式,3DES的64個輸入位中的每一位都很重要,您必須以與服務器完全相同的方式獲取它。您應該檢查該代碼。

+0

謝謝托馬斯您在這裏的詳細答案。你提到的概念正是我最終看到的更多細節。我很幸運,因爲我可以訪問.NET版本的加密代碼,這給我提供了寶貴的指導。 將4位數的PIN碼變成8位數值,並通過追加4個0來輸入3DES算法。 我將此代碼添加到此答案中,但標記爲解決方案,因爲您的知識完全正確。 – 2010-03-16 17:14:08

1

也許你需要使用填充?嘗試將選項設置爲:

(kCCOptionPKCS7Padding | kCCOptionECBMode) 
+0

感謝您的建議,但是我已經被告知,.NET代碼設置填充到「PaddingMode.None」。因此,我認爲我不需要從iPhone應用程序進行填充。我當然可能錯了! – 2010-03-16 14:34:34

+0

您有權訪問.Net代碼嗎?如果你這樣做,那麼我建議閱讀它來反向​​工程實際使用的加密。 – 2010-03-16 15:28:34

2

好吧,我設法解決這個很多閱讀和評論這裏在stackoverflow。那裏有幾個問題。 .NET開發人員給出的關鍵字是48個字符。這當然需要被讀作一個十六進制字符串,然後轉換爲24個字符。

我添加了代碼來做到這一點,完整的例程如下。我不確定它會有什麼用處,因爲它對我們的實施非常具體。

+ (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; } 

爲strToChar的代碼如下:

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); } 

我希望這可以幫助別人......