我编写的代码利用iOS的Common Crypto来加密和解密NSData
对象。加密密钥为AES128,存储在iOS钥匙串中。我可以成功加密和解密数据,所以我知道代码的一部分是有效的。但是,作为完整性检查,我还生成了第二个 AES128密钥,并尝试解密使用第一个加密密钥加密的数据。我期望CCCryptorStatus
值不是kCCSuccess
,但事实并非如此。我收到了一个NSData
对象,没有错误。我的加密/解密代码看起来像这样......
-(NSData *)dataDecryptedUsingAlgorithm:(CCAlgorithm)algorithm
data:(NSData *)data
key:(id)key
initializationVector:(id)iv
options:(CCOptions)options
error:(CCCryptorStatus *)error {
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
NSMutableData * keyData, * ivData;
if ( [key isKindOfClass: [NSData class]] )
keyData = (NSMutableData *) [key mutableCopy];
else
keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
if ( [iv isKindOfClass: [NSString class]] )
ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
else
ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
// [keyData autorelease];
// [ivData autorelease];
// ensure correct lengths for key and iv data, based on algorithms
FixKeyLengths( algorithm, keyData, ivData );
status = CCCryptorCreate( kCCDecrypt, algorithm, options,
[keyData bytes], [keyData length], [ivData bytes],
&cryptor );
if ( status != kCCSuccess )
{
if ( error != NULL )
*error = status;
return ( nil );
}
NSData *result = [self runCryptor:cryptor onData:data result:&status];
if ( (result == nil) && (error != NULL) )
*error = status;
CCCryptorRelease(cryptor);
return ( result );
}
-(NSData *)dataEncryptedUsingAlgorithm:(CCAlgorithm) algorithm
data:(NSData *)data
key:(id)key
initializationVector:(id)iv
options:(CCOptions)options
error:(CCCryptorStatus *)error {
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
NSMutableData * keyData, * ivData;
if ( [key isKindOfClass: [NSData class]] )
keyData = (NSMutableData *) [key mutableCopy];
else
keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
if ( [iv isKindOfClass: [NSString class]] )
ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
else
ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
// [keyData autorelease];
// [ivData autorelease];
// ensure correct lengths for key and iv data, based on algorithms
FixKeyLengths( algorithm, keyData, ivData );
status = CCCryptorCreate( kCCEncrypt, algorithm, options,
[keyData bytes], [keyData length], [ivData bytes],
&cryptor );
if ( status != kCCSuccess )
{
if ( error != NULL )
*error = status;
return ( nil );
}
NSData *result = [self runCryptor:cryptor onData:data result:&status];
if ( (result == nil) && (error != NULL) )
*error = status;
CCCryptorRelease( cryptor );
return ( result );
}
-(NSData *)runCryptor:(CCCryptorRef)cryptor onData:(NSData *)data result:(CCCryptorStatus *)status {
size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[data length], true );
void * buf = malloc( bufsize );
size_t bufused = 0;
size_t bytesTotal = 0;
*status = CCCryptorUpdate( cryptor, [data bytes], (size_t)[data length],
buf, bufsize, &bufused );
if ( *status != kCCSuccess )
{
free( buf );
return ( nil );
}
bytesTotal += bufused;
// From Brent Royal-Gordon (Twitter: architechies):
// Need to update buf ptr past used bytes when calling CCCryptorFinal()
*status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
if ( *status != kCCSuccess )
{
free( buf );
return ( nil );
}
bytesTotal += bufused;
return ( [NSData dataWithBytesNoCopy: buf length: bytesTotal] );
}
当我调用加密和解密方法时,我传递kCCAlgorithmAES128
作为我的算法,kCCOptionPKCS7Padding
作为我的选项。有没有办法捕获一个坏密钥用于解密,所以我可以返回相应的错误?
答案 0 :(得分:2)
区分坏密钥和损坏数据的唯一可靠方法是,如Zaph所说,某种婴儿床(即用于最广义的术语;即你对加密知之甚少)。如果您对此方法感兴趣,请参阅RNCryptor v4 spec。目前还没有实现,它只是一个规范,但它包含一个验证字段,可用于确定密码是否正确。它使用HKDF-Expand步骤将您的一些初始密钥材料转换为验证令牌。
作为一个说明,你的方法的这一部分非常令人担忧:
if ( [iv isKindOfClass: [NSString class]] )
ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
else
ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
如果传入一个字符串,则键空间会比您预期的要小得多。即使它是16个完全随机的字符串字符串,合法的UTF8字符串代表的空间比同等16字节的随机数据小得多。
答案 1 :(得分:1)
不,除非您使用添加身份验证(如GCM或EAX操作模式)的密码模式,否则无法使用。否则,解密将总是奇怪的可能性,因为填充在解密之后可能是正确的。换句话说,您不能使用CCCryptorStatus
来(可靠地)检测错误的密钥或损坏的密文。正如Zaph指出a discussion on the Apple forums所指出的那样,对于较新版本的iOS(6和7),CCCryptorStatus
可能永远不会被设置为kCCDecodeError
,因为可能会填充Oracle攻击。
除了使用添加身份验证的密码,您还可以添加自己的身份验证标记,例如通过计算密文上的HMAC值。最好使用第二个密钥用于HMAC并将IV包含在经过身份验证的数据中。请注意,您需要在使用明文之前检查身份验证标记,或者解密最后一个块(在CBC模式下加密)。否则你将容易受到填充oracle攻击。
请注意,您将无法完全区分密钥错误和密文损坏。
答案 2 :(得分:0)
基本上没有办法知道消息是否被正确或错误地解密。就AES(以及大多数加密)而言,它只是位输入和位输出。这是一个功能。
错误CCCryptorStatus
仅处理严重错误。它没有为不正确的填充设置kCCDecodeError,这已在Apple Developer论坛上详细讨论过。
确定解密是否正确是WWII中使用解密的主要问题。基本上需要一个“Crib”,消息的某些部分已知用于测试解密。来自维基百科:“婴儿床”一词起源于英国第二次世界大战解密行动布莱切利公园。
如果您需要,则需要将其添加到用于通信的协议中。