iOS 13 中的 AES 加密 CryptLib 不工作
AES Encryption CryptLib in iOS 13 not working
我的应用程序使用 AES 256 加密来加密字符串。之前使用的相同代码生成了不同的结果。当 iOS 13 发布时,这个问题就开始了。而且它只发生在运送到商店或使用 Xcode 11.
构建的应用程序上
这里是用于加密的代码:
- (NSData *)encrypt:(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+2];
BOOL patchNeeded;
bzero(keyPointer, sizeof(keyPointer)); // fill with zeroes for padding
patchNeeded= ([key length] > kCCKeySizeAES256+1);
if(patchNeeded)
{
NSLog(@"Key length is longer %lu", (unsigned long)[[self md5:key] length]);
key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
}
//NSLog(@"md5 :%@", key);
[key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
[iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];
if (patchNeeded) {
keyPointer[0] = '[=11=]'; // Previous iOS version than iOS7 set the first char to '[=11=]' if the key was longer than kCCKeySizeAES256
}
NSUInteger dataLength = [plainText length];
//see https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CCryptorCreateFromData.3cc.html
// 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;
//refer to http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h
//for details on this function
//Stateless, one-shot encrypt or decrypt operation.
CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
keyPointer, kCCKeySizeAES256, /* key and its length */
ivPointer, /* 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 *) encryptPlainTextWith:(NSString *)plainText key:(NSString *)key iv:(NSString *)iv {
return [[[[CryptLib alloc] init] encrypt:[plainText dataUsingEncoding:NSUTF8StringEncoding] key:[[CryptLib alloc] sha256:key length:32] iv:iv] base64EncodedStringWithOptions:0];
}
/**
* This function computes the SHA256 hash of input string
* @param key input text whose SHA256 hash has to be computed
* @param length length of the text to be returned
* @return returns SHA256 hash of input text
*/
- (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, (CC_LONG)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:@""];
if(length > [hash length])
{
return hash;
}
else
{
return [hash substringToIndex:length];
}
}
##
我想知道代码路径中的某些内容是否在其工作方式上发生了变化。调用进行加密的方法是 "encryptPlainTextWith"。提前致谢。
我怀疑您的密钥超过了 32 个 UTF-8 字节。在那种情况下,此代码不正确。您的 patchNeeded
条件基本上是在创建垃圾密钥。如果此函数 return return 为假,则不会承诺 buffer
的内容,但您依赖它们。
没有安全的方法来 t运行cate 给你的密钥,所以我不确定你在这里想要什么行为。这取决于您传递的字符串类型。
如果 iv
小于 16 个 UTF-8 字节,则此代码也不正确。您最终会包括堆栈中的随机值。该部分可以通过以下方式修复:
bzero(ivPointer, sizeof(ivPointer));
但如果你以前的版本依赖于随机值,这仍然会有所不同。
假设您需要匹配旧行为,调试此问题的最佳方法是 运行 在调试器中查看您以前的版本,然后查看 keyPointer
和 ivPointer
最终会变成什么样.
(请注意,这种创建密钥的方法非常不安全。它极大地缩小了 AES 密钥空间。缩小多少取决于您传递的字符串类型,但它是戏剧性的。您也不应该重复使用相同的密钥+iv 在使用 CBC 时在两条消息中组合,这看起来可能确实如此。如果可能,我建议转向正确的 AES 实现。您可以查看 RNCryptor 以了解如何执行此操作的一个示例,或使用如果您愿意,可以直接使用 RNCryptor。)
内部:
- (NSString*) sha256:(NSString *)key length:(NSInteger) length
我换了
NSString *hash=[out description];
至
NSString *hash=[out debugDescription];
然后一切恢复正常。干杯快乐编码。
@Rob Napier 的替代解决方案
创建用于将 NSData 转换为 Hex 的单独函数
#pragma mark - String Conversion
-(NSString*)hex:(NSData*)data{
NSMutableData *result = [NSMutableData dataWithLength:2*data.length];
unsigned const char* src = data.bytes;
unsigned char* dst = result.mutableBytes;
unsigned char t0, t1;
for (int i = 0; i < data.length; i ++ ) {
t0 = src[i] >> 4;
t1 = src[i] & 0x0F;
dst[i*2] = 48 + t0 + (t0 / 10) * 39;
dst[i*2+1] = 48 + t1 + (t1 / 10) * 39;
}
return [[NSString alloc] initWithData:result encoding:NSASCIIStringEncoding];
}
在那之后里面:
- (NSString*) sha256:(NSString *)key length:(NSInteger) length
我换了
NSString *hash=[out description];
至
NSString *hash = [self hex:out];
我的应用程序使用 AES 256 加密来加密字符串。之前使用的相同代码生成了不同的结果。当 iOS 13 发布时,这个问题就开始了。而且它只发生在运送到商店或使用 Xcode 11.
构建的应用程序上这里是用于加密的代码:
- (NSData *)encrypt:(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+2];
BOOL patchNeeded;
bzero(keyPointer, sizeof(keyPointer)); // fill with zeroes for padding
patchNeeded= ([key length] > kCCKeySizeAES256+1);
if(patchNeeded)
{
NSLog(@"Key length is longer %lu", (unsigned long)[[self md5:key] length]);
key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
}
//NSLog(@"md5 :%@", key);
[key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
[iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];
if (patchNeeded) {
keyPointer[0] = '[=11=]'; // Previous iOS version than iOS7 set the first char to '[=11=]' if the key was longer than kCCKeySizeAES256
}
NSUInteger dataLength = [plainText length];
//see https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CCryptorCreateFromData.3cc.html
// 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;
//refer to http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h
//for details on this function
//Stateless, one-shot encrypt or decrypt operation.
CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
keyPointer, kCCKeySizeAES256, /* key and its length */
ivPointer, /* 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 *) encryptPlainTextWith:(NSString *)plainText key:(NSString *)key iv:(NSString *)iv {
return [[[[CryptLib alloc] init] encrypt:[plainText dataUsingEncoding:NSUTF8StringEncoding] key:[[CryptLib alloc] sha256:key length:32] iv:iv] base64EncodedStringWithOptions:0];
}
/**
* This function computes the SHA256 hash of input string
* @param key input text whose SHA256 hash has to be computed
* @param length length of the text to be returned
* @return returns SHA256 hash of input text
*/
- (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, (CC_LONG)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:@""];
if(length > [hash length])
{
return hash;
}
else
{
return [hash substringToIndex:length];
}
}
##
我想知道代码路径中的某些内容是否在其工作方式上发生了变化。调用进行加密的方法是 "encryptPlainTextWith"。提前致谢。
我怀疑您的密钥超过了 32 个 UTF-8 字节。在那种情况下,此代码不正确。您的 patchNeeded
条件基本上是在创建垃圾密钥。如果此函数 return return 为假,则不会承诺 buffer
的内容,但您依赖它们。
没有安全的方法来 t运行cate 给你的密钥,所以我不确定你在这里想要什么行为。这取决于您传递的字符串类型。
如果 iv
小于 16 个 UTF-8 字节,则此代码也不正确。您最终会包括堆栈中的随机值。该部分可以通过以下方式修复:
bzero(ivPointer, sizeof(ivPointer));
但如果你以前的版本依赖于随机值,这仍然会有所不同。
假设您需要匹配旧行为,调试此问题的最佳方法是 运行 在调试器中查看您以前的版本,然后查看 keyPointer
和 ivPointer
最终会变成什么样.
(请注意,这种创建密钥的方法非常不安全。它极大地缩小了 AES 密钥空间。缩小多少取决于您传递的字符串类型,但它是戏剧性的。您也不应该重复使用相同的密钥+iv 在使用 CBC 时在两条消息中组合,这看起来可能确实如此。如果可能,我建议转向正确的 AES 实现。您可以查看 RNCryptor 以了解如何执行此操作的一个示例,或使用如果您愿意,可以直接使用 RNCryptor。)
内部:
- (NSString*) sha256:(NSString *)key length:(NSInteger) length
我换了
NSString *hash=[out description];
至
NSString *hash=[out debugDescription];
然后一切恢复正常。干杯快乐编码。
@Rob Napier 的替代解决方案
创建用于将 NSData 转换为 Hex 的单独函数
#pragma mark - String Conversion
-(NSString*)hex:(NSData*)data{
NSMutableData *result = [NSMutableData dataWithLength:2*data.length];
unsigned const char* src = data.bytes;
unsigned char* dst = result.mutableBytes;
unsigned char t0, t1;
for (int i = 0; i < data.length; i ++ ) {
t0 = src[i] >> 4;
t1 = src[i] & 0x0F;
dst[i*2] = 48 + t0 + (t0 / 10) * 39;
dst[i*2+1] = 48 + t1 + (t1 / 10) * 39;
}
return [[NSString alloc] initWithData:result encoding:NSASCIIStringEncoding];
}
在那之后里面:
- (NSString*) sha256:(NSString *)key length:(NSInteger) length
我换了
NSString *hash=[out description];
至
NSString *hash = [self hex:out];