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

2022-09-01 18:52:56

我正在用objective-c加密一个字符串,并使用AES在Java中加密相同的字符串,并且看到一些奇怪的问题。结果的第一部分匹配到某一点,但后来它是不同的,因此当我去解码来自Java的结果到iPhone时,它无法解密它。

我正在使用“现在那么,这是关于什么的废话”的源字符串。你知道吗?使用“1234567890123456”键

要加密的目标 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版本似乎使用了其他东西。

编辑:

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

答案 2

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

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

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

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

密码 init 函数将采用 IV 字节数组作为第三个参数:

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