将c# cryptosys辅助的3DES加密移植到Objective-C CommonCrypto问题

本文关键字:Objective-C 问题 CommonCrypto 加密 3DES cryptosys | 更新日期: 2023-09-27 18:13:09

我目前正在开发一个iPhone应用程序,它是一个。net c#功能子集的移植。我必须使用3DES加密密码登录服务器(是的,我知道这不是最佳标准,但请原谅我)。

然而,到目前为止,没有快乐。我无法在这段c#代码中正确地复制加密。c#和objective-c中的代码共享这些公共变量:

  1. strPassword为未加密的密码,如"secret"
  2. abPlain是一个字节数组,包含strPassword {73, 65, 63, 72, 65, 74, 02, 02}
  3. 的十六进制值。
  4. rpmPassword为随机字符串。
  5. rpmPasswordAsData是使用UTF8编码的rpmPassword作为NSData的object - c唯一表示
  6. abPassword是包含rpmPassword
  7. 值的字节数组。
  8. 我在
  9. 下面的objective-C代码中添加了导出nLen的代码

下面是c#代码:

static int ITERATIONCOUNT = 2048;
static int KEYBYTES = 24;
static int BLOCKBYTES = 8;
byte[] abInitV = CryptoSysAPI.Rng.NonceBytes(BLOCKBYTES);
byte[] abKey = CryptoSysAPI.Pbe.Kdf2(KEYBYTES, abPassword, abInitV, ITERATIONCOUNT);
CryptoSysAPI.Tdea cipher = CryptoSysAPI.Tdea.Instance();
cipher.InitEncrypt(abKey, Mode.CBC, abInitV);
byte[] abCipher = cipher.Update(abPlain);
abOutput = new byte[abCipher.Length + BLOCKBYTES];
for (int i = 0; i < BLOCKBYTES; i++) abOutput[i] = abInitV[i];
for (int i = 0; i < nLen + nPad; i++) abOutput[BLOCKBYTES + i] = abCipher[i];
return CryptoSysAPI.Cnv.ToHex(abOutput)

可以看到,这个返回的加密值实际上是abInitVabCipher十六进制值的串联。

我一直在模仿Rob Napier,试图将其转换为工作的objective-c代码,但到目前为止,它还没有发生。我正在生成正确长度的abInitVabCipher值,并且我也将它们正确地连接到abOutput中,但是当我尝试登录时,我被服务器拒绝了。

这是我的objective-c代码(常量也声明了,我保证):

int nLen = [strPassword length];
int nPad = ((nLen / BLOCKBYTES) + 1) * BLOCKBYTES - nLen;
NSData *abInitV = [self randomDataOfLength:BLOCKBYTES]; // This is the salthex for the encryption
const unsigned char *abInitVAsBytes = [abInitV bytes];
NSData *abKey = [self TDEAKeyForPassword:strPassword salt:abInitV];
size_t movedBytes = 0;    
NSMutableData *abCipher = [NSMutableData dataWithLength:BLOCKBYTES];
CCCryptorStatus result = CCCrypt(kCCEncrypt,
                                 kCCAlgorithm3DES,
                                 ccNoPadding & kCCModeCBC,
                                 [abKey bytes],
                                 kCCKeySize3DES,
                                 [abInitV bytes],
                                 abPassword,
                                 [rpmPasswordAsData length],
                                 abCipher.mutableBytes,
                                 KEYBYTES,
                                 &movedBytes);
if (result == kCCSuccess)
{
   NSLog(@"abCipher == %@ 'n", [abCipher description] ); 
}
NSMutableData *abOutput = [NSMutableData dataWithCapacity:[abCipher length] + BLOCKBYTES];
const unsigned char *abCipherAsBytes = [abCipher bytes];
for (int i = 0; i < BLOCKBYTES; i++)
{
    [abOutput replaceBytesInRange:NSMakeRange(i, sizeof(abInitVAsBytes[i])) withBytes:&abInitVAsBytes[i]];
}
for (int i = 0; i < nLen + nPad; i++)
{
    [abOutput replaceBytesInRange:NSMakeRange(BLOCKBYTES + i, sizeof(abCipherAsBytes[i])) withBytes:&abCipherAsBytes[i]];        
}
return [EncryptionUtil NSDataToHex:abOutput];

下面是上面代码中调用的支持方法:

+(NSString*) NSDataToHex:(NSData*)data
{
    const unsigned char *dbytes = [data bytes];
    NSMutableString *hexStr =
    [NSMutableString stringWithCapacity:[data length]*2];
    int i;
    for (i = 0; i < [data length]; i++) {
        [hexStr appendFormat:@"%02x ", dbytes[i]];
    }
    return [NSString stringWithString: hexStr];
}
+(NSData*)HexToNSData:(NSString*)hex
{
    NSMutableData* data = [NSMutableData data];
    int idx;
    for (idx = 0; idx+2 <= [hex length]; idx+=2) {
        NSRange range = NSMakeRange(idx, 2);
        NSString* hexStr = [hex substringWithRange:range];
        NSScanner* scanner = [NSScanner scannerWithString:hexStr];
        unsigned int intValue;
        [scanner scanHexInt:&intValue];
        [data appendBytes:&intValue length:1];
    }
    return data;
}
+(NSData *)randomDataOfLength:(size_t)length
{
    NSMutableData *data = [NSMutableData dataWithLength:length];
    int result = SecRandomCopyBytes(kSecRandomDefault,
                                length,
                                data.mutableBytes);
    NSAssert(result == 0, @"Unable to generate random bytes: %d",
         errno);
    return data;
}
+(NSData *)TDEAKeyForPassword:(NSString *)password
                     salt:(NSData *)salt
{
    NSMutableData *
    derivedKey = [NSMutableData dataWithLength:kCCKeySize3DES];
    int result = CCKeyDerivationPBKDF(kCCPBKDF2,            // algorithm
                                  password.UTF8String,  // password
                                  password.length,  // passwordLength
                                  salt.bytes,           // salt
                                  salt.length,          // saltLen
                                  kCCPRFHmacAlgSHA1,    // PRF
                                  ITERATIONCOUNT,         // rounds
                                  derivedKey.mutableBytes, // derivedKey
                                  derivedKey.length); // derivedKeyLen
    return derivedKey;
}

所以,如果有人能告诉我我做错了什么,我将衷心感激。如果我必须大胆猜测,我认为问题在两个地方之一:

  1. TDEAKeyForPassword的调用或代码中生成密钥
  2. 呼叫CCCrypt .

也就是说,我已经尝试了所有可用的PRF常数,以及填充和不填充。

我对加密很没有经验,所以我很感激任何人提供的帮助。

谢谢!

将c# cryptosys辅助的3DES加密移植到Objective-C CommonCrypto问题

首先,您的代码示例看起来非常不同。c#代码正在加密abPlain。ObjC代码正在加密abPassword。您指出abPlain是静态的,而abPassword是随机的。你在ObjC中有一些我在c#中看不到的填充(也许这是0填充?这不是标准的填充方式。)

如果去掉随机性,那么对于相同的输入,每个实现应该返回完全相同的结果。因此,首先,硬编码一个随机值(我通常喜欢使用0,它和其他数字一样随机),并选择一个常见的abPassword。然后,在流程的每一步,确保两个实现生成相同的结果。最重要的是,abKey是相同的,然后是abCipher,最后是abOutput。在其中一点上,你们的实现显然是不同的。(从我的快速阅读来看,他们似乎一直在做非常不同的事情。)

一件非常令人困惑的事情是如何在abPasswordrpmPassword之间切换。在一种情况下,您传递abPassword,但rpmPasswordAsData的长度。那感觉就像一个容易犯错的地方。

abPassword的长度不清楚。我假设它是8的倍数,否则你的ccNoPadding会爆炸。

NSMutableData有一个appendData方法。这比你的replaceBytesInRange:…

简单多了