如何使 Java 和 Objective-C (iPhone) 之间的 AES 加密相同?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2280375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:37:43  来源:igfitidea点击:

How can I make my AES encryption identical between Java and Objective-C (iPhone)?

javaiphoneobjective-cencryptionaes

提问by Simon Lee

I am encrypting a string in objective-c and also encrypting the same string in Java using AES and am seeing some strange issues. The first part of the result matches up to a certain point but then it is different, hence when i go to decode the result from Java onto the iPhone it cant decrypt it.

我在objective-c 中加密一个字符串,也在Java 中使用AES 加密同一个字符串,我看到了一些奇怪的问题。结果的第一部分匹配到某个点,但随后就不同了,因此当我将结果从 Java 解码到 iPhone 上时,它无法解密。

I am using a source string of "Now then and what is this nonsense all about. Do you know?" Using a key of "1234567890123456"

我使用的源字符串是“那么,这是什么废话。你知道吗?” 使用密钥“1234567890123456”

The objective-c code to encrypt is the following: NOTE: it is a NSData category so assume that the method is called on an NSData object so 'self' contains the byte data to encrypt.

加密的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;
}

And the java encryption code is...

而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;
    }
}

The hex output of the objective-c code is -

Objective-c 代码的十六进制输出是 -

7a68ea36 8288c73d f7c45d8d 22432577 9693920a 4fae38b2 2e4bdcef 9aeb8afe 69394f3e 1eb62fa7 74da2b5c 8d7b3c89 a295d306 f1f90349 6899ac34 63a6efa0

and the java output is -

而java输出是-

7a68ea36 8288c73d f7c45d8d 22432577 e66b32f9 772b6679 d7c0cb69 037b8740 883f8211 748229f4 723984beb 50b5aea1 f17594c9 fad2d05e e0926805 572156d

As you can see everything is fine up to -

正如你所看到的一切都很好 -

7a68ea36 8288c73d f7c45d8d 22432577

I am guessing I have some of the settings different but can't work out what, I tried changing between ECB and CBC on the java side and it had no effect.

我猜我有一些不同的设置,但不知道是什么,我尝试在 Java 端在 ECB 和 CBC 之间进行更改,但没有效果。

Can anyone help!? please....

有人可以帮忙吗!?请....

采纳答案by Alexander Torstling

Since the CCCrypt takes an IV, does it not use a chaining block cipher method (such as CBC)? This would be consistant with what you see: the first block is identical, but in the second block the Java version applies the original key to encrypt, but the OSX version seems to use something else.

由于 CCCrypt 采用 IV,它是否不使用链接分组密码方法(例如 CBC)?这与您所看到的一致:第一个块是相同的,但在第二个块中,Java 版本应用原始密钥进行加密,但 OSX 版本似乎使用了其他东西。

EDIT:

编辑:

From hereI saw an example. Seems like you need to pass the kCCOptionECBMode to CCCrypt:

这里我看到了一个例子。似乎您需要将 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);

EDIT 2:

编辑2:

I played around with some command line to see which one was right. I thought I could contribute it:

我玩了一些命令行,看看哪个是正确的。我以为我可以贡献它:

$ 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.

回答by Simon Lee

For anyone else who needs this, disown was absolutely spot on... the revised call to create the crypt in objective-c is as follows (note you need the ECB mode AND the padding)...

对于任何需要这个的人来说,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);

回答by user357614

I spent a few weeks decrypting a base64 encoded, AES256 encrypted string. Encryption was done by CCCrypt (Objective-C)on an iPad. The decryption was to be done in Java (using Bouncy Castle).

我花了几个星期来解密 base64 编码的 AES256 加密字符串。加密是由 CCCrypt (Objective-C) 在 iPad 上完成的。解密将在 Java 中完成(使用 Bouncy Castle)。

I finally succeeded and learnt quite a lot in the process. The encryption code was exactly the same as above (I guess it's taken from the Objective-C sample in the iPhone developer documentation).

我终于成功了,在这个过程中学到了很多东西。加密代码与上面完全相同(我猜它取自 iPhone 开发人员文档中的 Objective-C 示例)。

What CCCrypt() documentation does NOT mention is that it uses CBC mode by default (if you don't specify an option like kCCOptionECBMode). It does mention that the IV, if not specified, defaults to all zeros (so IV will be a byte array of 0x00, 16 members in length).

CCCrypt() 文档没有提到它默认使用 CBC 模式(如果你没有指定像 kCCOptionECBMode 这样的选项)。它确实提到了 IV,如果未指定,默认为全零(因此 IV 将是一个 0x00 的字节数组,长度为 16 个成员)。

Using these two pieces of information, you can create a functionally identical encryption module using CBC (and avoid using ECB which is less secure) on both Java and OSx/iphone/ipad(CCCrypt).

使用这两条信息,您可以在 Java 和 OSx/iphone/ipad(CCCrypt) 上使用 CBC(并避免使用安全性较低的 ECB)创建功能相同的加密模块。

The Cipher init function will take the IV byte array as a third argument:

Cipher init 函数将 IV 字节数组作为第三个参数:

cipher.init(Cipher.ENCRYPT_MODE, keySpec, IV).

回答by Nichole

Just to add to the first post: in your objective C/cocoa code you used CBC mode and in your java code you used EBC and an IV initialization vector wasn't used in either. EBC cipher is block by block and CBC chains upon the preceding block, so if your text is smaller than 1 block (=16 bytes in your example), the cipher text produced by both are decryptable by the other (the same).

只是添加到第一篇文章:在您的目标 C/cocoa 代码中,您使用了 CBC 模式,在您的 Java 代码中,您使用了 EBC,并且两者都没有使用 IV 初始化向量。EBC 密码是逐块和 CBC 链在前一个块上的,因此如果您的文本小于 1 个块(在您的示例中为 16 个字节),则两者生成的密文可由另一个解密(相同)。

If you are looking for a way to standardize your use of the ciphers, NIST Special Publication 800-38A, 2001 Edition has test vectors. I can post code for the AES CBC and EBC vectors if it's helpful to anyone.

如果您正在寻找一种标准化密码使用的方法,NIST 特别出版物 800-38A,2001 版有测试向量。如果对任何人有帮助,我可以发布 AES CBC 和 EBC 向量的代码。