2013-10-31 62 views
3

在理解兩個設備上的密碼/加密器時,我遇到了一個真正的問題。AES加密Android <-> iOS消息長度> 15字節的不同結果

1. 如果我們使用加密AES加密和字符串的charlength在iOS和Android上的消息不超過16大(例如「ABCDEFGHIJKLMNO」),我們使用相同的密鑰將其加密後得到了相同的結果/密碼。

2. 但是,如果需要較長的消息,我們得到的iOS和Android(例如「abcdefghijklmnop」)

我做了很多研究如何得到相同的params用於在兩個設備,並以不同的結果首先我認爲這是安全的。

這裏是加密我的密碼代碼:

public String encode(Context context, String password, String text) 
     throws NoPassGivenException, NoTextGivenException { 
    if (password.length() == 0 || password == null) { 
     throw new NoPassGivenException("Please give Password"); 
    } 

    if (text.length() == 0 || text == null) { 
     throw new NoTextGivenException("Please give text"); 
    } 

    try { 
     SecretKeySpec skeySpec = getKey(password); 
     byte[] clearText = text.getBytes("UTF8"); 


     //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID 
     final byte[] iv = new byte[16]; 
     Arrays.fill(iv, (byte) 0x00); 
     IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 

     // Cipher is not thread safe 
        //EDITED AFTER RIGHT ANSWER FROM 
        //*** Cipher cipher = Cipher.getInstance("AES"); ***// 
        // TO 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 


     cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); 

     String encrypedValue = Base64.encodeToString(
       cipher.doFinal(clearText), Base64.DEFAULT); 
     Log.d(TAG, "Encrypted: " + text + " -> " + encrypedValue); 
     return encrypedValue; 

    } catch (InvalidKeyException e) { 
     e.printStackTrace(); 
    } catch (UnsupportedEncodingException e) { 
     e.printStackTrace(); 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (BadPaddingException e) { 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     e.printStackTrace(); 
    } catch (IllegalBlockSizeException e) { 
     e.printStackTrace(); 
    } catch (InvalidAlgorithmParameterException e) { 
     e.printStackTrace(); 
    } 
    return ""; 
} 


public SecretKeySpec getKey(String password) 
     throws UnsupportedEncodingException { 


    int keyLength = 128; 
    byte[] keyBytes = new byte[keyLength/8]; 
    // explicitly fill with zeros 
    Arrays.fill(keyBytes, (byte) 0x0); 

    // if password is shorter then key length, it will be zero-padded 
    // to key length 
    byte[] passwordBytes = password.getBytes("UTF-8"); 
    int length = passwordBytes.length < keyBytes.length ? passwordBytes.length 
      : keyBytes.length; 
    System.arraycopy(passwordBytes, 0, keyBytes, 0, length); 
    SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); 
    return key; 
} 

這裏是從我的collegue iOS的掛件:

- (NSData *)AES128EncryptWithKey:(NSString *)key { 

    // 'key' should be 32 bytes for AES256, 
    // 16 bytes for AES256, will be null-padded otherwise 
    char keyPtr[kCCKeySizeAES128 + 1]; // room for terminator (unused) 
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) 

    // insert key in char array 
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

    NSUInteger dataLength = [self length]; 
    size_t bufferSize = dataLength + kCCBlockSizeAES128; 
    void *buffer = malloc(bufferSize); 

    size_t numBytesEncrypted = 0; 

    // the encryption method, use always same attributes in android and iPhone (f.e. PKCS7Padding) 
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, 
              kCCAlgorithmAES128, 
              kCCOptionPKCS7Padding, 
              keyPtr, 
              kCCKeySizeAES128, 
              NULL      /* initialization vector (optional) */, 
              [self bytes], dataLength, /* input */ 
              buffer, bufferSize,  /* output */ 
              &numBytesEncrypted); 
    if (cryptStatus == kCCSuccess) { 

     return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 
    } 

    free(buffer); 
    return nil; 
} 

我真的想了解什麼區別可能是,如何避免它。與更大的字符串比15個字符確切的測試給了我一個提示,但我不知道爲什麼:)

謝謝先進的傢伙!

+2

檢查兩個系統上正在使用的填充。不同的填充會導致不同的輸出。不要依賴默認值,而是在兩側明確設置填充。你的第二個代碼片段顯式地設置PKCS7填充。在兩端使用它。 – rossum

+0

呵呵,非常感謝,請給它一個答案'密碼cipher = Cipher.getInstance(「AES/CBC/PKCS7Padding」);' –

+3

你的密鑰派生函數非常弱,而且密鑰可能是強制性的。您應該使用適當的密鑰派生函數,如PBKDF2或bcrypt。您也不應該使用靜態IV,因爲這會破壞CBC模式的某些安全屬性。 – ntoskrnl

回答

7

檢查兩個系統上正在使用哪些填充。不同的填充會導致不同的輸出。不要依賴默認值,而是在兩側明確設置填充。你的第二個代碼片段顯式地設置PKCS7填充。在兩端使用它。

作爲一般規則,不是依靠不同系統之間的默認值。顯式設置IV,模式,填充,隨機數或其他任何需要的。如果即使最細微的細節不匹配,Crypto的設計也會嚴重失敗。

+0

謝謝你做的伎倆:'密碼cipher = Cipher.getInstance(「AES/CBC/PKCS7Padding」)' –

+0

注意非Android Java將其稱爲「PKCS5Padding」而非「PKCS7Padding」。 – ntoskrnl

+0

是的,如果使用PKCS7Padding,速度會變慢。使用AES/CBC/PKCS5Padding在JellyBean及以後版本中將會快得多。 – kroot

相關問題