Encryption using AES-128 in Android and IPhone (Different result)
我尝试在Android和iPhone平台上使用AES算法加密一些文本。我的问题是,即使使用相同的加密/解密算法(aes-128)和相同的固定变量(key、iv、mode),在两个平台上也会得到不同的结果。我包含了两个平台的代码示例,用于测试加密/解密。如果能帮我判断我做错了什么,我将不胜感激。
- 密钥:"123456789abcdefg"
- 四:"1111111111111111"
- 纯文本:"hellothere"
- 模式:"aes/cbc/nopadding"
Android代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | public class Crypto { private final static String HEX ="0123456789ABCDEF"; 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)代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | - (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]; } |
号
我的一些参考资料:
- http://code.google.com/p/aes-encryption-samples/wiki/howtoEncryptWithJava
- http://automagical.rationalmind.net/2009/02/12/aes-interoperability-between-net-and-iphone/
- .NET和iPhone之间的AES互操作性?
对于iPhone,我使用了aescrypt objc,对于Android,使用以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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; } } |
难怪你会得到不同的结果。
您的问题是您使用了错误的sha1prng进行密钥派生。阿法克没有一个共同的标准,一个sha1prng如何内部工作。即使是J2SE和BouncyCast实现也会使用相同的种子输出不同的结果。
因此,您的
如果您想要一个密钥派生函数,那么使用一个类似pbkdf2的函数,它几乎完全标准化了密钥派生。
在Android上,您使用的是
我不知道Objective-C的等价物,但不依赖于默认值。将字符串转换为字节时显式指定UTF-8。这样,您将在两侧获得相同的字节。
我还注意到您在Objective-C代码中使用MD5,但在Android代码中没有。这是故意的吗?
请参阅我对基于密码的AES加密的答案,因为您有效地将"seed"用作密码。(如果需要,只需将密钥长度从256更改为128即可。)
试图通过使用相同的值植入DRBG来生成相同的密钥是不可靠的。
接下来,在你的Android加密中,你没有使用CBC或IV。我的示例也演示了如何正确地执行该操作。顺便说一下,您需要为您加密的每个消息生成一个新的IV,如我的示例所示,并将其与密码文本一起发送。否则,使用CBC没有意义。
如果你想要一个Android和iPhone兼容代码的例子,请看iOS的RnCuor库和Java/Android的JNCHILTER库。
这两个项目都是开放源码的,并且共享一种通用的数据格式。在这些库中,使用了aes 256位,但是如果需要支持128位aes,则很容易修改代码。
根据接受的答案,两个库都使用pbkdf2。