iOS和Android上的AES加密,输出和缓冲区大小不同

Pra*_*dip 5 aes objective-c encryption-symmetric ios

使用CCCrypt函数在iOS上实现AES256.但输出和输出缓冲区长度与Android不同.

Android中的密码类产生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];
}
Run Code Online (Sandbox Code Playgroud)

请找到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;
}
Run Code Online (Sandbox Code Playgroud)

The*_*ini 3

提供 iOS 代码后更新:

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

之前的回复:

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

如果没有所需的信息,我无法得到您的答案,但我有一些建议可以帮助您了解真相:

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

话虽如此,有些事情很奇怪,并且可能实现错误,我们需要代码来弄清楚它的真相。Android 代码产生 3 个 16 块,而 Apple 代码产生 5 个 16 块,这是没有意义的。

另外,正如我上面评论的那样,尽管 Apple 告诉您 IV 是可选的,但他们的意思是在使代码正常工作方面它是可选的。为了安全起见,它不是可选的。IV 是必需的,并且对于 CBC 操作模式来说必须是不可预测的,并且绝不能重复。如果您忽略这一点,则会泄漏有关数据的信息,并且在某些情况下,攻击者可能能够解密数据(填充预言机攻击)。