2010-03-16 10 views
6

W tej chwili waliłem głową w ścianę. Muszę zakodować moją aplikację na iPhone'a, aby zaszyfrować 4-cyfrowy "pinezkę" wykorzystującą 3DES w trybie ECB do transmisji do usługi internetowej, która moim zdaniem jest napisana w .NET.Problem z kluczem szyfrowania 3DES 3D iPhone'a

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

zrobić uzyskać wartość zaszyfrowany przy użyciu powyższego kodu, jednak nie pasuje do wartości z usługi .NET internetowej.

Uważam, że problem polega na tym, że klucz szyfrujący, który dostarczyłem programistom usług internetowych, ma długość 48 znaków.

Widzę, że stała SDK iPhone'a "kCCKeySize3DES" wynosi 24. Więc PODEJMUJĘ, ale nie wiem, że wywołanie API commoncrypto używa tylko pierwszych 24 znaków dostarczonego klucza.

Czy to prawda?

Czy istnieje DOWOLNY sposób, w jaki mogę uzyskać to, aby wygenerować poprawną zaszyfrowaną szpilkę? Mam dane wyjściowe bajtów z szyfrowania PRIOR do kodowania base64 i próbowałem dopasować to do tych wygenerowanych z kodu .NET (przy pomocy programisty .NET, który wysłał mi dane wyjściowe tablicy bajtów). Ani kodowana macierzowa macierz base64, ani końcowe łańcuchy kodowane base64 nie pasują do siebie.

Odpowiedz

3

3DES to symetryczny szyfr blokowy. Przy użyciu klucza 24-bajtowego algorytm 3DES szyfruje blok 8-bajtowy w kolejny 8-bajtowy blok. Przy użyciu tego samego 24-bajtowego klucza szyfrowanie jest odwracalne (tzn. Można odszyfrować).

Klawisz jest dowolną sekwencją bajtów. To nie to samo co "postacie". W szczególności posiadanie jednego z tych bajtów o wartości zero jest całkowicie legalne. Podobnie, dane wejściowe i wyjściowe mogą być dowolnymi bajtami.

Jeśli podany klucz składa się z "znaków", to musi zostać przekształcony w odpowiednią sekwencję bajtów w jakiś sposób. Ponieważ masz 48-znakowy "kluczowy ciąg", a 48 to dokładnie 24 * 2, prawdopodobne jest, że klucz jest podany w zapisie heksadecymalnym: zobacz, czy zawiera tylko cyfry i litery od "a" do "f".

Co do wypełnienia: 3DES szyfruje tylko 8-bajtowe bloki. Kiedy "wiadomość" ma zostać zaszyfrowana i ma długość inną niż 8 bajtów, to zwykle jest to formatowanie i dzielenie i przetwarzanie wiadomości, aby można było zaszyfrować ją w szeregu wywołań do 3DES. Te dwa słowa kluczowe to wypełnianie i łańcuchowe. Wypełnienie polega na dodaniu na koniec dodatkowych bajtów (w taki sposób, że bajt można jednoznacznie usunąć), tak aby długość była odpowiednia (na przykład wielokrotność 8). Łańcuchowanie polega na decydowaniu o tym, co dokładnie wchodzi w każdą inwokację 3DES (po prostu dzielenie wyściełanej wiadomości na niezależnie zaszyfrowane bloki jest znane jako "ECB" i ma słabe punkty).

Jeśli twój kod PIN zawiera 4 cyfry, musi istnieć jakaś konwencja dotycząca sposobu, w jaki te cztery cyfry stają się co najmniej 8 bajtami, które mają zostać przesłane do 3DES. Jeśli iPhone zachowuje się podobnie do tego, co opisuje ten man page for MacOS X, Twój kod nie powinien działać poprawnie, chyba że długość encryptData jest wielokrotnością ośmiu. Co oznacza, że ​​kod, który nie jest wyświetlany, który przekształca 4-cyfrowy kod PIN w 8-bajtowy bufor, dokonuje już pewnych nietrywialnych przekształceń. Na przykład, ten kod może umieścić cztery cyfry na cztery bajty (używając kodowania ASCII) i ustawić cztery inne bajty na zero. A może tego nie robi.Tak czy inaczej, każdy z 64 bitów wejściowych do 3DES jest ważny i musisz go uzyskać dokładnie w ten sam sposób, co serwer. Powinieneś również sprawdzić ten kod.

+0

Dziękuję Thomasowi za szczegółową odpowiedź tutaj. Pojęcia, o których wspomniałeś, są dokładnie tym, do czego doszedłem, aby przyjrzeć się bardziej szczegółowo. Miałem szczęście, że miałem dostęp do wersji .NET kodu szyfrowania i to dało mi cenne wskazówki. 4 cyfrowy PIN został zamieniony na 8-cyfrową wartość, która ma zostać wprowadzona do algorytmu 3DES przez dodanie 4 0-tych. Dodałem kod do tej odpowiedzi, ale oznaczono jako rozwiązanie, ponieważ twoja wiedza była całkowicie poprawna. –

1

Może potrzebujesz podkładki? Spróbuj ustawić następujące opcje:

(kCCOptionPKCS7Padding | kCCOptionECBMode) 
+0

Dzięki za sugestię, ale powiedziano mi, że kod .NET ustawia dopełnienie do "PaddingMode.None". Jest to więc moje założenie, że nie potrzebuję pad z aplikacji na iPhone'a. Oczywiście mogę się mylić! –

+0

Czy masz dostęp do kodu .Net? Jeśli to zrobisz, sugeruję, aby przeczytać go, aby odwrócić inżynier rzeczywistego szyfrowania. –

2

cóż, udało mi się rozwiązać to z dużą ilością czytania i komentarzy tutaj na stackoverflow. Tam, gdzie kilka problemów. Klucz, jaki otrzymałem od programistów .NET, liczył 48 znaków. To oczywiście musi być odczytywane jako ciąg szesnastkowy i zamieniane na 24 znaki.

Dodałem kod, aby to zrobić, a pełna procedura jest następująca. Nie jestem pewien, czy to będzie użyteczne, ponieważ jest to dość specyficzne dla naszej realizacji.

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

Kod dla strToChar jest następujący:

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

Mam nadzieję, że ktoś pomoże ...

Powiązane problemy