【问题标题】:Decrypt SJCL cipher text in Objective-C在 Objective-C 中解密 SJCL 密文
【发布时间】:2016-07-28 14:46:52
【问题描述】:

我将使用 SJCL 加密的数据接收到需要解密的 iOS 应用程序中。另一端是使用 SJCL 和 AES CCM 模式,Apple 的 CommonCrypto 不支持这种模式,所以我为此使用 VPCCMCrypt 库。无法对发送 SJCL 密文的对方进行任何更改。

这是我的解密方法:

+ (NSData *)decryptSjcl:(NSDictionary *)cipher password:(NSString *)password {
    if (cipher == nil || password == nil) {
        return nil;
    }

    int version = [cipher[@"v"] intValue];
    NSString *iv = cipher[@"iv"];
    uint iter = [cipher[@"iter"] unsignedIntValue];
    uint ks = [cipher[@"ks"] unsignedIntValue];
    uint ts = [cipher[@"ts"] unsignedIntValue];
    NSString *mode = cipher[@"mode"];
    NSString *adata = cipher[@"adata"];
    NSString *algorithm = cipher[@"cipher"];
    NSString *salt = cipher[@"salt"];
    NSString *ct = cipher[@"ct"];

    if (version != 1 || ! [@"aes" isEqualToString:algorithm]) {
        return nil;
    }

    NSData *rawIv = [[NSData alloc] initWithBase64EncodedString:iv options:0];
    NSData *rawSalt = [[NSData alloc] initWithBase64EncodedString:salt options:0];
    NSData *rawAdata = [[NSData alloc] initWithBase64EncodedString:adata options:0];
    NSData *cipherData = [[NSData alloc] initWithBase64EncodedString:ct options:0];
    NSData *key;

    NSMutableData *decipheredData = nil;

    if ([@"ccm" isEqualToString:mode]) {
        key = [Cryptor sjclAesKeyForPassword:password salt:rawSalt iterations:iter keySize:ks];
        decipheredData = [Cryptor decryptAesCcmData:cipherData iv:rawIv key:key adata:rawAdata tagSize:ts];
    }

    return decipheredData;
}

SJCL 密钥生成:

+ (NSData *)sjclAesKeyForPassword:(NSString *)password salt:(NSData *)salt iterations:(uint)iterations keySize:(uint)keySizeBits {
    NSMutableData *derivedKey = [NSMutableData dataWithLength:keySizeBits / 8];

    int result = CCKeyDerivationPBKDF(kCCPBKDF2,
            password.UTF8String,
            [password lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
            salt.bytes,
            salt.length,
            kCCPRFHmacAlgSHA256,
            iterations,
            derivedKey.mutableBytes,
            derivedKey.length);

    NSAssert(result == kCCSuccess, @"Unable to create AES key for password: %d", result);

    return derivedKey;
}

AES CCM 解密:

+ (NSMutableData *)decryptAesCcmData:(NSData *)cipherData iv:(NSData *)iv key:(NSData *)key adata:(NSData *)adata tagSize:(uint)tagSizeBits {
    VPCCMCrypt *ccm = [[VPCCMCrypt alloc] initWithKey:key
                                                   iv:iv
                                                adata:adata
                                            tagLength:tagSizeBits / 8];

    [ccm decryptDataWithData:cipherData
               finishedBlock:^(NSData *data) {
                   NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

                   NSLog(@"Decrypted SJCL message: %@", result);
               }
                  errorBlock:^(NSError *error) {
                      NSLog(@"ERROR");
                  }];

    return nil;
}

所有来自 SJCL 的输入数据都被正确解析(IV、salt、密钥大小、标签大小、PBKDF 迭代、密文)并从它们的Base64 编码表示中解码为NSData。密码使用相同。包括创建的 AES 密钥在内的所有数据都不为零。

最后,它在 VPCCMCrypt 内验证 CCM 标签失败(标签不同)。上面的代码有问题吗?是否有任何其他 iOS/Objective-C/Swift 库来解密 AES CCM 甚至更好的 SJCL?我对 SJCL 库的 JavaScript 包装器不感兴趣。

为了测试,我使用来自SJCL's demo page 的简单加密数据。

编辑:

如 cmets 中所述,SJCL 发送 16 字节 IV 而不是 CCM 模式的最大 12 字节,然后在解密时将其内部钳制为最大 12 字节。这是更新的解密方法:

+ (NSData *)decryptSjcl:(NSDictionary *)cipher password:(NSString *)password {
    if (cipher == nil || password == nil) {
        return nil;
    }

    int version = [cipher[@"v"] intValue];
    NSString *iv = cipher[@"iv"];
    uint iter = [cipher[@"iter"] unsignedIntValue];
    uint ks = [cipher[@"ks"] unsignedIntValue];
    uint ts = [cipher[@"ts"] unsignedIntValue];
    NSString *mode = cipher[@"mode"];
    NSString *adata = cipher[@"adata"];
    NSString *algorithm = cipher[@"cipher"];
    NSString *salt = cipher[@"salt"];
    NSString *ct = cipher[@"ct"];

    if (version != 1 || ! [@"aes" isEqualToString:algorithm]) {
        return nil;
    }

    NSMutableData *rawIv = [[NSMutableData alloc] initWithBase64EncodedString:iv options:0];
    NSMutableData *rawSalt = [[NSMutableData alloc] initWithBase64EncodedString:salt options:0];
    NSMutableData *rawAdata = [[NSMutableData alloc] initWithBase64EncodedString:adata options:0];
    NSMutableData *cipherData = [[NSMutableData alloc] initWithBase64EncodedString:ct options:0];
    NSData *key;

    NSData *decipheredData = nil;

    if ([@"ccm" isEqualToString:mode]) {
        key = [Cryptor sjclAesKeyForPassword:password salt:rawSalt iterations:iter keySize:ks];

        // Clamp the SJCL IV - They use a 16 byte IV for CCM mode which is against specification and they do a funky
        // clamping. CCM mode IV should be max 13 bytes long. They almost always clamp it to 13 bytes but save the whole
        // 16 bytes in their JSON container.
        // for (L=2; L<4 && ol >>> 8*L; L++) {}
        // if (L < 15 - ivl) { L = 15-ivl; }
        // iv = w.clamp(iv,8*(15-L));
        int64_t ivl = rawIv.length;
        int64_t ol = cipherData.length - (ts / 8);
        int64_t L = 2;
        for (L = 2; L < 4 && ol >> 8*L; L++) {}
        if (L < 15 - ivl) {
            L = 15 - ivl;
        }
        NSRange subrange = NSMakeRange(0, (NSUInteger)(15 - L));

        decipheredData = [Cryptor decryptAesCcmData:cipherData iv:[rawIv subdataWithRange:subrange] key:key adata:rawAdata tagSize:ts];
    }
    else {
        decipheredData = nil;
    }

    return decipheredData;
}

最后缺少的一件事是验证 TAG,我无法做到这一点。有什么想法吗?

【问题讨论】:

    标签: ios objective-c encryption cryptography sjcl


    【解决方案1】:

    SJCL 在 AES-CCM 模式下不使用整个 (128/192/256bit) IV,但演示页面显示了它。尝试使用更少的字节数。

    您可以在此处了解 IV 长度计算的工作原理: http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_ccm.js.html

    for (L=2; L<4 && ol >>> 8*L; L++) {}
    if (L < 15 - ivl) { L = 15-ivl; }
    iv = w.clamp(iv,8*(15-L));
    

    【讨论】:

    • 谢谢,但我已经解决了这部分问题,并在我的代码中添加了 IV 钳位。不过,我没有时间更新我的问题,也没有人关心它。稍后我将添加 Objective-C 以进行钳位。使用钳位 IV 可以解密,但 TAG 验证仍然失败。知道为什么即使使用正确钳位的 IV 标记验证也会失败吗? SJCL 是否对 TAG 做一些时髦的事情,比如对违反规范的 IV?
    • 我刚刚用更新的 Objective-C 代码更新了我的问题,以将 IV 限制为最大 12 个字节。我会接受您的回答,因为解密有效,但 TAG 仍未验证...
    • VPCCMCrypt decryptDataWithData 验证标签,因此如果解码成功,则标签验证! github.com/billp/VPCCMCrypt/blob/master/lib/VPCCMCrypt.m#L308
    • 它首先尝试验证标签,如果验证失败,它不会继续解密数据。我必须禁用标签验证码才能解密数据。
    猜你喜欢
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    • 2015-08-21
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    相关资源
    最近更新 更多