2012-11-09 18 views
10

Sto tentando di crittografare del testo utilizzando l'algoritmo AES su entrambe le piattaforme Android e IPhone. Il mio problema è che, usando lo stesso algoritmo di crittografia/decrittografia (AES-128) e le stesse variabili fisse (chiave, IV, modalità), ottengo risultati diversi su entrambe le piattaforme. Sto includendo esempi di codice da entrambe le piattaforme, che sto usando per testare la crittografia/decrittografia. Gradirei qualche aiuto nel determinare cosa sto facendo male.Crittografia tramite AES-128 in Android e IPhone (risultato diverso)

  • chiave: “123456789ABCDEFG”
  • IV: “1111111111111111”
  • Plain Text: “HelloThere”
  • Modalità: “AES/CBC/NoPadding”

Codice Android:

public class Crypto { 
    private final static String HEX = "ABCDEF"; 

    public static String encrypt(String seed, String cleartext) 
      throws Exception { 
     byte[] rawKey = getRawKey(seed.getBytes()); 
     byte[] result = encrypt(rawKey, cleartext.getBytes()); 
     return toHex(result); 
    } 

    public static String decrypt(String seed, String encrypted) 
      throws Exception { 
     byte[] rawKey = getRawKey(seed.getBytes()); 
     byte[] enc = toByte(encrypted); 
     byte[] result = decrypt(rawKey, enc); 
     return new String(result); 
    } 

    private static byte[] getRawKey(byte[] seed) throws Exception { 
     KeyGenerator kgen = KeyGenerator.getInstance("CBC"); 
     SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 
     sr.setSeed(seed); 
     kgen.init(128, sr); // 192 and 256 bits may not be available 
     SecretKey skey = kgen.generateKey(); 
     byte[] raw = skey.getEncoded(); 
     return raw; 
    } 

    private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { 
     SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); 
     Cipher cipher = Cipher.getInstance("AES"); 
     cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 
     byte[] encrypted = cipher.doFinal(clear); 
     return encrypted; 
    } 

    private static byte[] decrypt(byte[] raw, byte[] encrypted) 
      throws Exception { 
     SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); 
     Cipher cipher = Cipher.getInstance("AES"); 
     cipher.init(Cipher.DECRYPT_MODE, skeySpec); 
     byte[] decrypted = cipher.doFinal(encrypted); 
     return decrypted; 
    } 

    public static String toHex(String txt) { 
     return toHex(txt.getBytes()); 
    } 

    public static String fromHex(String hex) { 
     return new String(toByte(hex)); 
    } 

    public static byte[] toByte(String hexString) { 
     int len = hexString.length()/2; 
     byte[] result = new byte[len]; 
     for (int i = 0; i < len; i++) 
      result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 
        16).byteValue(); 
     return result; 
    } 

    public static String toHex(byte[] buf) { 
     if (buf == null) 
      return ""; 

     StringBuffer result = new StringBuffer(2 * buf.length); 
     for (int i = 0; i < buf.length; i++) { 
      appendHex(result, buf[i]); 
     } 

     return result.toString(); 
    } 

    private static void appendHex(StringBuffer sb, byte b) { 
     sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f)); 
    } 
} 

IPhone (Objective-C) Codice:

- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData { 

    NSData* secretKey = [Cipher md5:cipherKey]; 

    CCCryptorRef cryptor = NULL; 
    CCCryptorStatus status = kCCSuccess; 

    uint8_t iv[kCCBlockSizeAES128]; 
    memset((void *) iv, 0x0, (size_t) sizeof(iv)); 

    status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
         [secretKey bytes], kCCKeySizeAES128, iv, &cryptor); 

    if (status != kCCSuccess) { 
     return nil; 
    } 

    size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length], true); 

    void * buf = malloc(bufsize * sizeof(uint8_t)); 
    memset(buf, 0x0, bufsize); 

    size_t bufused = 0; 
    size_t bytesTotal = 0; 

    status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length], 
         buf, bufsize, &bufused); 

    if (status != kCCSuccess) { 
     free(buf); 
     CCCryptorRelease(cryptor); 
     return nil; 
    } 

    bytesTotal += bufused; 

    status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused); 

    if (status != kCCSuccess) { 
     free(buf); 
     CCCryptorRelease(cryptor); 
     return nil; 
    } 

    bytesTotal += bufused; 

    CCCryptorRelease(cryptor); 

    return [NSData dataWithBytesNoCopy:buf length:bytesTotal]; 
} 

+ (NSData *) md5:(NSString *) stringToHash { 

    const char *src = [stringToHash UTF8String]; 

    unsigned char result[CC_MD5_DIGEST_LENGTH]; 

    CC_MD5(src, strlen(src), result); 

    return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH]; 
} 

Alcuni dei miei riferimenti:

+0

Quali 'cifrare()' metodo stai usando? La tua domanda è un casino. Fornisci solo il codice essenziale. – erickson

+0

è possibile utilizzare questa soluzione, il lavoro per me: [entra Descrizione collegamento qui] [1] [1]: http://stackoverflow.com/questions/17535918/aes-gets-different-results -in-ios-and-java? answertab = active # tab-top – salimido

+0

Perché hai bisogno che i dati crittografati siano uguali? Probabilmente renderai il tuo codice meno sicuro senza motivo. Nella maggior parte dei casi, i dati decrittografati devono essere uguali. Vedere crypto.stackexchange.com/q/5094 –

risposta

3

Su Android, che si sta utilizzando getBytes(). Questo è un errore in quanto significa che stai usando il set di caratteri predefinito piuttosto che un set di caratteri conosciuto. Utilizzare invece getBytes("UTF-8") in modo da sapere esattamente quali byte si otterranno.

Non conosco l'equivalente per Objective-C, ma non si basano sul valore predefinito. Specificare esplicitamente UTF-8 durante la conversione delle stringhe in byte. In questo modo otterrai gli stessi byte su entrambi i lati.

Inoltre, si nota che si sta utilizzando MD5 nel codice Objective-C ma non nel codice Android. È deliberato?

7

Non mi meraviglia che si ottengano risultati diversi.

Il problema è che si utilizza impropriamente un SHA1PRNG per derivazione di chiave. AFAIK non esiste uno standard comune su come un SHA1PRNG funzioni internamente. AFAIR anche l'implementazione di J2SE e Bouncycaste ha prodotto risultati diversi usando lo stesso seme.

Quindi la vostra implementazione del vostro getRawKey(byte[] seed) genererà una chiave casuale. Se si utilizza la chiave per la crittografia, si ottiene un risultato che dipende da quella chiave. Siccome la chiave è casuale, non avrai la stessa chiave su iOS e quindi otterrai un risultato diverso.

Se si desidera che una funzione di derivazione chiave utilizzi una funzione come PBKDF2 è quasi completamente standardizzata per quanto riguarda la derivazione della chiave.

+1

+1 - la chiave è nel nome, numero casuale di psuedo Generatore. Diverse implementazioni non danno lo stesso risultato, anche quando si utilizza lo stesso seme. – Qwerky

+0

Prima di tutto voglio ringraziarti per il tuo feedback, ma apprezzo il tuo sforzo se puoi dare maggiori dettagli o esempi dato che è un po 'confuso per me –

+0

Vedi la mia risposta aggiornata sulla tua implementazione getRawKey (..). – Robert

2

Vedere la mia risposta per password-based AES encryption, poiché, si sta effettivamente utilizzando il "seme" come password. (Basta cambiare la lunghezza della chiave da 256 a 128, se è quello che vuoi.)

Provare a generare la stessa chiave seminando un DRBG con lo stesso valore non è affidabile.

Successivamente, non si utilizza CBC o IV nella crittografia Android. Il mio esempio mostra come farlo anche correttamente. A proposito, è necessario generare una nuova IV per ogni messaggio crittografato, come mostra il mio esempio, e inviarlo insieme al testo cifrato. Altrimenti, non ha senso usare CBC.

0

Se si desidera un esempio di codice compatibile per Android e iPhone, consultare RNCryptor library per iOS e JNCryptor library per Java/Android.

Entrambi i progetti sono open source e condividono un formato dati comune. In queste librerie viene utilizzato AES a 256 bit, tuttavia sarebbe banale adattare il codice se necessario per supportare AES a 128 bit.

Come per la risposta accettata, entrambe le librerie utilizzano PBKDF2.

14

per iPhone ho usato AESCrypt-ObjC, e per Android di questo codice:

public class AESCrypt { 

    private final Cipher cipher; 
    private final SecretKeySpec key; 
    private AlgorithmParameterSpec spec; 


    public AESCrypt(String password) throws Exception 
    { 
    // hash password with SHA-256 and crop the output to 128-bit for key 
    MessageDigest digest = MessageDigest.getInstance("SHA-256"); 
    digest.update(password.getBytes("UTF-8")); 
    byte[] keyBytes = new byte[32]; 
    System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length); 

    cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
    key = new SecretKeySpec(keyBytes, "AES"); 
    spec = getIV(); 
    }  

    public AlgorithmParameterSpec getIV() 
    { 
    byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; 
    IvParameterSpec ivParameterSpec; 
    ivParameterSpec = new IvParameterSpec(iv); 

    return ivParameterSpec; 
    } 

    public String encrypt(String plainText) throws Exception 
    { 
    cipher.init(Cipher.ENCRYPT_MODE, key, spec); 
    byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8")); 
    String encryptedText = new String(Base64.encode(encrypted, Base64.DEFAULT), "UTF-8"); 

    return encryptedText; 
    } 

    public String decrypt(String cryptedText) throws Exception 
    { 
    cipher.init(Cipher.DECRYPT_MODE, key, spec); 
    byte[] bytes = Base64.decode(cryptedText, Base64.DEFAULT); 
    byte[] decrypted = cipher.doFinal(bytes); 
    String decryptedText = new String(decrypted, "UTF-8"); 

    return decryptedText; 
    } 
} 
+3

-1 Non utilizzare un IV fisso. http://crypto.stackexchange.com/q/5094 –

+1

@Chris Questo è stato fatto per allineare il codice Android con la libreria iOS AESCrypt-ObjC. Aggiungi AESCrypt-ObjC non è maintaing da me. – Dimentar

Problemi correlati