将AES256加密实现到IOS中

3

这是我的Java代码。现在我想在Objective-C中实现相同的功能。

Cipher encryptCipher;
IvParameterSpec iv = new IvParameterSpec(key);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = encryptCipher.doFinal(dataToEncrypt.getBytes());
Log.d("TAG", "encrypted string:"
        + Base64.encodeToString(encrypted, Base64.DEFAULT));
return Base64.encodeToString(encrypted, Base64.DEFAULT).trim();

这是我的iOS实现

- (NSData *)AES256EncryptWithKey:(NSString*)key
{
    char keyPtr[kCCKeySizeAES256 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    size_t bufferSize           = dataLength + kCCBlockSizeAES128;
    void* buffer                = malloc(bufferSize);

    size_t numBytesEncrypted    = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                  kCCAlgorithmAES128,
                                  kCCOptionPKCS7Padding,
                                  keyPtr,
                                  kCCKeySizeAES256,
                                  NULL,
                                  [self bytes],
                                  dataLength,
                                  buffer,
                                  bufferSize, 
                                  &numBytesEncrypted);

    if (cryptStatus == kCCSuccess)
    {

        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer);
    return nil;
}

这是我的哈希键生成函数。该函数在安卓和iOS中返回相同的键。

int dkLen = 16;
    NSData *keyData = [hash_key dataUsingEncoding:NSUTF8StringEncoding];
    NSData *salt    = [saltKey dataUsingEncoding:NSUTF8StringEncoding];
    uint    rounds  = 1000;
    uint    keySize = kCCKeySizeAES128;

    NSMutableData *derivedKey = [NSMutableData dataWithLength:keySize];





    CCKeyDerivationPBKDF(kCCPBKDF2,               // algorithm
                         keyData.bytes,           // password
                         keyData.length,          // passwordLength
                         salt.bytes,              // salt
                         salt.length,             // saltLen
                         kCCPRFHmacAlgSHA1,       // PRF
                         rounds,                  // rounds
                         derivedKey.mutableBytes, // derivedKey
                         dkLen*8);
 return derivedKey;

我得到了不同的输出结果。我做错了什么吗?请帮我找出问题。


由于您仍然似乎存在问题,您可以进一步改进问题:(1) 显示两个版本的输入和输出(以十六进制表示); (2) 在您喜欢的在线IDE中提供可运行的代码片段,例如 ideone.com(它支持Java和Objective-C)。 - Artjom B.
请问您能给我任何类似的例子吗? - Askarc Ali
@ Artjom B,非常感谢您的帮助。请查看此链接:https://gist.github.com/askarali/401e1056c5d2c3ad29d9 - Askarc Ali
2个回答

6
一个问题是Java代码使用了CBC模式,而iOS代码使用了ECB模式。
接下来,从引用的项目中可以看到:
//result= yHbhApwTpQ2ZhE97AKF/g==
这不是有效的Base64编码,它的字节数不是4的倍数。
这些选项是:CBC,PKCS#7填充。
输入:  
   数据:"hello",将被空置到16字节的块长度。  
   密钥:  
      base64:VQQhu+dUdqXGoE7RZL2JWg==  
      hex:550421bbe75476a5c6a04ed164bd895a  
   iv:   
      base64:VQQhu+dUdqXGoE7RZL2JWg==  
      hex:550421bbe75476a5c6a04ed164bd895a  
加密输出:  
   hex:ff21db840a704e943666113dec0285fe  
   base64:/yHbhApwTpQ2ZhE97AKF/g==  
这是测试代码:
NSString *base64Key  = @"VQQhu+dUdqXGoE7RZL2JWg==";
NSString *dataString = @"hello";

NSData *key  = [[NSData alloc] initWithBase64EncodedString:base64Key  options:0];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];

NSLog(@"key:  %@", key);
NSLog(@"data: %@", data);

NSData *encryptedData = [TestClass crypt:data
                                 iv:key
                                key:key
                            context:kCCEncrypt];

NSLog(@"encryptedData: %@", encryptedData);
NSString *encryptedBase64Data = [encryptedData base64EncodedStringWithOptions:0];
NSLog(@"encryptedBase64Data: %@", encryptedBase64Data);

这是加密方法(在类TestClass中):
+ (NSData *)crypt:(NSData *)dataIn
                  iv:(NSData *)iv
                 key:(NSData *)symmetricKey
             context:(CCOperation)encryptOrDecrypt
{
    CCCryptorStatus ccStatus   = kCCSuccess;
    size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
    NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];

    ccStatus = CCCrypt( encryptOrDecrypt,
                       kCCAlgorithmAES128,
                       kCCOptionPKCS7Padding,
                       symmetricKey.bytes,
                       kCCKeySizeAES128,
                       iv.bytes,
                       dataIn.bytes,
                       dataIn.length,
                       dataOut.mutableBytes,
                       dataOut.length,
                       &cryptBytes);

    if (ccStatus != kCCSuccess) {
        NSLog(@"CCCrypt status: %d", ccStatus);
    }

    dataOut.length = cryptBytes;

    return dataOut;
}

注意:我将加密和数据转换分开处理。混淆它们只会使测试更加复杂。
如果您使用在线加密实现,填充可能不是PKCS#7,因为mcrypt不支持它,而是使用非标准的null填充。由于填充字节只是填充字节的计数,因此可以在输入中模拟填充。以下是使用AES - 对称密码在线的示例。

请注意,"hello" PKCS#7填充到16字节的块大小时,会添加11个uint8值为11或0x0B的字节:68656c6c6f0B0B0B0B0B0B0B0B0B0B0B
最后一个问题是为什么Java代码没有产生这个结果?

谢谢你,zaph。我已经在Java和C#中有了一个可行的实现,它运行良好。 - Askarc Ali
为了实现互操作性,需要做的就是确保所有输入都相同。合并多个操作的库通常不会记录内部信息,这使得互操作性变得困难。可能存在其他问题。也许字符串不是UTF-8编码?这就是为什么我保持实际加密简单,只使用数据字节,然后在加密外添加数据转换。通过使用十六进制转储,我可以准确地查看数据内容。 - zaph
一个区别是密钥大小,在iOS代码中,密钥明确指定为256位(32字节),但Java代码根据提供的密钥数据选择密钥大小,该数据为128位(16字节)。另一个可能性是数据(“hello”)由unichar(16位)组成。 - zaph
这个方法是我所有项目中都使用的,所以目前我无法进行改进。 - Askarc Ali
Zaph解密输出结果不同。 - Askarc Ali
显示剩余3条评论

1

那么,你的密钥有多大?kCCAlgorithmAES128kCCKeySizeAES256假定不同的密钥大小。我假设你使用的是16字节的密钥,否则你的Java代码会抛出异常。如果你使用了128位密钥,则应使用kCCKeySizeAES128

另外,你没有传入任何IV,因此将假定IV填充为0x00字节,但在Java中,你正在使用密钥作为IV。

不要使用密钥作为IV。这降低了IV本来的用途,即随机化密文。你需要为每个加密生成一个随机IV,并将其与密文一起发送,例如通过在密文前添加它。

是的,PKCS#5 padding和PKCS#7 padding是相同的


所以,这是一个128位的密钥(假设使用base64编码)。如果您想使用AES-256,则需要更大的密钥,然后您将无法直接在Java中使用该密钥作为IV。 - Artjom B.
这并没有提供问题的答案。如果您想对作者进行批评或请求澄清,请在他们的帖子下留言。 - grebulon
@grebulon 你被否决了。我想了解你的理由。注意:你的答案在另一个城堡里:何时一个答案不是答案? - Artjom B.
@askarcali 这里是我在回答中所说的示例。如果这不能解决您的问题,那么可能是您传递密钥的方式有误。密钥派生的结果是 NSMutableData,但您将其作为字符串传递,然后强制施加 UTF-8 编码,这是错误的,因为它可能会在转换过程中破坏密钥的字节表示。 [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 以及之前的行是问题所在。恐怕我对 Obj-C 的了解不够,无法提供更多帮助。 - Artjom B.
@Artjom B,你能打开聊天窗口吗?并且你可以分享你所知道的一切。 - Askarc Ali
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接