【问题标题】:AES encryption on iOS and android, output and buffer size is differentiOS和android上的AES加密,输出和缓冲区大小不同
【发布时间】:2017-08-11 07:34:44
【问题描述】:

使用 CCCrypt 功能在 iOS 上实现 AES256。但是输出和输出缓冲区长度与Android不同。

Android 中的 Cipher 类产生 48 字节数据,而 iOS 中我们获得 80 字节数据。

在 IOS 中使用 kCCAlgorithmAES、kCCOptionPKCS7Padding,在 android 中使用 AES/CBC/PKCS5Padding。

在 IOS IV 中为 NULL,在 android 中创建 iv 作为新的 16 字节数组。

请帮忙。

请查找输入和代码以供参考。

 - (void)viewDidLoad {
    [super viewDidLoad];

    NSString *message = [NSString stringWithFormat:@"com.myapp.com|355004059196637|911111111111|11341e5e-9643-4559-bbb7-34d40555e96c"];
    NSString *key = [NSString stringWithFormat:@"4f28d5901b4b7b80d33fda76ca372c2a20bd1a6c2aad7fa215dc79d507330678"];
    NSString *shaEncryptMessage = [self sha256:message length:0];
    NSData *aesEncryptData = [self aesEncrypt:[shaEncryptMessage dataUsingEncoding:NSUTF8StringEncoding] key:key iv:nil];
    NSString *hMac = [aesEncryptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    NSLog(@"hMac = %@",hMac);

    // IOS output : Can+oQR79D3/lsQGctzY/d2VBNZbWWtJxGI8iRIu80R2yTskn9gf2oKHaRESX73u
    //                  LpJHLx1Xr6iH11jFPlmqwW7mQz0xAW4uACNAMEoZ0kY=
    // Android output : MiMDkdo5cGsPMj2qCnNobgp7dr5KMvBhGuKTonrqr1lCYte/kKegGMtI/4TPhUNI
}


- (NSString*) sha256:(NSString *)key length:(NSInteger) length{
    const char *s=[key cStringUsingEncoding:NSASCIIStringEncoding];
    NSData *keyData=[NSData dataWithBytes:s length:strlen(s)];

    uint8_t digest[CC_SHA256_DIGEST_LENGTH]={0};
    CC_SHA256(keyData.bytes, (unsigned int)keyData.length, digest);
    NSData *out=[NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
    NSString *hash=[out description];
    hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
    hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
    hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
    return  hash;
}
-  (NSData *)aesEncrypt:(NSData *)plainText key:(NSString *)key iv:(NSString *)iv {
    char keyPointer[kCCKeySizeAES256+2],// room for terminator (unused) ref: https://devforums.apple.com/message/876053#876053
    ivPointer[kCCBlockSizeAES128];
    BOOL patchNeeded;
    bzero(keyPointer, sizeof(keyPointer)); // fill with zeroes for padding
    //key = [[StringEncryption alloc] md5:key];
    key = [self stringFromHex:key];
    patchNeeded= ([key length] > kCCKeySizeAES256+1);
    if(patchNeeded)
    {
        key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
    }

    [key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
    [iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];

    //    if (patchNeeded) {
    //        keyPointer[0] = '\0';  // Previous iOS version than iOS7 set the first char to '\0' if the key was longer than kCCKeySizeAES256
    //    }

    NSUInteger dataLength = [plainText length];

    // For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
    size_t buffSize = dataLength + kCCBlockSizeAES128;
    void *buff = malloc(buffSize);

    size_t numBytesEncrypted = 0;



    CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
                                     kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
                                     kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
                                     keyPointer, kCCKeySizeAES256, /* key and its length */
                                     NULL, /* initialization vector - use random IV everytime */
                                     [plainText bytes], [plainText length], /* input  */
                                     buff, buffSize,/* data RETURNED here */
                                     &numBytesEncrypted);


    if (status == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
    }

    free(buff);
    return nil;
}

- (NSString *) stringFromHex:(NSString *)str
{
    NSMutableData *stringData = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [str length] / 2; i++) {
        byte_chars[0] = [str characterAtIndex:i*2];
        byte_chars[1] = [str characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [stringData appendBytes:&whole_byte length:1];
    }
    return [[NSString alloc] initWithData:stringData encoding:NSASCIIStringEncoding];
}

请同时查找android代码,

   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    generateHMAC();
}

String K0 = "4f28d5901b4b7b80d33fda76ca372c2a20bd1a6c2aad7fa215dc79d507330678";
String generatedString = "com.myapp.com|355004059196637|911111111111|11341e5e-9643-4559-bbb7-34d40555e96c";

private void generateHMAC() {
    Log.d("Message of Hash", generatedString);
    byte[] var14 = new byte[0];
    try {
        var14 = SHA256(generatedString);
        byte[] var15 = new byte[0];
        var15 = encrypt(var14, hexStringToByteArray(K0));
        String var4 = Base64.encodeToString(var15, 2);
        Log.d("Existing K0", K0);
        Log.d("HMAC", var4);
    } catch (Exception e) {
        e.printStackTrace();
    }
}


public byte[] SHA256(String paramString) throws Exception {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    md.update(paramString.getBytes("UTF-8"));
    byte[] digest = md.digest();
    return digest;
}

public byte[] encrypt(byte[] var1, byte[] var2) throws Exception {
    SecretKeySpec var3 = new SecretKeySpec(var2, "AES");
    byte[] var4 = new byte[16];
    IvParameterSpec var5 = new IvParameterSpec(var4);
    Cipher var6 = Cipher.getInstance("AES/CBC/PKCS5Padding");
    var6.init(1, var3, var5);
    byte[] var7 = var6.doFinal(var1);
    return var7;
}

public byte[] hexStringToByteArray(String var1) {
    byte[] var2 = new byte[var1.length() / 2];

    for (int var3 = 0; var3 < var2.length; ++var3) {
        int var4 = var3 * 2;
        int var5 = Integer.parseInt(var1.substring(var4, var4 + 2), 16);
        var2[var3] = (byte) var5;
    }

    return var2;
}

【问题讨论】:

  • 请尝试github.com/alexeypro/EncryptDecrypt它的AES256EncryptWithKey
  • 你能把代码贴出来吗?
  • 虽然这不能回答你的问题,但你应该知道,虽然 Apple 告诉你初始化向量 (IV) 是可选的,但如果你不指定它并选择你的 IV,加密是不安全的一种不可预知的方式。为这个糟糕的加密 API 感到羞耻。

标签: ios objective-c aes encryption-symmetric


【解决方案1】:

提供 iOS 代码后的更新:

  • aesEncryptData 应该是您的输出。摆脱与 AES 加密无关的 hmac(相反,它是为了消息完整性)。
  • 匹配 Android 代码的唯一方法是使用与 Android 代码相同的 IV。

之前的回复:

输入多长时间?提供源代码和示例数据可以帮助我们更快地解决问题。

没有要求的信息,我没有你的答案,但我有一些建议可以帮助你深入了解它:

  • 您的填充没问题。 Java 中的 PKCS5Padding 是wrongly named PKCS#7 的实现,因此它应该与 Apple 的 kCCOptionPKCS7Padding 兼容。
  • Apple by default uses CBC mode 如果未指定模式,则在后台,因此与 Android 代码一致。所以这也不是问题。
  • 加密时,密文将是 16 字节的倍数(因为 AES 的块大小为 N=16 字节并且根据PKCS #7 的定义)。具体来说:
    • 如果输入是 16 字节的倍数,那么输出应该比输入正好多 16 字节。
    • 如果输入不是 16 字节的倍数,则输出应为 16*Ceiling(输入长度/16)。示例:47 字节输入应为 16*Ceiling(17/16) = 16*3 = 48 字节输出。
  • 其中一种实现可能将 IV 作为密文的一部分输出。如果发生这种情况,它应该在密文的开头。您应该能够测试以查看是否发生这种情况。 (如果发生这种情况,请告诉我)

话虽如此,但有些东西很奇怪,很可能实现错误,我们需要代码来深入了解它。 Android 代码生成 3 个 16 块,而 Apple 代码生成 5 个 16 块,这毫无意义。

此外,正如我在上面评论的那样,即使 Apple 告诉您 IV 是可选的,它们也意味着它在让代码工作方面是可选的。它是not optional for security。 IV 是必需的,并且对于 CBC 操作模式必须是不可预测的,并且永远不应重复。如果忽略这一点,就会泄露有关数据的信息,并且在某些情况下,攻击者可能能够解密数据(填充 oracle 攻击)。

【讨论】:

  • 感谢@TheGreatContini,添加 iOS 代码和示例输入供您参考。请帮助解决这个问题。
  • @Pradip 你在 AES 之后有 HMAC-sha256,这是一种完全不同的算法。取出 HMAC,您只想加密,别无其他。要匹配 Android,您需要使用与 Android 相同的 IV。您不能简单地替换 nil 并期望数据匹配,因为它不会匹配。
  • 请找到更新的 android 代码,SHA256 后跟 AES,输出后跟 base64 编码(作为我们所需的逻辑)。我希望 iOS 的逻辑与 android 类似。
  • 是的问题现在已解决,在 iOS 中,我将 SHA 输出转换为字符串,并在将该字符串传递给 AES 后再次使用 NSUTF8StringEncoding 将其转换为 NSData,这是错误的。通过直接将字节传递给方法问题得到解决。
猜你喜欢
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 2021-12-12
相关资源
最近更新 更多