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:
strPassword
is the unencrypted password, like "secret"
abPlain
is a byte array with the hex values of strPassword {73, 65, 63, 72, 65, 74, 02, 02}
rpmPassword
is a string of random characters.
rpmPasswordAsData
is the objective-C only representation of rpmPassword as NSData using UTF8 encoding
abPassword
is a byte array with the values of rpmPassword
- 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:
- Generating the key, either in the call to, or code of
TDEAKeyForPassword
- 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!