In order to follow the Data Storage Guidelines I must use the below method to add a flag to say to not back it up to iCloud. However, the parameter here is for a NSURL. I need to pass it a NSString like from a line like so
return [[self offlineQueuePath] stringByAppendingPathComponent:#"SHKOfflineQueue.plist"];
Here is the method that takes in a URL.
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
} else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
Anyway, how would I modify the method above to achieve the same while taking in a NSString as a parameter?
Thanks!
You don't need to modify the method. Convert your string to URL.
NSURL *url = [NSURL URLWithString:#"your string"];
Use this method
NSURL *pathURL113= [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#",Your string]];
this is perfect code.
Related
We are facing problem while creating compressed file at iOS Device Document Directory, .tgz file is in Hex string transferring from pin-pad device to iPad iOS App at TCP socket layer. We used below HexToString function to convert that hex string and make file with .tgz. but at the end file is corrupted.
Can anyone please help us here, how to create compress file at iOS level with below hex string ? Please suggest us any code changes.
Note :- we had tried multiple NSStringEncoding technique, like ASCII, Unicode, Utf8, etc.
HEX String:-
1F8B08003058A8620203EDCA3B0A80301045D1594A5660265FB7E036065422A8453282CB57B4B2B112419CD3DCE2BD6966DD8F54925E4A975B62D22551EE741A2A5E199E80BBE8F1681DFDA5270BC6DB60D1398735A0092E0650082F580A53566A6F36F7BFFBFDA39A01841042FCD0062C8057FA00080000
we are using Xcode Version:13.1 and IOS Version 15.1 and above.
//Below function we used for creating .tgz file
//fileName here is abc.tgz which is compress file type
//content here is hex string mention aboved
+ (void)writeToLogFile:(NSString*)content fileName:(NSString*)fileNameString{
content = [NSString stringWithFormat:#"%#",content];
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:fileNameString];
NSData *fileOriginalString = [self HextoString:content];
NSData *fileData = [fileOriginalString dataUsingEncoding:NSASCIIStringEncoding];
***//In alternative we also tried direct hex string to NSData type by calling below commentented method but it still failing
//NSData *fileData = [self dataFromHexString:content];***
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSError *error = nil;
[fileData writeToFile:fileName options:NSDataWritingAtomic error:&error];
NSLog(#"Write returned error: %#", [error localizedDescription]);
});
}
//Below function we used for Hex to String conversion
+(NSString*)HextoString:(NSString*)string{
#try{
NSMutableString * StrResult = [[NSMutableString alloc] init];
int i = 0;
while (i < [string length]){
NSString * hexChar = [string substringWithRange: NSMakeRange(i, 2)];
int value = 0;
sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
[StrResult appendFormat:#"%c", (char)value];
i+=2;
}
return StrResult;
}
#catch (NSException *exception){
[AELoggerManager info:[NSString stringWithFormat:#" %s EXCEPTION ::%#",__FUNCTION__,exception]];
}
}
+ (NSData *)dataFromHexString:(NSString *) string {
if([string length] % 2 == 1){
string = [#"0"stringByAppendingString:string];
}
const char *chars = [string UTF8String];
int i = 0, len = (int)[string length];
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:2];
}
return data;
}
In my small iOS app I have a CSV file that represents an histogram. The content of file is:
label0,value0
...
labelN,valueN
In my ViewController I read the file async with Grand Central Dispatch this way:
//create the channel with which read the CSV file
dispatch_io_t ch = dispatch_io_create_with_path(DISPATCH_IO_STREAM, [[[NSBundle mainBundle] pathForResource:#"histogram1" ofType:#"csv"] UTF8String], O_RDONLY, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int error) {
if(!error){
NSLog(#"Channel created!");
}
});
//read the whole file
dispatch_io_read(ch, 0, SIZE_MAX, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(bool done, dispatch_data_t dataRead, int error) {
if(!error && done){
NSString *ma = ; //???
NSLog(#"%#", ma);
}
});
I'd like to convert the dataRead (of type dispatch_data_t) to NSString to extract the values that represent the bars of the histogram.
There are a number of ways to do this, but the function you're looking for is dispatch_data_apply. Assuming the string you're reading is UTF8, this should work:
#interface NSString (FromDispatchData)
+ (instancetype)stringFromDispatchData: (dispatch_data_t)data;
#end
#implementation NSString (FromDispatchData)
+ (instancetype)stringFromDispatchData: (dispatch_data_t)data
{
if (!data)
return nil;
NSMutableString* str = [NSMutableString string];
dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
[str appendString: [[NSString alloc] initWithBytes:buffer length:size encoding: NSUTF8StringEncoding]];
return true;
});
return [[self class] stringWithString: str];
}
#end
I have googling a lot, but any answer help me with this problem:
Code:
MAIN DECRYPT in XRSA.m
- (NSData *) decryptWithString:(NSString *)content {
return [self RSADecryptData:[content dataUsingEncoding:NSUTF8StringEncoding]];
}
LOAD PRIVATE KEY .p12 in XRSA.m
#pragma mark - Private Key (.p12)
-(BOOL)setPrivateKey:(NSString *)privateKeyPath withPassphrase:(NSString *)password{
NSData *pkcs12key = [NSData dataWithContentsOfFile:privateKeyPath];
NSDictionary* options = NULL;
CFArrayRef importedItems = NULL;
if (password) {
options = [NSDictionary dictionaryWithObjectsAndKeys: password, kSecImportExportPassphrase, nil];
}
OSStatus returnCode = SecPKCS12Import((__bridge CFDataRef) pkcs12key,
(__bridge CFDictionaryRef) options,
&importedItems);
if (returnCode != 0) {
NSLog(#"SecPKCS12Import fail");
return FALSE;
}
NSDictionary* item = (NSDictionary*) CFArrayGetValueAtIndex(importedItems, 0);
SecIdentityRef identity = (__bridge SecIdentityRef) [item objectForKey:(__bridge NSString *) kSecImportItemIdentity];
SecIdentityCopyPrivateKey(identity, &privateKey);
if (privateKey == nil) {
NSLog(#"SecIdentityCopyPrivateKey fail");
return FALSE;
}
return TRUE;
}
Decrypt message in XRSA.m
#pragma mark - RSA Decryption
-(NSData *)RSADecryptData:(NSData *)content{
NSAssert(privateKey != nil,#"Private key can not be nil");
size_t cipherLen = content.length;
void *cipher = malloc(cipherLen);
[content getBytes:cipher length:cipherLen];
size_t plainLen = SecKeyGetBlockSize(privateKey) - 12;
void *plain = malloc(plainLen);
//SecKeyDecrypt(<#SecKeyRef key#>, <#SecPadding padding#>, <#const uint8_t *cipherText#>, <#size_t cipherTextLen#>, <#uint8_t *plainText#>, <#size_t *plainTextLen#>)
OSStatus returnCode = SecKeyDecrypt(privateKey, kSecPaddingPKCS1, cipher,cipherLen, plain, &plainLen);
NSData *result = nil;
if (returnCode != 0) {
NSLog(#"SecKeyDecrypt fail. Error Code: %d", (int)returnCode);
}
else {
result = [NSData dataWithBytes:plain
length:plainLen];
}
free(plain);
free(cipher);
return result;
}
in ViewControler.m:
NSString *privatekeyPath = [[NSBundle mainBundle] pathForResource:#"private_key" ofType:#"p12"];
XRSA *rsa2 = [XRSA alloc];
if([rsa2 setPrivateKey:privatekeyPath withPassphrase:#"Xs23tg"]){
NSString *data = #"UKFpmRmyu1TUZLqcgHmCEGnHaT7+0j5fAaf57xzVR2/j/Qe0j+b5Lez7wya3jlARfzRuHSSZctsGs4gK2JX2LEqHmQLX2zRhLSSzyMlLnYPF8X4pjbDY5agjPlWf4FpFJnmwGr2XjdqRJzPZ9NvEJAns5dNKAh0lQ3nc3kDppfg=";
[rsa2 decryptWithString:data];
}
else{
}
In RSADecryptData fuction, OSStaus is always return error code -9809.
Any ideas?
Thanks for your time.
There are a couple of possibilities:
In the line [content getBytes:cipher length:cipherLen]; you are not assigning that result to anything. Perhaps assign it to a const uint8_t * and pass into the SecKeyDecrypt function instead of content.
You should check to ensure that the cipherLen is less than the plainLen value. You didn't mention your key length, but that could be the cause of the failure. If you need to support larger message, you will need to decrypt in smaller chunk and iterate over your cipher.
I am trying to find the total size of the documents directory of my IOS app. I believe it is the value that I see in the Settings app under usage (documents and data, this value now is 40MB). By using this method:
-(int)sizeOfFolder:(NSString *)folderPath
{
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *contentsEnumurator = [contents objectEnumerator];
NSString *file;
unsigned long long int folderSize = 0;
while (file = [contentsEnumurator nextObject]) {
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}
//This line will give you formatted size from bytes ....
//NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile];
NSLog(#"size: %llu", folderSize);
return folderSize;
}
I get always the same value. This is the path I pass onto the method:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* rootPath = [paths objectAtIndex:0];
int space = [self sizeOfFolder:rootPath];
First of all you should be careful with file types. You should use long long to be able to calculate the total size:
unsigned long long folderSize = 0; // remove int here
// ...
folderSize += [[fileAttributes objectForKey:NSFileSize] longLongValue]; // note longLongValue
Next you should skip directories to get more accurate results
if (![fileAttirbutes[NSFileType] equalsToString:NSFileTypeDirectory]) {
folderSize += ...
}
I would recommend you to look at another directory enumeration approach. Using - (NSDirectoryEnumerator *)enumeratorAtURL:(NSURL *)url includingPropertiesForKeys:(NSArray *)keys options:(NSDirectoryEnumerationOptions)mask errorHandler:(BOOL (^)(NSURL *url, NSError *error))handler will allow you to perform deeper directory enumeration and list all directory contents recursively
unsigned long long count = 0;
NSNumber *value = nil;
for (NSURL *url in [[NSFileManager defaultManager] enumeratorAtURL:directoryURL includingPropertiesForKeys:#[NSURLFileSizeKey] options:0 errorHandler:NULL]) {
if ([url getResourceValue:&value forKey:NSURLFileSizeKey error:outError]) {
count += [value longLongValue];
} else {
return nil;
}
}
return #(count);
NSFileManager has no capabilities of returning the size of a folder(a folder + contents). Found this on Internet.
- (unsigned long long) fastFolderSizeAtFSRef:(FSRef*)theFileRef {
FSIterator thisDirEnum = NULL;
unsigned long long totalSize = 0;
// Iterate the directory contents, recursing as necessary
if (FSOpenIterator(theFileRef, kFSIterateFlat, &thisDirEnum) == noErr)
{
const ItemCount kMaxEntriesPerFetch = 256;
ItemCount actualFetched;
FSRef fetchedRefs[kMaxEntriesPerFetch];
FSCatalogInfo fetchedInfos[kMaxEntriesPerFetch];
// DCJ Note right now this is only fetching data fork sizes... if we decide to include
// resource forks we will have to add kFSCatInfoRsrcSizes
OSErr fsErr = FSGetCatalogInfoBulk(thisDirEnum, kMaxEntriesPerFetch, &actualFetched,
NULL, kFSCatInfoDataSizes | kFSCatInfoNodeFlags, fetchedInfos,
fetchedRefs, NULL, NULL);
while ((fsErr == noErr) || (fsErr == errFSNoMoreItems))
{
ItemCount thisIndex;
for (thisIndex = 0; thisIndex < actualFetched; thisIndex++)
{
// Recurse if it's a folder
if (fetchedInfos[thisIndex].nodeFlags & kFSNodeIsDirectoryMask)
{
totalSize += [self fastFolderSizeAtFSRef:&fetchedRefs[thisIndex]];
}
else
{
totalSize += fetchedInfos [thisIndex].dataLogicalSize;
}
}
if (fsErr == errFSNoMoreItems)
{
break;
}
else
{
// get more items
fsErr = FSGetCatalogInfoBulk(thisDirEnum, kMaxEntriesPerFetch, &actualFetched,
NULL, kFSCatInfoDataSizes | kFSCatInfoNodeFlags, fetchedInfos,
fetchedRefs, NULL, NULL);
}
}
FSCloseIterator(thisDirEnum);
}
return totalSize; }
I implemented a category method on the NSData class which returns a signature of the data using an SHA-1 hash and subsequent encryption with a private key as follows:
- (NSData *)signatureWithKey:(SecKeyRef)keyRef {
if (keyRef == NULL) {
return nil;
}
NSData *sha1Digest = [self dataWithSHA1Digest];
size_t maxLength = SecKeyGetBlockSize(keyRef) - 11;
if ([sha1Digest length] > maxLength) {
NSString *reason = [NSString stringWithFormat:#"Digest is too long to sign with this key, max length is %ld and actual length is %ld", maxLength, (unsigned long)[self length]];
NSException *ex = [NSException exceptionWithName:#"BMInvalidArgumentException" reason:reason userInfo:nil];
#throw ex;
}
#if TARGET_OS_IPHONE
OSStatus status = noErr;
uint8_t *plainBuffer = (uint8_t *)[sha1Digest bytes];
size_t plainBufferSize = [sha1Digest length];
size_t cipherBufferSize = SecKeyGetBlockSize(keyRef);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
status = SecKeyRawSign(keyRef,
kSecPaddingPKCS1SHA1,
plainBuffer,
plainBufferSize,
&cipherBuffer[0],
&cipherBufferSize
);
if (status == noErr) {
return [NSData dataWithBytesNoCopy:cipherBuffer length:cipherBufferSize freeWhenDone:YES];
}
free(cipherBuffer);
return nil;
#else
CFErrorRef error = NULL;
SecTransformRef signer = NULL;
CFTypeRef signature = NULL;
if ((signer = SecSignTransformCreate(keyRef, &error))) {
if (SecTransformSetAttribute(
signer,
kSecTransformInputAttributeName,
(CFDataRef)sha1Digest,
&error)) {
signature = SecTransformExecute(signer, &error);
}
}
if (error) {
LogWarn(#"Could not sign: %#", error);
CFRelease(error);
}
if (signer) {
CFRelease(signer);
}
if (signature) {
NSData *data = [NSData dataWithData:(NSData *)signature];
CFRelease(signature);
return data;
} else {
return nil;
}
#endif
}
Now the strange thing is that with the same private key (loaded from a p12 file) I get two different results for iOS and MacOSX when signing the same data. I am completely puzzled by this. You may notice the method above uses a different implementation for MacOSX using security transforms, but even if I use the iOS implementation on MacOSX (which gives a compile warning but works fine) I get the same result.
The method used for loading the private key from file is below:
+ (SecKeyRef)newPrivateKeyRefWithPassword:(NSString *)password fromData:(NSData *)data {
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
SecKeyRef privateKeyRef = NULL;
// Set the public key query dictionary
//change to your .pfx password here
[options setObject:password forKey:(id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((CFDataRef)data,
(CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp =
(SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
[options release];
if (items) CFRelease(items);
return privateKeyRef;
}
And this is the test case I use. Notice that two different strings are printed on iOS and MacOSX:
NSString *test = #"bla";
NSData *testData = [test dataUsingEncoding:NSUTF8StringEncoding];
NSString *p12Path= [[NSBundle mainBundle] pathForResource:#"private_key" ofType:#"p12"];
NSData *p12Data = [NSData dataWithContentsOfFile:p12Path];
SecKeyRef keyRef = [BMSecurityHelper newPrivateKeyRefWithPassword:#"xxxxxxxx" fromData:p12Data];
NSData *signatureData = [testData signatureWithKey:keyRef];
NSString *signatureString = [BMEncodingHelper base64EncodedStringForData:signatureData withLineLength:0];
if (keyRef) CFRelease(keyRef);
NSLog(#"signatureString: %#", signatureString);
It's always nice if you can answer your own question. I missed the following: under MacOSX the security transform also calculates the SHA-1 hash automatically, in contrast with the iOS implementation.
I fixed the problem by adding the following in the MacOSX implementation:
SecTransformSetAttribute(signer, kSecInputIsAttributeName, kSecInputIsDigest, &error)