6

我一直在用这个把头撞到墙上。我需要对我的 iPhone 应用程序进行编码,以在 ECB 模式下使用 3DES 加密一个 4 位“pin”,以便传输到我认为是用 .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 个字符。

它是否正确?

有什么方法可以让我生成正确的加密密码?我已将加密之前的数据字节输出到 base64 编码,并尝试将其与 .NET 代码生成的字节进行匹配(在将字节数组输出发送给我的 .NET 开发人员的帮助下)。非 base64 编码的字节数组和最终的 base64 编码字符串都不匹配。

4

3 回答 3

4

3DES 是一种对称分组密码。使用 24 字节的密钥,3DES 将一个 8 字节的块加密为另一个 8 字节的块。使用相同的 24 字节密钥,加密是可逆的(即您可以解密)。

密钥是任意字节序列。这与“字符”不同。特别是,其中一个字节的值为 0 是完全合法的。类似地,输入和输出可以是任意字节。

如果您获得的密钥包含在“字符”中,那么它必须以某种方式转换为适当的字节序列。由于您有一个 48 个字符的“密钥字符串”并且 48 正好是 24*2,因此一个合理的猜测是密钥以十六进制表示法给出:查看它是否仅包含数字,以及从 'a' 到 'f' 的字母。

至于填充:3DES 仅加密 8 字节块。当要对“消息”进行加密并且具有与 8 字节不同的长度时,通常会格式化、拆分和处理消息,以便可以在多次调用 3DES 时对其进行加密。这两个关键字是paddingchaining。填充是关于在末尾添加一些额外的字节(以这样的方式可以明确删除这些字节),以便长度合适(例如 8 的倍数)。链接是关于确定每个 3DES 调用的确切内容(简单地将填充消息拆分为独立加密的块被称为“ECB”并且有弱点)。

如果您的 PIN 码包含 4 位数字,则必须对这四位数字如何变成至少 8 个字节有一些约定,以提供给 3DES。如果 iPhone 的行为与MacOS X 的手册页所描述的类似,那么您的代码不应成功运行,除非 的长度encryptData是 8 的倍数。这意味着您未显示的将 4 位 PIN 转换为 8 字节缓冲区的代码已经进行了一些重要的转换。例如,该代码可能将四个数字放入四个字节(使用 ASCII 编码)并将其他四个字节设置为零。或者它可能没有这样做。无论哪种方式,3DES 的 64 个输入位中的每一个都很重要,并且您必须以与服务器完全相同的方式获取它。您还应该检查该代码。

于 2010-03-16T15:39:21.157 回答
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); }

我希望这可以帮助别人...

于 2010-03-16T17:10:52.790 回答
1

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

(kCCOptionPKCS7Padding | kCCOptionECBMode)
于 2010-03-16T13:55:26.940 回答