Storing and Retrieving compressed texts as BLOB from Sqlite via FMDB - ios

I am trying to store text data as compressed blobs.
[db executeUpdate:#"create table blobTable (a text, b blob)"];
[db executeUpdate:#"insert into blobTable (a, b) values (?, compressMe('random text string'))", #"lord of the rings"];
And then I try to query them using:
FMResultSet *rs = [db executeQuery:#"select uncompressMe(b) as k from blobTable where a = ?", #"lord of the rings"];
Where compressMe and uncompressMe are defined as:
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
[queue inDatabase:^(FMDatabase *adb) {
[adb makeFunctionNamed:#"compressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
#autoreleasepool {
const char *c = (const char *)sqlite3_value_text(aargv[0]);
NSString *s = [NSString stringWithUTF8String:c];
NSLog(#"string to compress: %#", s);
NSData *compressedBody = [self gzipDeflate:[s dataUsingEncoding:NSUTF8StringEncoding]];
sqlite3_result_blob(context, (__bridge const void *)(compressedBody), [compressedBody length], nil);
}
}
else {
NSLog(#"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
sqlite3_result_null(context);
}
}];
}];
[queue inDatabase:^(FMDatabase *adb) {
[adb makeFunctionNamed:#"uncompressMe" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_BLOB) {
#autoreleasepool {
NSLog(#"inside uncompressMe");
NSUInteger len = sqlite3_value_bytes(aargv[0]);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, sqlite3_value_blob(aargv[0]), len);
NSData *data = [[NSData alloc] initWithBytes:byteData length:len];
NSData *deflatedBody = [self gzipInflate:data];
NSString *deflatedString = [[NSString alloc] initWithData:deflatedBody encoding:NSUTF8StringEncoding];
sqlite3_result_text(context, (__bridge const void *)(deflatedString), -1, SQLITE_UTF8);
}
}
else {
NSLog(#"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
sqlite3_result_null(context);
}
}];
}];
For the sake of completeness, the zip functions are defined as such:
- (NSData *)gzipInflate:(NSData*)data
{
if ([data length] == 0) return data;
unsigned full_length = [data length];
unsigned half_length = [data length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[data bytes];
strm.avail_in = [data length];
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
while (!done)
{
// Make sure we have enough room and reset the lengths.
if (strm.total_out >= [decompressed length])
[decompressed increaseLengthBy: half_length];
strm.next_out = [decompressed mutableBytes] + strm.total_out;
strm.avail_out = [decompressed length] - strm.total_out;
// Inflate another chunk.
status = inflate (&strm, Z_SYNC_FLUSH);
if (status == Z_STREAM_END) done = YES;
else if (status != Z_OK) break;
}
if (inflateEnd (&strm) != Z_OK) return nil;
// Set real length.
if (done)
{
[decompressed setLength: strm.total_out];
return [NSData dataWithData: decompressed];
}
else return nil;
}
- (NSData *)gzipDeflate:(NSData*)data
{
if ([data length] == 0) return data;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.total_out = 0;
strm.next_in=(Bytef *)[data bytes];
strm.avail_in = [data length];
// Compresssion Levels:
// Z_NO_COMPRESSION
// Z_BEST_SPEED
// Z_BEST_COMPRESSION
// Z_DEFAULT_COMPRESSION
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
do {
if (strm.total_out >= [compressed length])
[compressed increaseLengthBy: 16384];
strm.next_out = [compressed mutableBytes] + strm.total_out;
strm.avail_out = [compressed length] - strm.total_out;
deflate(&strm, Z_FINISH);
} while (strm.avail_out == 0);
deflateEnd(&strm);
[compressed setLength: strm.total_out];
return [NSData dataWithData:compressed];
}
However, the uncompressMe function doesnt seem to succeed. It fails in the Inflate method, which always returns a nil. Any thoughts what I might be doing wrong? I have checked to make sure that the blob column does get populated.

I feel like issue is from below code:
NSUInteger len = sqlite3_value_bytes(aargv[0]);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, sqlite3_value_blob(aargv[0]), len);
NSData *data = [[NSData alloc] initWithBytes:byteData length:len];
Try this to load NSData from database instead of creating byte variable and copying it.
const void *bytes = sqlite3_column_blob(statement, 3);
NSData *data = [[NSData alloc] initWithBytes:bytes length:size];

Related

How can we encrypt bytes through DES/CBC/NOPADDING in Objective C?

I am trying to encrypt bytes data through DES/CBS/NOPADDING algorithm but unable to get correct output. What I have tried is given below in code. Please have a look and suggest.
#import "STEncryptViewController.h"
#import <CommonCrypto/CommonCryptor.h>
#import "STEncryptorDES.h"
#import "NSData+Base64.h"
#import <CommonCrypto/CommonDigest.h>
#define KEY #"MY_TEST_KEY"
#interface STEncryptViewController ()
#end
#implementation STEncryptViewController
#synthesize message;
#synthesize encrypted;
#synthesize decrypted;
#synthesize key;
#synthesize outputMessage;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (IBAction)encrypt:(id)sender
{
const unsigned char bytes[] = {65,17,17,17,17,17,17,17};
NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
NSString *str = self.key.text;
NSData* data12 = [str dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger len = str.length;
uint8_t *bytes1 = (uint8_t *)[data12 bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:len * 3];
// [result appendString:#"["];
//Key convertion
int i = 0;
while(i < len){
if (i) {
[result appendString:#","];
}
[result appendFormat:#"%d", bytes1[i]];
i++;
}
// [result appendString:#"]"];
NSLog (#"String is %#",str);
NSLog (#"Byte array is %#",result);
//
Byte iv1 [] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
Byte iv [] = {1, 35, 69, 103, -119, -85, -51, -17};
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSData *encryptedData = [STEncryptorDES encryptData1:data key:[NSData dataWithBytes:iv1 length:sizeof(iv1)] iv:ivData];
NSLog(#"encrypted : %#", [encryptedData base64EncodedString]);
self.outputMessage.text = [NSString stringWithFormat:#"Encrypted String ::>> %#",[encryptedData base64EncodedString]];
[self doCipher:nil enc:kCCEncrypt];
}
- (NSData *)md5DataFromString:(NSString *)input
{
const char *cStr = [input UTF8String];
unsigned char digest[16];
CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call
return [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
}
- (IBAction)decrypt:(id)sender
{
NSData *valueData = [self hexToBytes:self.decrypted.text];
NSData *keyData = [self md5DataFromString:self.key.text];
Byte iv [] = {1, 35, 69, 103, -119, -85, -51, -17};
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSData *decryptedData = [STEncryptorDES decryptData:valueData key:keyData iv:ivData];
NSLog(#"decrypted : %#", [[NSString alloc] initWithData:decryptedData encoding:NSASCIIStringEncoding]);
self.outputMessage.text = [NSString stringWithFormat:#"Decrypted String ::>> %#", [[NSString alloc] initWithData:decryptedData encoding:NSASCIIStringEncoding]];
}
- (NSString*)doCipher:(NSString*)plainText enc:(CCOperation)encryptOrDecrypt{
NSData *key123 = [self hexToBytes:#"MY_TEST_KEY"];
const void *vplainText;
size_t plainTextBufferSize;
if (encryptOrDecrypt == kCCDecrypt)
{
NSData *EncryptData = [self hexToBytes:#"5454545454545454"];
// NSData *EncryptData =[NSData dataWithBase64EncodedString:plainText];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else
{
plainTextBufferSize = [plainText length];
vplainText = (const void *) [plainText UTF8String];
}
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
// uint8_t ivkCCBlockSize3DES;
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key123 getBytes:cKey length:FBENCRYPT_KEY_SIZE];
//unsigned char result1[24]= {0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,00,00,00,00,0};
unsigned char IV3[8]={1, 35, 69, 103, -119, -85, -51, -17};
uint8_t iv[kCCBlockSize3DES];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
0x0000 ,
cKey, //"123456789012345678901234", //key
kCCKeySize3DES,
IV3 , //iv,
vplainText, //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
//if (ccStatus == kCCSuccess) NSLog(#"SUCCESS");
/*else*/ if (ccStatus == kCCParamError) return #"PARAM ERROR";
else if (ccStatus == kCCBufferTooSmall) return #"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) return #"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) return #"ALIGNMENT";
else if (ccStatus == kCCDecodeError) return #"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) return #"UNIMPLEMENTED";
NSString *result;
if (encryptOrDecrypt == kCCDecrypt)
{
result = [ [NSString alloc] initWithData: [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIStringEncoding];
}
else
{
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
NSLog(#"data is: %#", myData);
// result = [NSData base64StringFromData:myData length:myData.length];
// result = [[NSString alloc]initWithData:myData encoding:NSUTF8StringEncoding];
result = nil;
}
return result;
}
-(NSData*)hexToBytes:(NSString *)hex {
NSString *safeStr = [hex stringByReplacingOccurrencesOfString:#"0" withString:#"A"];
NSMutableData* data = [NSMutableData data];
int idx;
for (idx = 0; idx+2 <= safeStr.length; idx+=2) {
NSRange range = NSMakeRange(idx, 2);
NSString* hexStr = [safeStr substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
[data appendBytes:&intValue length:1];
}
NSLog(#"%lu",(unsigned long)data.hash);
return data;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This was my all effort. Currently it is working but not expected output getting. Please suggest me what am I missing here?
One glaring error is that STEncryptorDES is used and is specifies padding but the question states that padding should be used.
Observations:
- You call doCipher with nil and do not use it's return value, delete the unneeded/unused code from the question.
- There is a lot of code to perform a rather simple operation.
- It seems that the test data variable is named iv1, use a better name if that is correct.
- DES should not be used for new work, only when necessary to interoperate with existing work, it is not very secure. New work should use AES.
- #synthesize has not been needed for several years.
In cryptography "DESede/CBC/NoPadding" use following code might be help for you.
Byte iv [] = {1, 35, 69, 103, -119, -85, -51, -17};
NSData *ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
NSData key = ["MY_TEST_KEY" dataUsingEncoding:NSUTF8StringEncoding]
char cKey[key.length];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:key.length];
// setup iv
char cIv[kCCBlockSizeDES];
bzero(cIv, kCCBlockSizeDES);
if (ivData) {
[ivData getBytes:cIv length:kCCBlockSizeDES];
}
// setup output buffer
size_t bufferSize = [data length] + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
// do encrypt
size_t encryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
ccNoPadding,
cKey,
key.length,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&encryptedSize);
if (cryptStatus == kCCSuccess) {
resultData = [NSData dataWithBytes:buffer length:encryptedSize];
free(buffer);
} else {
free(buffer);
NSLog(#"[ERROR] failed to encrypt|CCCryptoStatus: %d", cryptStatus);
return nil;
}

AES 128 with CBC

I'm going mad with a simple AES 128 in Objective C and there's no way to get the expected ciphered text in a simple test. Could anyone tell me what am I doing wrong?
Test:
- (void)testAES128_1
{
NSString *testVector = #"6bc1bee22e409f96e93d7e117393172a";
NSString *initVector = #"000102030405060708090A0B0C0D0E0F";
NSString *key = #"2b7e151628aed2a6abf7158809cf4f3c";
NSString *expected = #"7649abac8119b246cee98e9b12e9197d";
NSData *inputData = [self dataFromHexString:testVector];
NSData *keyData = [self dataFromHexString:key];
NSData *expectedData = [self dataFromHexString:expected];
NSData *current = [inputData AES128EncryptWithIV:initVector andKey:key];
// What I get in current = cf2ea38a123be20765eb8c5c56caf224 != expected
BOOL res = [inputData isEqualToData:current];
XCTAssertTrue(res);
}
// For Converting incoming HexString into NSData
- (NSData *)dataFromHexString:(NSString *)string
{
NSMutableData *stringData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [string length] / 2; i++) {
byte_chars[0] = [string characterAtIndex:i*2];
byte_chars[1] = [string characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[stringData appendBytes:&whole_byte length:1];
}
return stringData;
}
Category NSData (AES):
- (NSData *)AES128EncryptWithIV:(NSString *)iv andKey:(NSString *)key {
char ivPtr[kCCKeySizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
// fetch iv data
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0,
keyPtr, kCCKeySizeAES128,
ivPtr /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
I am working with these set of AES test vectors:
http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-cbc-128
Thanks
Simply you are not converting the iv in hex ascii to an NSData. The example test vector have an iv so to obtain the matching cipher text you need to use the Initialization vector.
Note: The line:
BOOL res = [inputData isEqualToData:current];
should be:
BOOL res = [expectedData isEqualToData:current];
Note: The output block count is not larger if there is no padding.
Here is my test code:
No need for a Category, I just make there methods class methods.
+ (void)testAES128_1 {
NSString *testVector = #"6bc1bee22e409f96e93d7e117393172a";
NSString *initVector = #"000102030405060708090A0B0C0D0E0F";
NSString *key = #"2b7e151628aed2a6abf7158809cf4f3c";
NSString *expected = #"7649abac8119b246cee98e9b12e9197d";
NSData *inputData = [self dataFromHexString:testVector];
NSData *keyData = [self dataFromHexString:key];
NSData *ivData = [self dataFromHexString:initVector];
NSData *expectedData = [self dataFromHexString:expected];
NSError *error;
NSData *current = [Test doCipher:inputData
iv:ivData
key:keyData
context:kCCEncrypt
error:&error];
BOOL res = [expectedData isEqualToData:current];
NSLog(#"Match: %#", res ? #"Yes" : #"No"); // Match: Yes
}
+ (NSData *)doCipher:(NSData *)dataIn
iv:(NSData *)iv
key:(NSData *)symmetricKey
context:(CCOperation)encryptOrDecrypt // kCCEncrypt or kCCDecrypt
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
0, //kCCOptionPKCS7Padding,
symmetricKey.bytes,
kCCKeySizeAES128,
iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}

NSData find sequence of bytes

I need to find the sequence of bytes in my image data. I have next code on java, but I need make the same in obj-c.
Java:
private static int searchInBuffer(byte[] pBuf, int iBufferLen) {
for(int i = 0; i<iBufferLen - 7; i++) {
if (pBuf[i] == 'l' && pBuf[i + 1] == 'i' && pBuf[i + 2] == 'n' && pBuf[i + 3] == 'k')
return (int)pBuf[i + 4];
}
return -1;
}
public static int checkFlagInJpeg(String pFullFileName) {
int iRes = -1;
try {
File f = new File(pFullFileName);
FileInputStream is = new FileInputStream(f);
int iBufferSize = 6 * 1024, iCount = 15;
byte buf[] = new byte[iBufferSize];
while((is.available() > 0) && (iCount >= 0)) {
int iRead = is.read(buf),
iFlag = searchInBuffer(buf, iRead);
if (iFlag > 0) {
iRes = iFlag;
break;
}
iCount--;
}
is.close();
}
}
Obj-C (my version):
UIImage *image = [UIImage imageWithCGImage:[[[self.assets objectAtIndex:indexPath.row] defaultRepresentation] fullScreenImage]];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0f);
NSUInteger length = MIN(6*1024, [imageData length]);
Byte *buffer = (Byte *)malloc(length);
memcpy(buffer, [imageData bytes], length);
for (int i=0; i < length - 1; i++) {
if (buffer[i] == 'l' && buffer[i + 1] == 'i' && buffer[i + 2] == 'n' && buffer[i + 3] == 'k')
NSLog(#"%c", buffer[i + 4]);
}
free(buffer);
I'm still not sure, that I understand all aspects of work with bytes, so I need a help.
UPDATE:
The problem was in getting image data. With help of Martin R. I combine to solutions in one and get next working code:
ALAssetRepresentation *repr = [[self.assets objectAtIndex:indexPath.row] defaultRepresentation];
NSUInteger size = (NSUInteger) repr.size;
NSMutableData *data = [NSMutableData dataWithLength:size];
NSError *error;
[repr getBytes:data.mutableBytes fromOffset:0 length:size error:&error];
NSData *pattern = [#"link" dataUsingEncoding:NSUTF8StringEncoding];
NSRange range = [data rangeOfData:pattern options:0 range:NSMakeRange(0, data.length)];
int iRes = -1;
if (range.location != NSNotFound) {
uint8_t flag;
[data getBytes:&flag range:NSMakeRange(range.location + range.length, 1)];
iRes = flag;
}
NSLog(#"%i", iRes);
It's working perfect! Thank you again!
NSData has a method rangeOfData:... which you can use to find the pattern:
NSData *pattern = [#"link" dataUsingEncoding:NSUTF8StringEncoding];
NSRange range = [imageData rangeOfData:pattern options:0 range:NSMakeRange(0, imageData.length)];
If the pattern was found, get the next byte:
int iRes = -1;
if (range.location != NSNotFound && range.location + range.length < imageData.length) {
uint8_t flag;
[imageData getBytes:&flag range:NSMakeRange(range.location + range.length, 1)];
iRes = flag;
}

128 bit AES encryption returns null in Objective-C

I am trying to encrypt a string using AES 128 bit encryption, but the encrypted data converted, when converted to a string, always returns null.
NSString *iv = #"fedcba9876543210";
NSString *key = #"0123456789abcdef";
- (NSData *)AES128EncryptWithKey
{
char keyPtr[kCCKeySizeAES128 + 1];
bzero( keyPtr, sizeof( keyPtr ) );
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL ,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted );
if( cryptStatus == kCCSuccess )
{
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
}
This is my encryption function.
NSData *data = [#"String to encrypt" dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [[NSData alloc] init];
encryptedData = [data AES128EncryptWithKey];
NSLog(#"Encrypted Data Length %d", [encryptedData length]);
if (encryptedData != nil)
{
NSString* myString;
myString = [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding];
NSString* newStr = [NSString stringWithUTF8String:[encryptedData bytes]];
}
After encryption, encryption data length is reported as 16 but after converting it to an NSString it returns null. If I use stringWithUTF16String to convert it, it returns like "軽ﶁែ뼐끨驂퐇". What is the issue here?
try this code
#define kCryptingKey #"1234567890abcdef"
#implementation Tool
+ (NSString*) crypt:(NSString*)recource
{
NSData *data = [recource dataUsingEncoding:NSUTF8StringEncoding];
NSData *encrypt = [self AES128EncryptWithKey:kCryptingKey withData:data];
return [self stringWithHexBytes:encrypt];
}
+ (NSString*) decryptData:(NSData*)recource
{
NSData *decrypt = [self AES128DecryptWithKey:kCryptingKey withData:recource];
return [[NSString alloc] initWithData:decrypt encoding:NSUTF8StringEncoding];
}
+ (NSString*) decrypt:(NSString*)recource
{
NSData* data=[self decodeFromHexidecimal:recource];
NSData *decrypt = [self AES128DecryptWithKey:kCryptingKey withData:data];
return [[NSString alloc] initWithData:decrypt encoding:NSUTF8StringEncoding];
}
+ (NSData *) decodeFromHexidecimal:(NSString*)str
{
NSString *command = [NSString stringWithString:str];
command = [command stringByReplacingOccurrencesOfString:#" " withString:#""];
NSMutableData *commandToSend = [[NSMutableData data] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [command length]/2; i++) {
byte_chars[0] = [command characterAtIndex:i*2];
byte_chars[1] = [command characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[commandToSend appendBytes:&whole_byte length:1];
}
return commandToSend;
}
+ (NSData *)AES128EncryptWithKey:(NSString *)key withData:(NSData*)_data
{
// ‘key’ should be 16 bytes for AES128
char keyPtr[kCCKeySizeAES128 + 1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [_data length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That’s why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[_data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted );
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free( buffer ); //free the buffer
return nil;
}
+ (NSData *)AES128DecryptWithKey:(NSString *)key withData:(NSData*)data
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [data length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode | kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
+ (NSString*) stringWithHexBytes:(NSData*)_data {
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([_data length] * 2)];
const unsigned char *dataBuffer = [_data bytes];
int i;
for (i = 0; i < [_data length]; ++i) {
[stringBuffer appendFormat:#"%02lX", (unsigned long)dataBuffer[i]];
}
return [[stringBuffer copy] autorelease];
}
#end
Finally I got it.. As Joachim suggested I used the following method and it worked..
unichar* hexChars = (unichar*)malloc(sizeof(unichar) * (data.length*2));
unsigned char* bytes = (unsigned char*)data.bytes;
for (NSUInteger i = 0; i < data.length; i++) {
unichar c = bytes[i] / 16;
if (c < 10) c += '0';
else c += 'a' - 10;
hexChars[i*2] = c;
c = bytes[i] % 16;
if (c < 10) c += '0';
else c += 'a' - 10;
hexChars[i*2+1] = c;
}
NSString* retVal = [[NSString alloc] initWithCharactersNoCopy:hexChars
length:data.length*2
freeWhenDone:YES];

Use CCCrypt encrypt/decrypt file

I don't know why the original mp3Data size is 4750347 bytes, and decryptData size is 4750344 bytes
Is there any implemetation error in TripleDES method?
Thanks for any reply.
- (void) testTripleDESMp3
{
NSString * mp3Path = [[self libraryPath]stringByAppendingPathComponent:#"A.mp3"];
NSData *mp3Data = [[NSData alloc] initWithContentsOfFile:mp3Path] ;//4750347
NSData *encryptData = [self TripleDES:mp3Data encryptOrDecrypt:kCCEncrypt key:#"12345"];//bufferPtrSize 4750352, plainTextBufferSize:4750347, movedBytes:47503520 ,NSData 6333804
NSData *decryptData = [self TripleDES:encryptData encryptOrDecrypt:kCCDecrypt key:#"12345"];//bufferPtrSize 4750360, plainTextBufferSize:4750352, movedBytes:4750344,NSData 4750344
//mp3Data 4750347 -> encryptData 6333804-> decryptData 4750344
NSError* error;
NSString * mp3Path2 = [[self libraryPath]stringByAppendingPathComponent:#"B.mp3"];
[decryptData writeToFile:mp3Path2 options:NSDataWritingAtomic error:&error];
}
- (NSData*)TripleDES:(NSData*)plainData encryptOrDecrypt:(CCOperation)encryptOrDecrypt key:(NSString*)key {
const void *vplainText;
size_t plainTextBufferSize;
if (encryptOrDecrypt == kCCDecrypt)
{
NSData *EncryptData = [GTMBase64 decodeData:plainData];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else
{
plainTextBufferSize = [plainData length];
vplainText = (const void *)[plainData bytes];
}
CCCryptorStatus ccStatus;
//I am not sure use char*/void*/uint8_t
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = static_cast<uint8_t *>(malloc( bufferPtrSize * sizeof(uint8_t)));
//bufferPtr = (char *)malloc( bufferPtrSize * sizeof(char));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
// memset((void *) iv, 0x0, (size_t) sizeof(iv));
NSString *initVec = #"init Vec";
const void *vkey = (const void *) [key UTF8String];
const void *vinitVec = (const void *) [initVec UTF8String];
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding,//kCCOptionECBMode,
vkey,
kCCKeySize3DES,
vinitVec, //"init Vec", //iv,
vplainText, //"Your Name", //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
if (ccStatus == kCCSuccess) NSLog(#"SUCCESS");
else if (ccStatus == kCCParamError) NSLog( #"PARAM ERROR");
else if (ccStatus == kCCBufferTooSmall) NSLog( #"BUFFER TOO SMALL");
else if (ccStatus == kCCMemoryFailure) NSLog( #"MEMORY FAILURE");
else if (ccStatus == kCCAlignmentError) NSLog( #"ALIGNMENT");
else if (ccStatus == kCCDecodeError) NSLog( #"DECODE ERROR");
else if (ccStatus == kCCUnimplemented) NSLog( #"UNIMPLEMENTED");
NSData *result;
if (encryptOrDecrypt == kCCDecrypt)
{
result = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
}
else
{
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [GTMBase64 encodeData:myData];
}
//bufferPtrSize 1931240, plainTextBufferSize:1931232, movedBytes:1931232
NSLog(#"bufferPtrSize %d, plainTextBufferSize:%d, movedBytes:%d", (int)bufferPtrSize,(int)plainTextBufferSize, (int)movedBytes);//
return result;
}

Resources