2012-11-09 127 views
10

我想在Android和iPhone平臺上使用AES算法對一些文本進行加密。我的問題是,即使使用相同的加密/解密算法(AES-128)和相同的固定變量(鍵,IV,模式),我在兩個平臺上都得到不同的結果。我包含兩個平臺的代碼示例,我正在使用它來測試加密/解密。我很感謝在確定我做錯了什麼。在Android和iPhone上使用AES-128進行加密(不同結果)

  • 鍵:「123456789ABCDEFG」
  • IV:「1111111111111111」
  • 純文本:「HelloThere」
  • 模式:「AES/CBC/NoPadding」

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的)代碼:

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

我的一些參考資料:

+0

哪'加密?你的問題很混亂。只提供基本代碼。 – erickson

+0

您可以使用此解決方案,它爲我工作:[此處輸入鏈接的描述] [1] [1]:http://stackoverflow.com/questions/17535918/aes-gets-different-results -in-ios-and-java?answertab = active#tab-top – salimido

+0

爲什麼你需要加密的數據是平等的?也許你會沒有理由讓你的代碼更安全。在大多數情況下,只有解密數據必須相同。請參閱crypto.stackexchange.com/q/5094 –

回答

3

在Android上,您正在使用getBytes()。這是一個錯誤,因爲這意味着您使用默認字符集而不是已知的字符集。請使用getBytes("UTF-8"),以便您確切知道要獲取哪些字節。

我不知道Objective-C的等價物,但不依賴於默認值。將字符串轉換爲字節時明確指定UTF-8。這樣你就可以在兩邊獲得相同的字節。

我還注意到你在Objective-C代碼中使用MD5,但在Android代碼中沒有。這是故意的嗎?

7

這讓我難怪你會得到不同的結果。

你的問題是,你濫用SHA1PRNG密鑰派生。 AFAIK沒有共同的標準,SHA1PRNG是如何在內部工作的。 AFAIR甚至J2SE和Bouncycaste實現使用相同的種子輸出不同的結果。

因此,您的getRawKey(byte[] seed)的執行將生成一個隨機密鑰。如果你使用密鑰進行加密,你會得到一個依賴於該密鑰的結果。由於密鑰是隨機的,你不會在iOS上獲得相同的密鑰,因此你會得到不同的結果。

如果你想要一個密鑰導出函數,使用像PBKDF2這樣的函數對於密鑰導出來說幾乎是完全標準化的。

+1

+1 - 密鑰在名稱中,psuedo隨機數發電機。即使使用相同的種子,不同的實現也不會給出相同的結果。 – Qwerky

+0

首先,我想感謝您的反饋,但是我感謝您的努力,如果您可以提供更多詳細信息或示例,因爲它對我有點困惑 –

+0

請參閱關於您的getRawKey(..)實現的最新答案。確切地說,是 – Robert

2

查看我對password-based AES encryption,的回答,因爲您正在有效地使用您的「種子」作爲密碼。 (只要將密鑰長度從256更改爲128,如果這就是你想要的。)

嘗試通過爲相同值分種DRBG來生成相同密鑰是不可靠的。

接下來,您並未在您的Android加密中使用CBC或IV。我的例子顯示瞭如何正確地做到這一點。順便說一下,您需要爲您加密的每封郵件生成一個新的IV,如我的示例所示,並將其與密文一起發送。否則,使用CBC沒有意義。

0

如果您需要Android和iPhone兼容代碼的示例,請參閱適用於iOS的RNCryptor library和適用於Java/Android的JNCryptor library

這兩個項目都是開源的,共享一個通用的數據格式。在這些庫中,使用了AES 256位,但如果需要修改代碼以支持128位AES,那將是微不足道的。

根據公認的答案,這兩個庫都使用PBKDF2。

14

對於iPhone我用AESCrypt-ObjC,併爲Android使用此代碼:()`方法是您使用

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請勿使用固定的IV。 http://crypto.stackexchange.com/q/5094 –

+1

@Chris這是爲了使Android代碼與AESCrypt-ObjC iOS庫保持一致。添加AESCrypt-ObjC不會被我維護。 – Dimentar

相關問題