我一直用这个把头撞在墙上。我需要对我的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编码字符串都不匹配。
答案 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)