如何使 Java 和 Objective-C (iPhone) 之间的 AES 加密相同?
Posted
技术标签:
【中文标题】如何使 Java 和 Objective-C (iPhone) 之间的 AES 加密相同?【英文标题】:How can I make my AES encryption identical between Java and Objective-C (iPhone)? 【发布时间】:2011-01-17 19:58:40 【问题描述】:我在 Objective-c 中加密一个字符串,并在 Java 中使用 AES 加密相同的字符串,我看到了一些奇怪的问题。结果的第一部分匹配到某个点,但随后又有所不同,因此当我将结果从 Java 解码到 iPhone 上时,它无法解密。
我使用的源字符串是“现在,这都是什么废话。你知道吗?” 使用“1234567890123456”键
要加密的 Objective-c 代码如下: 注意:它是一个 NSData 类别,因此假设在 NSData 对象上调用该方法,因此“self”包含要加密的字节数据。
- (NSData *)AESEncryptWithKey:(NSString *)key
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess)
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
free(buffer); //free the buffer;
return nil;
而java加密代码是……
public byte[] encryptData(byte[] data, String key)
byte[] encrypted = null;
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] keyBytes = key.getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
catch (Exception e)
logger.log(Level.SEVERE, e.getMessage());
finally
return encrypted;
objective-c 代码的十六进制输出为 -
7a68ea36 8288c73d f7c45d8d 22432577 9693920a 4fae38b2 2e4bdcef 9aeb8afe 69394f3e 1eb62fa7 74da2b5c 8d7b3c89 a295d306 f1f90349 6899ac34 63a6efa0
java 输出是 -
7a68ea36 8288c73d f7c45d8d 22432577 e66b32f9 772b6679 d7c0cb69 037b8740 883f8211 748229f4 723984beb 50b5aea1 f17594c9 fad2d05e e0926805 572156d
如您所见,一切都很好-
7a68ea36 8288c73d f7c45d8d 22432577
我猜我有一些不同的设置,但不知道是什么,我尝试在 java 端在 ECB 和 CBC 之间进行更改,但没有效果。
谁能帮忙!?请……
【问题讨论】:
你们把我从自我维持的噩梦中救了出来……谢谢! 【参考方案1】:既然 CCCrypt 采用 IV,它不使用链接块密码方法(例如 CBC)吗?这与您看到的一致:第一个块是相同的,但在第二个块中,Java 版本应用原始密钥进行加密,但 OSX 版本似乎使用了其他东西。
编辑:
来自here 我看到了一个例子。似乎您需要将 kCCOptionECBMode 传递给 CCCrypt:
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
kCCOptionECBMode, <-- this could help
vkey, //"123456789012345678901234", //key
kCCKeySize3DES,
nil, //"init Vec", //iv,
vplainText, //"Your Name", //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
编辑 2:
我使用了一些命令行来查看哪个是正确的。我想我可以贡献它:
$ echo "Now then and what is this nonsense all about. Do you know?" | openssl enc -aes-128-ecb -K $(echo 1234567890123456 | xxd -p) -iv 0 | xxd
0000000: 7a68 ea36 8288 c73d f7c4 5d8d 2243 2577 zh.6...=..]."C%w
0000010: e66b 32f9 772b 6679 d7c0 cb69 037b 8740 .k2.w+fy...i..@
0000020: 883f 8211 7482 29f4 7239 84be b50b 5aea .?..t.).r9....Z.
0000030: eaa7 519b 65e8 fa26 a1bb de52 083b 478f ..Q.e..&...R.;G.
【讨论】:
我在想,但我将 Java 代码更改为以下... Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); ......但它没有任何区别。除非我需要更改其他内容以使 Java 代码使用 CBC? 绝对准确!太感谢了。我现在可以停止用头撞墙了 :) 再次欢呼。 没问题,很高兴为您提供帮助。我玩弄了openssl,试图弄清楚Java 或Mac 是否合适。我添加了用于生成密码的 openssl 命令行。我希望它可以使用。 Argh 我有同样的问题,相同的目标 C 代码,但我不能再更改 objC 端,因为数据已经加密,你知道如何解决这个更改 java 代码的问题吗?请参阅我的问题:***.com/questions/3092190/… 你们把我从自我维持的噩梦中救了出来……谢谢!【参考方案2】:对于需要这个的其他人,disown 绝对是正确的...在objective-c 中创建crypt 的修改调用如下(注意您需要ECB 模式和填充)...
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
【讨论】:
请注意,出于安全考虑,CBC 模式被认为远远优于 ECB。 ECB 泄露了有关哪些输入块彼此相同的信息,而这种相同的块经常出现在“正常数据”中。 CBC 可以看作是一种数据随机化,它使此类块的出现概率大大降低。 谢谢!我会花一整天的时间来弄清楚这一点。 这有帮助!!接受的答案会给你一个错误,比如“方法调用中的更多参数”。这应该是答案。【参考方案3】:我花了几个星期来解密一个 base64 编码、AES256 加密的字符串。加密由 iPad 上的 CCCrypt (Objective-C) 完成。解密将在 Java 中完成(使用 Bouncy Castle)。
我终于成功了,在这个过程中学到了很多东西。加密代码和上面完全一样(我猜它是从 iPhone 开发者文档中的 Objective-C 示例中获取的)。
CCCrypt() 文档没有提到的是它默认使用 CBC 模式(如果你没有指定类似 kCCOptionECBMode 的选项)。它确实提到了 IV,如果未指定,则默认为全零(因此 IV 将是一个 0x00 的字节数组,长度为 16 个成员)。
使用这两条信息,您可以在 Java 和 OSx/iphone/ipad(CCCrypt) 上使用 CBC(并避免使用安全性较低的 ECB)创建功能相同的加密模块。
Cipher init 函数将 IV 字节数组作为第三个参数:
cipher.init(Cipher.ENCRYPT_MODE, keySpec, IV).
【讨论】:
谢谢你,你拯救了我的一天。 CCCrypt 文档确实谈到了默认使用 CBC —— 至少我很确定我在某个地方看到过。我在任何地方没有看到的是任何提及默认IV的内容。那是缺少的成分-谢谢! 对于其他阅读本文的人......您不能再将 IV 传递给Cipher.init()
。你需要做的是(1)获得一个AlgorithmParameters
对象(使用getInstance()
类工厂方法,(2)将IV存储在参数对象中:params.init(new IvParameterSpec(iv));
和(3)传递params
对象作为cipher.init()
的第三个参数。【参考方案4】:
只是添加到第一篇文章中:在您的目标 C/cocoa 代码中,您使用了 CBC 模式,在您的 java 代码中,您使用了 EBC,并且两者都没有使用 IV 初始化向量。 EBC 密码是逐块的,而 CBC 链接在前一个块上,因此如果您的文本小于 1 个块(在您的示例中 = 16 个字节),则两者产生的密文都可以被另一个(相同)解密。
如果您正在寻找一种方法来标准化密码的使用,NIST Special Publication 800-38A, 2001 Edition 提供了测试向量。如果对任何人有帮助,我可以发布 AES CBC 和 EBC 向量的代码。
【讨论】:
以上是关于如何使 Java 和 Objective-C (iPhone) 之间的 AES 加密相同?的主要内容,如果未能解决你的问题,请参考以下文章
NSRange : 从 Objective-C 到 Java
如何在ios objective-c 应用程序中使屏幕的全部内容滚动
如何使 UIImage 在 Objective-c 的 Xcode 中工作