سؤال

I am currently working on an iPhone app that is a port of a subset of .NET C# functionality. I must log into a server using a 3DES encrypted password (yes, I know that is not an optimal standard, but please bear with me).

So far, however, no joy. I cannot properly replicate the encryption in this C# code. The code in both C# in objective-c shares these common variables:

  1. strPassword is the unencrypted password, like "secret"
  2. abPlain is a byte array with the hex values of strPassword {73, 65, 63, 72, 65, 74, 02, 02}
  3. rpmPassword is a string of random characters.
  4. rpmPasswordAsData is the objective-C only representation of rpmPassword as NSData using UTF8 encoding
  5. abPassword is a byte array with the values of rpmPassword
  6. I've added the code to derive nLen in the objective-C code below

Here's the C# code first:

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)

As you can see, the encrypted value this returns is actually a concatenation of the hex values of abInitV and abCipher.

I have been cribbing from Rob Napier to try and transform this into working objective-c code, but so far, it's not happening. I am producing abInitV and abCipher values of the right length, and I'm also concatenating them properly into abOutput, but I am being rejected by the server when I try to log in.

Here's my objective-c code (the constants are also declared, I promise):

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];

And here are the supporting methods called in the code above:

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

So, if anyone can tell me what I'm doing wrong, I'd sincerely appreciate it. If I had to hazard a guess, I'd assume the problem is in one of 2 places:

  1. Generating the key, either in the call to, or code of TDEAKeyForPassword
  2. Calling CCCrypt.

That said, I've tried every available PRF constant, as well as padding and no padding.

I'm very inexperienced with encryption, so I'd appreciate any help anyone can offer.

Thanks!

هل كانت مفيدة؟

المحلول

First, your code samples seem very different. The C# code is encrypting abPlain. The ObjC code is encrypting abPassword. You indicate that abPlain is static, while abPassword is random. You have some kind of padding going on in the ObjC that I don't see in the C# (maybe this is 0 padding? That's not a standard way to pad.)

If you remove the randomness, then each implementation should return exactly the same result for the same input. So first, hard-code a random value (I generally like to use 0, which is just as random as any other number), and also choose a common abPassword. Then, at each step of the process, make sure that both implementations generate the same result. Most significantly, is abKey the same, and then is abCipher, and finally is abOutput. At one of these points your implementations clearly diverge. (From my quick reading, they seem to be doing very different things all along.)

One thing that is very confusing is how you switch between abPassword and rpmPassword. In one case you pass abPassword, but the length of rpmPasswordAsData. That feels like a place where it's easy to make a mistake.

The length of abPassword is unclear. I assume it's a multiple of 8, or else your ccNoPadding is going to blow up.

NSMutableData has an appendData method. That's much easier than your replaceBytesInRange:…

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top