2012-11-09 71 views
10

Tôi đang cố gắng mã hóa một số văn bản bằng thuật toán AES trên cả nền tảng Android và IPhone. Vấn đề của tôi là, thậm chí sử dụng cùng một thuật toán mã hóa/giải mã (AES-128) và các biến cố định tương tự (khóa, IV, chế độ), tôi nhận được kết quả khác nhau trên cả hai nền tảng. Tôi bao gồm các mẫu mã từ cả hai nền tảng, mà tôi đang sử dụng để kiểm tra mã hóa/giải mã. Tôi sẽ đánh giá cao một số trợ giúp trong việc xác định những gì tôi đang làm sai.Mã hóa bằng AES-128 trong Android và IPhone (Kết quả khác nhau)

  • chính: “123456789abcdefg”
  • IV: “1111111111111111”
  • Plain Text: “HelloThere”
  • Mode: “AES/CBC/NoPadding”

Mã 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) Mã số:

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

Một số tài liệu tham khảo của tôi:

+0

Những 'mã hóa() 'phương pháp bạn đang sử dụng? Câu hỏi của bạn là một mớ hỗn độn. Chỉ cung cấp mã cần thiết. – erickson

+0

bạn có thể sử dụng giải pháp này, nó làm việc cho tôi: [nhập liên kết mô tả ở đây] [1] [1]: http://stackoverflow.com/questions/17535918/aes-gets-different-results -in-ios-and-java? answerertab = active # tab-top – salimido

+0

Tại sao bạn cần dữ liệu được mã hóa bằng nhau? Có lẽ bạn sẽ làm cho mã của bạn kém an toàn hơn mà không có lý do. Trong hầu hết các trường hợp sử dụng, chỉ dữ liệu được giải mã phải bằng nhau. Xem crypto.stackexchange.com/q/5094 –

Trả lời

3

Trên Android, bạn đang sử dụng getBytes(). Đây là lỗi vì điều đó có nghĩa là bạn đang sử dụng bộ ký tự mặc định thay vì bộ ký tự đã biết. Sử dụng getBytes("UTF-8") thay vào đó để bạn biết chính xác bạn sẽ nhận được những byte nào.

Tôi không biết tương đương với Mục tiêu-C, nhưng không dựa vào mặc định. Chỉ định rõ ràng UTF-8 khi chuyển đổi chuỗi thành byte. Bằng cách đó bạn sẽ nhận được cùng một byte trên cả hai mặt.

Tôi cũng lưu ý rằng bạn đang sử dụng MD5 trong mã Mục tiêu-C nhưng không phải trong mã Android. Đây có phải là cố ý không?

7

Điều này khiến tôi không ngạc nhiên khi bạn nhận được các kết quả khác nhau.

Vấn đề của bạn là bạn sử dụng sai mục đích SHA1PRNG để tìm nguồn gốc chính. AFAIK không có tiêu chuẩn chung về cách SHA1PRNG hoạt động trong nội bộ. AFAIR ngay cả kết quả triển khai J2SE và Bouncycaste cũng có kết quả khác nhau bằng cách sử dụng cùng một hạt giống.

Do đó việc triển khai getRawKey(byte[] seed) của bạn sẽ tạo cho bạn một khóa ngẫu nhiên. Nếu bạn sử dụng khóa để mã hóa, bạn sẽ nhận được kết quả phụ thuộc vào khóa đó. Vì khóa là ngẫu nhiên, bạn sẽ không nhận được cùng một khóa trên iOS và do đó bạn đang nhận được một kết quả khác.

Nếu bạn muốn có một hàm dẫn xuất quan trọng, hãy sử dụng một hàm như PBKDF2 được chuẩn hóa gần như hoàn toàn liên quan đến dẫn xuất khóa.

+1

+1 - khóa có trong tên, số ngẫu nhiên psuedo máy phát điện. Các triển khai khác nhau không cho kết quả tương tự, ngay cả khi bạn sử dụng cùng một hạt giống. – Qwerky

+0

Trước hết tôi muốn cảm ơn vì phản hồi của bạn nhưng tôi đánh giá cao nỗ lực của bạn nếu bạn có thể cung cấp thêm chi tiết hoặc ví dụ vì nó hơi khó hiểu đối với tôi –

+0

Xem câu trả lời cập nhật về triển khai getRawKey (..) của bạn. – Robert

2

Xem câu trả lời của tôi cho password-based AES encryption, vì bạn đang sử dụng "hạt giống" của mình một cách hiệu quả làm mật khẩu. (Chỉ cần thay đổi độ dài khóa 256 đến 128, nếu đó là những gì bạn muốn.)

Cố gắng tạo khóa giống nhau bằng cách gieo một DRBG với cùng giá trị không đáng tin cậy.

Tiếp theo, bạn không sử dụng CBC hoặc IV trong mã hóa Android của mình. Ví dụ của tôi cũng cho thấy cách thực hiện điều đó đúng cách. Nhân tiện, bạn cần tạo một IV mới cho mỗi thư bạn mã hóa, như ví dụ của tôi hiển thị và gửi nó cùng với văn bản mã hóa. Nếu không, không có vấn đề gì khi sử dụng CBC.

0

Nếu bạn muốn có ví dụ về mã tương thích cho Android và iPhone, hãy xem RNCryptor library cho iOS và JNCryptor library dành cho Java/Android.

Cả hai dự án đều là nguồn mở và có chung một định dạng dữ liệu. Trong các thư viện này, AES 256-bit được sử dụng, tuy nhiên nó sẽ là tầm thường để thích ứng với mã nếu cần thiết để hỗ trợ AES 128 bit.

Theo câu trả lời được chấp nhận, cả hai thư viện đều sử dụng PBKDF2.

14

Đối với iPhone Tôi đã từng AESCrypt-ObjC, và dành cho Android sử dụng mã này:

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 Không sử dụng IV cố định. http://crypto.stackexchange.com/q/5094 –

+1

@Chris Điều này được thực hiện để căn chỉnh mã Android với Thư viện iOS AESCrypt-ObjC. Thêm AESCrypt-ObjC không được tôi bảo trì. – Dimentar

Các vấn đề liên quan