2010-03-16 15 views
6

He estado golpeando mi cabeza en una pared con este. Necesito codificar la aplicación de mi iPhone para cifrar un "pin" de 4 dígitos usando 3DES en modo ECB para la transmisión a un servicio web que creo que está escrito en .NET.Problema de longitud de clave de cifrado de iPhone 3DES

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

me pongo un valor cifrado usando el código anterior, sin embargo, no coincide con el valor del servicio .NET Web.

Creo que el problema es que la clave de encriptación que me han proporcionado los desarrolladores del servicio web es de 48 caracteres.

Veo que el SDK de iPhone constante "kCCKeySize3DES" es 24. Por lo tanto SOSPECHO, pero no sé, que la llamada de API commoncrypto solo utiliza los primeros 24 caracteres de la clave suministrada.

¿Es esto correcto?

¿Hay CUALQUIER forma en que pueda obtener esto para generar el pin correcto encriptado? He obtenido los bytes de datos del cifrado ANTES de la codificación base64 y he intentado hacer coincidir esto con los generados a partir del código .NET (con la ayuda de un desarrollador .NET que me envió la salida de matriz de bytes). Ni la matriz de bytes codificada no base64 ni las cadenas codificadas base64 final coinciden.

Respuesta

3

3DES es un cifrado de bloque simétrico. Usando una clave de 24 bytes, 3DES encripta un bloque de 8 bytes en otro bloque de 8 bytes. Con la misma clave de 24 bytes, el cifrado es reversible (es decir, puede descifrar).

La clave es una secuencia arbitraria de bytes. Eso no es lo mismo que "personajes". En particular, tener uno de esos bytes con valor cero es perfectamente legal. Del mismo modo, la entrada y la salida pueden ser bytes arbitrarios.

Si la clave que recibió está compuesta por "caracteres", debe transformarse en una secuencia de bytes adecuada de alguna manera. Como obtuvo una "cadena clave" de 48 caracteres y 48 es exactamente 24 * 2, una suposición plausible es que la clave se da en notación hexadecimal: vea si contiene solo dígitos y letras de 'a' a 'f'.

En cuanto al relleno: 3DES encripta solo bloques de 8 bytes. Cuando se va a cifrar un "mensaje" y tiene una longitud distinta de 8 bytes, se acostumbra formatear y dividir y procesar el mensaje para que se pueda cifrar en varias invocaciones a 3DES. Las dos palabras clave son relleno y encadenando. El relleno se trata de agregar algunos bytes adicionales al final (de tal forma que esos bytes puedan eliminarse sin ambigüedades) para que la longitud sea apropiada (por ejemplo, múltiplos de 8). El encadenamiento consiste en decidir qué corresponde exactamente a cada invocación de 3DES (simplemente dividir el mensaje acolchado en bloques cifrados independientemente se conoce como "ECB" y tiene debilidades).

Si su código PIN contiene 4 dígitos, entonces debe haber alguna convención sobre cómo estos cuatro dígitos se convierten en al menos 8 bytes, para alimentar a 3DES. Si el iPhone se comporta de manera similar a lo que describe man page for MacOS X, entonces su código no debería ejecutarse correctamente a menos que la longitud de encryptData sea múltiplo de ocho. Lo que significa que el código que no se muestra, que convierte un PIN de 4 dígitos en un búfer de 8 bytes, ya realiza algunas transformaciones no triviales. Por ejemplo, ese código podría poner los cuatro dígitos en cuatro bytes (usando codificación ASCII) y establecer los otros cuatro bytes en cero. O tal vez no lo hace.De cualquier forma, cada uno de los 64 bits de entrada a 3DES es importante, y debe obtenerlo exactamente de la misma manera que el servidor. Deberías inspeccionar ese código también.

+0

Gracias a Thomas por su respuesta detallada aquí. Los conceptos que mencionas son exactamente lo que terminé investigando con más detalle. Tuve suerte porque tenía acceso a la versión .NET del código de cifrado y eso me dio valiosos consejos. El PIN de 4 dígitos se convirtió en un valor de 8 dígitos para alimentar el algoritmo 3DES al agregar 4 0's. Agregué mi código a esta respuesta, pero marqué como la solución porque su conocimiento era totalmente correcto. –

1

¿Quizás necesites usar relleno? Intente configurar las opciones en:

(kCCOptionPKCS7Padding | kCCOptionECBMode) 
+0

Gracias por la sugerencia, sin embargo, me han dicho que el código .NET establece el acolchado a "PaddingMode.None". Por lo tanto, es una suposición que no necesito rellenar desde la aplicación de iPhone. ¡Puedo estar equivocado, por supuesto! –

+0

¿Tiene acceso al código .Net? Si lo hace, entonces sugiero leerlo para realizar una ingeniería inversa del cifrado real utilizado. –

2

bueno, me las arreglé para resolver esto con mucha lectura y los comentarios aquí en stackoverflow. Hubo varios problemas. La clave que me dieron los desarrolladores de .NET fue de 48 caracteres. Por supuesto, esto debe leerse como una cadena hexadecimal y, en su lugar, debe convertirse en 24 caracteres.

Agregué el código para hacer esto, y la rutina completa es la siguiente. No estoy seguro de que sea útil, ya que es bastante específico para nuestra implementación.

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

El código para strToChar es el siguiente:

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); } 

espero que esto ayude a alguien ...

Cuestiones relacionadas