iPhone 3DES加密密钥长度问题

时间:2010-03-16 13:38:00

标签: iphone encryption 3des

我一直用这个把头撞在墙上。我需要对我的iPhone应用程序进行编码,以便在ECB模式下使用3DES加密4位“引脚”,以便传输到我认为是用.NET编写的Web服务。

+ (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开发人员的帮助下将字节数组输出发送给我)。非base64编码的字节数组和最终的base64编码字符串都不匹配。

3 个答案:

答案 0 :(得分:4)

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个输入位中的每一个都很重要,您必须以与服务器相同的方式获得它。您也应该检查该代码。

答案 1 :(得分: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); }

我希望这有助于某人...

答案 2 :(得分:1)

也许你需要使用填充?尝试将选项设置为:

(kCCOptionPKCS7Padding | kCCOptionECBMode)