2009-07-15 8 views
8

Ho passato due giorni su questo fino ad ora e ho passato in rassegna ogni fonte a mia disposizione, quindi questa è l'ultima risorsa.Problemi di decrittografia in C# qualcosa criptato su iPhone usando RSA

Ho un certificato X509 con chiave pubblica che ho memorizzato nel portachiavi dell'iPhone (solo simulatore a questo punto). Sul lato ASP.NET, ho ottenuto il certificato nel cert store con una chiave privata. Quando crittografo una stringa sull'iPhone e la decrypt sul server, ottengo un "Dati non validi" CryptographicException. Ho provato il Array.Reverse suggerito nella pagina RSACryptoServiceProvider su un longshot, ma non è stato d'aiuto.

Ho confrontato le stringhe 64 base su entrambi i lati e sono uguali. Ho confrontato gli array di byte grezzi dopo la decodifica e anch'essi sono uguali. Se crittografo sul server utilizzando la chiave pubblica, l'array di byte è diverso dalla versione di iPhone e decifra prontamente utilizzando la chiave privata. La stringa in chiaro in chiaro è di 115 caratteri, quindi è compresa nella limitazione a 256 byte della mia chiave a 2048 bit.

Ecco il metodo di crittografia iPhone (più o meno testualmente metodo s' il CryptoExercise sample appwrapSymmetricKey):

+ (NSData *)encrypt:(NSString *)plainText usingKey:(SecKeyRef)key error:(NSError **)err 
{ 
    size_t cipherBufferSize = SecKeyGetBlockSize(key); 
    uint8_t *cipherBuffer = NULL; 
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t)); 
    memset((void *)cipherBuffer, 0x0, cipherBufferSize); 
    NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding]; 
    OSStatus status = SecKeyEncrypt(key, kSecPaddingNone, 
           (const uint8_t *)[plainTextBytes bytes], 
           [plainTextBytes length], cipherBuffer, 
           &cipherBufferSize); 
    if (status == noErr) 
    { 
     NSData *encryptedBytes = [[[NSData alloc] 
        initWithBytes:(const void *)cipherBuffer 
        length:cipherBufferSize] autorelease]; 
     if (cipherBuffer) 
     { 
      free(cipherBuffer); 
     } 
     NSLog(@"Encrypted text (%d bytes): %@", 
        [encryptedBytes length], [encryptedBytes description]); 
     return encryptedBytes; 
    } 
    else 
    { 
     *err = [NSError errorWithDomain:@"errorDomain" code:status userInfo:nil]; 
     NSLog(@"encrypt:usingKey: Error: %d", status); 
     return nil; 
    } 
} 

Ed ecco il metodo C# decrittazione lato server:

private string Decrypt(string cipherText) 
{ 
    if (clientCert == null) 
    { 
     // Get certificate 
     var store = new X509Store(StoreName.My, StoreLocation.LocalMachine); 
     store.Open(OpenFlags.ReadOnly); 
     foreach (var certificate in store.Certificates) 
     { 
      if (certificate.GetNameInfo(X509NameType.SimpleName, false) == CERT) 
      { 
       clientCert = certificate; 
       break; 
      } 
     } 
    } 

    using (var rsa = (RSACryptoServiceProvider)clientCert.PrivateKey) 
    { 
     try 
     { 
      var encryptedBytes = Convert.FromBase64String(cipherText); 
      var decryptedBytes = rsa.Decrypt(encryptedBytes, false); 
      var plaintext = Encoding.UTF8.GetString(decryptedBytes); 
      return plaintext; 
     } 
     catch (CryptographicException e) 
     { 
      throw(new ApplicationException("Unable to decrypt payload.", e)); 
     } 
    } 
} 

Il mio sospetto è che c'erano alcuni problemi di codifica tra le piattaforme. So che uno è big-endian e l'altro è little-endian ma non so abbastanza da dire quale sia o come superare la differenza. Mac OS X, Windows e iPhone sono tutti little-endian quindi non è questo il problema.

Nuova teoria: se si imposta il riempimento OAEP booleano su falso, il valore predefinito è PKCS # 1 1.5. SecKey ha solo definizioni SecPadding di PKCS1, PKCS1MD2, PKCS1MD5 e PKCS1SHA1. Forse Microsoft PKCS # 1 1.5! = PKCS1 di Apple e quindi il padding sta influenzando l'output binario della crittografia. Ho provato a utilizzare kSecPaddingPKCS1 con il fOAEP impostato su false e ancora non ha funzionato. Apparentemente, kSecPaddingPKCS1 è equivalent a PKCS # 1 1.5. Torna al tavolo da disegno su teorie e hellip;

Altre teorie neo-provato:

  1. Certificate su iPhone (file CER) non è esattamente la stessa di PKCS # 12 fascio sul server (file .pfx) e quindi potrebbe mai funzionare. File .cer installato in diversi store certificati e stringhe crittografate dal server racchiuse in modo corretto;
  2. La conversione in base-64 e l'operazione di POSTing sul server ha provocato stranezze non presenti nel roundtrip della stessa classe, quindi prima ho provato alcuni URLEncoding/Decoding e poi ho postato il binario raw da iPhone, verificato che era uguale e ha gli stessi cattivi dati;
  3. La mia stringa originale era di 125 byte, quindi ho pensato che potesse essere troncata in UTF-8 (long shot), quindi l'ho ritagliata su una stringa di 44 byte senza risultato;
  4. Si guardava indietro nella libreria System.Cryptography per assicurarsi che stavo usando una classe appropriata e ho scoperto `RSAPKCS1KeyExchangeDeformatter`, si è esaltato nei confronti dei nuovi potenziali clienti e si è abbattuto quando si è comportato esattamente allo stesso modo.

Successo!

Si è scoperto che avevo un cruft nel mio portachiavi sul simulatore di iPhone che stava confondendo le acque, per così dire. Ho cancellato il Keychain DB allo ~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db per farlo ricreare e ha funzionato bene. Grazie per tutto il vostro aiuto. Immagina che sarebbe stato qualcosa di semplice ma non ovvio. (Due cose che ho imparato: 1) disinstallare l'app dal simulatore non cancella le voci del portachiavi e 2) iniziare assolutamente fresco periodicamente.)

NOTA: il percorso generico per il file portachiavi dipende dalla versione iOS: ~/Libreria/Supporto applicazioni/iPhone Simulator/[versione] /Libreria/Keychains/keychain-2-debug.db ad es., ~/Libreria/Supporto applicazioni/Simulatore iPhone/4.3/Libreria/Portachiavi/portachiavi-2-debug .db

+0

Tempo per una taglia! – bbrown

risposta

3

Bene ... il primo passo (come dici tu hai fatto) è crittografare gli stessi messaggi con gli stessi vettori di inizializzazione utilizzando sia l'implementazione iPhone che C#. Dovresti ottenere lo stesso risultato. Hai detto di no, quindi c'è un problema.

Ciò significa sia:

  • L'applicazione iPhone di RSA non è corretto.
  • L'implementazione .NET di RSA non è corretta.
  • I file chiave sono diversi (o interpretati in modo diverso).

Suggerirei che i primi due sono improbabili, tuttavia sono lontanamente possibili.

È stato: "Installato file CER in diverse negozio cert e server string crittografati roundtripped bene" ... questo non prova nulla: tutto questo dimostra è che dato un particolare insieme casuale di numeri è possibile crittografare/decodifica con successo su una piattaforma. Non stai garantendo che entrambe le piattaforme vedano lo stesso insieme di numeri casuali.

Quindi suggerisco di portarlo al livello più basso possibile qui. Ispezionare gli ingressi e le uscite dirette (array di byte) della crittografia su entrambe le piattaforme. Se con gli stessi ingressi (binari) non si ottiene lo stesso risultato, si ha un problema con la piattaforma. Penso che sia improbabile, quindi suppongo che scoprirai che le IV sono interpretate in modo diverso.

+0

Grazie per la tua risposta! Non riesco a trovare da nessuna parte nelle librerie .NET o Cocoa in cui è possibile impostare un IV per la crittografia o la decifrazione RSA. Ho pensato che la IV si applicava solo agli algoritmi simmetrici. Anche io non presumo di pensare che 1 o 2 siano possibili, ma non riesco nemmeno a capire che una chiave pubblica del certificato X509 sarebbe interpretata in modo diverso sulle due piattaforme. Ma ho preso il certificato dal telefono e l'ho inserito nel server e ci sono riuscito, quindi non so cosa pensare. Guarderò nei dettagli chiave pubblica all'interno di ciascuna piattaforma per assicurarmi che siano gli stessi, bbrown

+0

ma sono abbastanza sicuro che non dovrei essere sorpreso che la stessa chiave pubblica possa generare output diversi per lo stesso input su diversi passaggi. Questo è lo scopo del padding PKCS # 1, qualcosa confermato da un ingegnere Apple: http://lists.apple.com/archives/apple-cdsa/2009/Jul/msg00032.html – bbrown

+0

Scusate - quando ho detto che la IV ero forse abusando della terminologia - è tardi qui :) Intendevo fondamentalmente la chiave di crittografia: i dati che si ottengono dal metodo ExportParameters in .NET o il contenuto del parametro "chiave" sull'iPhone. Penso che varrebbe la pena assicurarsi che siano uguali (escludendo ovviamente la parte privata). –

-2

Credo che tu abbia risposto tu stesso alla domanda. Il problema si trova sicuramente all'interno dell'endianness.

Questo è un possibile modo di scrivere metodi di conversione a due vie:

short convert_short(short in) 
{ 
short out; 
char *p_in = (char *) ∈ 
char *p_out = (char *) &out; 
p_out[0] = p_in[1]; 
p_out[1] = p_in[0]; 
return out; 
} 

long convert_long(long in) 
{ 
long out; 
char *p_in = (char *) ∈ 
char *p_out = (char *) &out; 
p_out[0] = p_in[3]; 
p_out[1] = p_in[2]; 
p_out[2] = p_in[1]; 
p_out[3] = p_in[0]; 
return out; 
} 

Questa potrebbe essere una buona risorsa per voi (diverso da wikipedia): http://betterexplained.com/articles/understanding-big-and-little-endian-byte-order/

+0

È bello ascoltarlo e grazie per la risposta! Perdona la mia ignoranza, ma in che modo esattamente utilizzerò questi metodi di conversione? Presumo che siano per l'iPhone, quindi dove dovrei collegarlo al mio metodo? – bbrown

+0

Si scopre che l'endianness tra le piattaforme è una reliquia dei giorni PowerPC. Mac OS X, essendo basato sull'architettura x86, è little-endian, come Windows. L'iPhone, sull'architettura ARM, è anche little-endian il più vicino possibile dalle ricerche. – bbrown

+0

L'endianità non sarebbe un problema qui, comunque. Tutte le operazioni coinvolgono byte non elaborati, non numeri a più byte. –

-3

Poiché è possibile controllare entrambi i lati, il mio la raccomandazione (se non si riesce a far funzionare gli algoritmi di crittografia della libreria su entrambe le piattaforme) sarebbe quella di scrivere la crittografia da soli su entrambi i lati, utilizzando lo stesso algoritmo.

In questo modo si ha il controllo e si può eseguire il debug degli interni di crittografia per vedere cosa non funziona.

Si tratta di un ultimo ricorso (ovviamente), ma probabilmente avrebbe preso meno tempo di quanto i tre giorni che si è speso già, e hanno una elevata probabilità di successo

HTH

+4

Con tutto il dovuto rispetto, penso che dovrei usare le rispettive funzionalità di sicurezza dei rispettivi framework invece di far girare il mio. Innanzitutto, la loro implementazione è testata in battaglia e ha superato le revisioni di sicurezza che il mio non avrà. Secondo, sono un programmatore C# e Objective-C con abilità crittografiche dichiaratamente limitate. Terzo, penso di essere molto vicino a questo e che il problema sia solubile. Infine, il numero di domande su SO (e altrove) su questa situazione suggerisce che ci sono molti che sono stati affrontati senza successo e quindi sarebbe bello avere una risoluzione disponibile. – bbrown

+5

Ehm, no. Nessuno sano di mente dovrebbe implementare l'algoritmo RSA a meno che (a) non ce ne sia uno disponibile per la piattaforma, (b) che siano un crittografo professionista esperto, e (c) siano disposti a preventivare un sacco di tempo per revisione del codice e test. Le implementazioni crittografiche sono complicate, soggette a problemi delicati e molto sensibili poiché le persone le stanno attivamente attaccando alla ricerca di exploit. –

0

Sarà questo aiuto ?

Asymmetric Key Encryption w/ .NET & C#

  • Ci scusiamo per il breve post, vincoli di tempo e di tutti. Ad ogni modo, ho visto la tua richiesta di aiuto su Twitter .. questo dimostra come ho fatto questo con PHP e decifrato su .NET, simliar. Ho notato che la tua classe di decifratura è leggermente diversa dalla mia, quindi questo articolo potrebbe aiutarti.
+0

Purtroppo, OpenSSL non è disponibile su iPhone, quindi non mi aiuta. Ma grazie per aver provato! – bbrown

+0

Il punto che stavo facendo era provare la routine di decrittazione per abbinarla alla mia.Ho trovato che il 509Certificate2 era migliore. –

+0

Quello era il mio male. Nell'esempio di codice sopra, c'è una variabile di istanza privata chiamata "clientCert" che è di tipo X509Certificate2. Non sono riuscito a includerlo nell'elenco. Mi dispiace ... – bbrown

1

questa è la mia prima risposta su StackOverflow, quindi per favore perdonami se lo faccio male!

Non riesco a darti una risposta completa, tuttavia ho avuto problemi molto simili quando ho provato ad integrarmi con PHP - sembra che il formato dei file dei certificati Apple sia leggermente diverso da quello che altri software si aspettano (incluso openssl).

Ecco come decifro una firma cifrata in PHP - Io in realtà estrarre il modulo e PK dalla chiave pubblica trasmessa manualmente e l'uso che per la roba RSA, piuttosto che cercare di importare la chiave:

// Public key format in hex (2 hex chars = 1 byte): 
//30480241009b63495644db055437602b983f9a9e63d9af2540653ee91828483c7e302348760994e88097d223b048e42f561046c602405683524f00b4cd3eec7e67259c47e90203010001 
//<IGNORE><--------------------------------------------- MODULUS --------------------------------------------------------------------------><??><PK> 
// We're interested in the modulus and the public key. 
// PK = Public key, probably 65537 

// First, generate the sha1 of the hash string: 
$sha1 = sha1($hashString,true); 

// Unencode the user's public Key: 
$pkstr = base64_decode($publicKey); 
// Skip the <IGNORE> section: 
$a = 4; 
// Find the very last occurrence of \x02\x03 which seperates the modulus from the PK: 
$d = strrpos($pkstr,"\x02\x03"); 
// If something went wrong, give up: 
if ($a == false || $d == false) return false; 
// Extract the modulus and public key: 
$modulus = substr($pkstr,$a,($d-$a)); 
$pk = substr($pkstr,$d+2); 

// 1) Take the $signature from the user 
// 2) Decode it from base64 to binary 
// 3) Convert the binary $pk and $modulus into (very large!) integers (stored in strings in PHP) 
// 4) Run rsa_verify, from http://www.edsko.net/misc/rsa.php 
$unencoded_signature = rsa_verify(base64_decode($signature), binary_to_number($pk), binary_to_number($modulus), "512"); 

//Finally, does the $sha1 we calculated match the $unencoded_signature (less any padding bytes on the end)? 
return ($sha1 == substr($unencoded_signature,-20)); // SHA1 is only 20 bytes, whilst signature is longer than this. 

l'Objective-C che genera questa chiave pubblica è:

NSData * data = [[SecKeyWrapper sharedWrapper] getPublicKeyBits]; 
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Public-Key"]; 
data = [[SecKeyWrapper sharedWrapper] getSignatureBytes:[signatureData dataUsingEncoding:NSUTF8StringEncoding]]; 
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Signature"]; 

Utilizzando SecKeyWrapper da CryptoExercise progetto di esempio di Apple (è possibile visualizzare il file qui: https://developer.apple.com/iphone/library/samplecode/CryptoExercise/listing15.html)

Spero che questo aiuti?

+0

Come suggerito da Jon Grant, ispezionerò personalmente i bit della chiave pubblica, ma il fatto è che l'iPhone sta leggendo un certificato X509 in bundle con l'app (non generato sul telefono stesso) e trovo incredibile che il telefono legga il pubblico digitare in modo diverso. Lo sistemerò controllando i byte, ma sarò terribilmente sorpreso se questo è il caso. – bbrown

Problemi correlati