NSData to NSString in ObjectiveC - objective-c

I am converting the APN Device token which is in NSData format to NSString, but i am some special characters,
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"Device Token 111 : %#", deviceToken);
NSString *deviceStr = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];
NSLog(#"Device Token : %#", deviceStr);
[deviceStr release];
}
Device Token 111 : <d8b62879 48de8f9f 90507519 da1d39cf 1b700f7f 022dcaf4 7532a8b7 a6f9afe4>
Device Token : ض(yHÞPuÚ9Ïep-Êôu2¨·¦ù¯ä
I have even tried with NSASCIIStringEncoding.
What am i doing wrong ?

Try using the following method with [deviceToken bytes] as the first parameter.
const static char hexchar[] = "0123456789ABCDEF";
- (NSString*) bytes2hex:(const char* ) buffer length:(int)buf_len {
size_t i;
char *p;
int len = (buf_len * 2) + 1;
p = malloc(len);
for (i = 0; i < buf_len; i++) {
p[i * 2] = hexchar[(unsigned char)buffer[i] >> 4 & 0xf];
p[i * 2 + 1] = hexchar[((unsigned char)buffer[i] ) & 0xf];
}
p[i * 2] = '\0';
NSString * result = [NSString stringWithCString:p encoding:NSUTF8StringEncoding];
free(p);
return result;
}

You may use description method to get the string representation of a token
NSLog(#"Token: [%#]", [devToken description]);
To remove non-numerical characters you may do:
NSCharacterSet *set = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
NSString *tkn = [[[devToken description] componentsSeparatedByCharactersInSet:set] componentsJoinedByString: #""];

Related

How to encrypt with AES 256 CBC in Objective C

I am building an iPhone app which gets a encrypt string and sent it to backend.
In PHP I am encrypting the string like this:
$encrypt_method = "AES-256-CBC";
$secret_key = 'This is my secret key';
$secret_iv = 'This is my secret iv';
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
if( $action == 'encrypt' ) {
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
}
How I do the same like that but in Objective C
#import <CommonCrypto/CommonCryptor.h>
#define key #"YOUR_KEY"
#define iv #"YOUR_IV"
- (NSData *) cryptOperation:(CCOperation)operation
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keys[kCCKeySizeAES256 + 1];
[key getCString:keys maxLength:sizeof(keys) encoding:NSUTF8StringEncoding];
// Perform PKCS7Padding on the key.
unsigned long bytes_to_pad = sizeof(keys) - [key length];
if (bytes_to_pad > 0)
{
char byte = bytes_to_pad;
for (unsigned long i = sizeof(keys) - bytes_to_pad; i < sizeof(keys); i++)
keys[i] = byte;
}
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 numBytesDecrypted = 0;
CCCryptorStatus status = CCCrypt(operation, kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keys, kCCKeySizeAES256,
[iv UTF8String],
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (status == kCCSuccess)
{
//the returned NSData takes ownership of buffer and will free it on dealloc
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
- (NSData *)AES256Encrypt
{
return [self cryptOperation:kCCEncrypt];
}
- (NSData *)AES256Decrypt
{
return [self cryptOperation:kCCDecrypt];
}
you can use this method by following way..
NSString *receivedDataDecryptString = [self decrypt:#"YOUR_STRING"];
NSString *receivedDataEncryptString = [self encrypt:#"YOUR_STRING"];
-(NSString *)encrypt:(NSString *)string
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData *dataEncrypted = [data AES256Encrypt];
NSString *strRecordEncrypted = [dataEncrypted base64EncodedStringWithOptions:0];
return strRecordEncrypted;
}
-(NSString *)decrypt:(NSString *)string
{
if([string containsString:#"\n"] || [string containsString:#"\t"])
{
string = [[string componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
string = [string stringByReplacingOccurrencesOfString:#"\t" withString:#""];
}
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:string options:0];
NSData *dataDecrypted = [keyData AES256Decrypt];
NSString *receivedDataDecryptString = [[NSString alloc]initWithData:dataDecrypted encoding:NSUTF8StringEncoding];
return receivedDataDecryptString;
}
Thanks Nirav Kotecha for your answer.
I ended up using CrytoSwift and Add Extension class NSString and String to call it.

How does AES-128 CBC encryption with IV work in objective-C?

I am trying to encrypt a string with 'AES-128 CBC with IV'. Here is the input parameter and expected output:
Key:
000102030405060708090A0B0C0D0E0F
IV:
00102030405060708090A0B0C0D0E0F0
Input data:
EA010B23CDA9B16F0001020304050607
Output:
B773C36749E87D3F8FED98FE52026A15
I have verified the output on this web site:
http://extranet.cryptomathic.com/aescalc/index?key=000102030405060708090A0B0C0D0E0F&iv=00102030405060708090A0B0C0D0E0F0&input=EA010B23CDA9B16F0001020304050607&mode=cbc&action=Encrypt&output=B773C36749E87D3F8FED98FE52026A15
How to encrypt a string with AES-128 CBC with IV in objective C? (With same result as http://extranet.cryptomathic.com/aescalc) I am trying to get the encrypted string - B773C36749E87D3F8FED98FE52026A15 , but no luck.
I have tried to use this library for the encryption: https://github.com/Pakhee/Cross-platform-AES-encryption/tree/master/iOS
Here is my objective c code:
NSString* data = #"EA010B23CDA9B16F0001020304050607";
NSString* key = #"000102030405060708090A0B0C0D0E0F";
NSString* iv = #"00102030405060708090A0B0C0D0E0F0";
NSData *encryptedData = [[StringEncryption alloc] encrypt:[#"EA010B23CDA9B16F0001020304050607" dataUsingEncoding:NSUTF8StringEncoding] key:key iv:iv];
NSLog(#"encryptedData %#", encryptedData);
The output of encryptedData is:
<68f8ed75 e79f2ba2 c80e67a2 f0c84b7a c4b07fd1 59e937e5 14644cba c0ddb60c 40502375 7798e7a1 58bd05a5 b3d9e7bd>
I expect the value of *encryptedData should be <42373733 43333637 34394538 37443346 38464544 39384645 35323032 36413135>, which is hex of B773C36749E87D3F8FED98FE52026A15
I have tried another library - https://github.com/dev5tec/FBEncryptor
NSData* _data = [data dataUsingEncoding:NSUTF8StringEncoding];
NSData* _key = [key dataUsingEncoding:NSUTF8StringEncoding];
NSData* _iv = [iv dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData2 = [FBEncryptorAES encryptData:_data key:_key iv:_iv];
NSLog(#"encryptedData2 = %#", encryptedData2);
Output is <2beea977 aef69eb1 ed9f6dd0 7bf5f1ce d1e5df46 2cbf8465 773f122d 03267abb 2e113d9b 07189268 4fd6babe 7b1c0056>
It seems that I am using wrong library or I have wrong input to the encryption function. Any recommendation of AES library for objective c?
Common Crypto is the correct thing to use for encryption for iOS and OSX. The issue it to provide the correct input.
The input key, iv and data appear to be in hexadecimal. Cryptomathic expects it inputs to be in hexadecimal and it's output is in hexadecimal so it works correctly.
But the ObjC code uses:
NSString* data = #"EA010B23CDA9B16F0001020304050607";
NSData* _data = [data dataUsingEncoding:NSUTF8StringEncoding];
which uses the hexadecimal as a character string.
Instead use a hex to data conversion such as #Larme linked to, see the first comment.
From the sizes of the input and output it appears you are using PKCS#7 padding which adds a full block of padding if the input is an exact multiple of the block size, Cryptomathic does not add PKCS#7 padding.
Update
#interface Crypt : NSObject
+ (NSData *)aes128Data:(NSData *)dataIn;
+ (NSData *)dataFromHexString:(NSString *)hexString;
#end
#implementation Crypt
+ (NSData *)aes128Data:(NSData *)dataIn
operation:(CCOperation)operation // kCC Encrypt, Decrypt
key:(NSData *)key
options:(CCOptions)options // kCCOption PKCS7Padding, ECBMode,
iv:(NSData *)iv
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
ccStatus = CCCrypt( operation,
kCCAlgorithmAES,
options,
key.bytes, key.length,
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 *)dataFromHexString:(NSString *)hexString {
char buf[3];
buf[2] = '\0';
unsigned char *bytes = malloc([hexString length]/2);
unsigned char *bp = bytes;
for (CFIndex i = 0; i < [hexString length]; i += 2) {
buf[0] = [hexString characterAtIndex:i];
buf[1] = [hexString characterAtIndex:i+1];
char *b2 = NULL;
*bp++ = strtol(buf, &b2, 16);
}
return [NSData dataWithBytesNoCopy:bytes length:[hexString length]/2 freeWhenDone:YES];
}
#end
NSString *dataHexString = #"EA010B23CDA9B16F0001020304050607";
NSString *keyHexString = #"000102030405060708090A0B0C0D0E0F";
NSString *ivHexString = #"00102030405060708090A0B0C0D0E0F0";
NSLog(#"dataHexString: %#", dataHexString);
NSLog(#"keyHexString: %#", keyHexString);
NSLog(#"ivHexString: %#", ivHexString);
NSData *data = [Crypt dataFromHexString:dataHexString];
NSData *key = [Crypt dataFromHexString:keyHexString];
NSData *iv = [Crypt dataFromHexString:ivHexString];
NSLog(#"data: %#", data);
NSLog(#"key: %#", key);
NSLog(#"iv: %#", iv);
NSError *error;
NSData *encryptedData = [Crypt
aes128Data:data
operation:kCCEncrypt
key:key
options:0
iv:iv
error:&error];
NSLog(#"encryptedData %#", encryptedData);
Output:
dataHexString: EA010B23CDA9B16F0001020304050607
keyHexString: 000102030405060708090A0B0C0D0E0F
ivHexString: 00102030405060708090A0B0C0D0E0F0
data: <ea010b23 cda9b16f 00010203 04050607>
key: <00010203 04050607 08090a0b 0c0d0e0f>
iv: <00102030 40506070 8090a0b0 c0d0e0f0>
encryptedData: <b773c367 49e87d3f 8fed98fe 52026a15>
Note encryptedData matches the Cryptomathic result.

NSString to UTF8String error when contains chinese character

I tried to encrypt a data that might contains chinese character, however i kept getting null when I decrypt the string. the way I encrypt the data is derived from our android team, So I wanna keep it the same. It looks like when I call [[NSString alloc] initWithData:dataFrom64 encoding:NSUTF8StringEncoding]; It gives me a NSString representation of an UTF8String. and when I call NSString UTF8String, it returns something unexpected. I tried to print out every thing to see where goes wrong. Sorry for the mess.
I really need help on this. I can't figure out how to solve it.
NSLog(#"--------Test begins--------");
NSString *chinese = #"abcd 測試";
/** encrypt **/
char const *testCStr = [testString UTF8String];
char const *cStr = [chinese UTF8String];
char *newCStr = (char*)calloc(sizeof(char), strlen(cStr));
strcpy(newCStr, cStr);
int lenStr = strlen(cStr);
int lenKey = testString.length;
for (int i = 0, j = 0; i < lenStr; i++, j++) {
if (j >= lenKey) j = 0;
newCStr[i] = cStr[i] ^ testCStr[j];
}
NSString *tempStr = [NSString stringWithUTF8String:[[NSString stringWithFormat:#"%s",newCStr] UTF8String]];
NSData *tempData = [tempStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64Str = [tempData base64EncodedString];
char const *dataCStr = [tempData bytes];
NSString* dataToStr = [[NSString alloc] initWithData:tempData
encoding:NSUTF8StringEncoding];
NSLog(#"chinese : %#", chinese);
NSLog(#"chinese utf8 : %s ", [chinese UTF8String]);
NSLog(#"encrypted utf8 : %s ", newCStr);
NSLog(#"--------Encrypt--------");
NSLog(#"encrypted str : %#", tempStr);
NSLog(#"temp data bytes : %s", dataCStr);
NSLog(#"data to str : %#", dataToStr);
NSLog(#"base64 data : %#", base64Str);
NSLog(#"data temp : %#", tempData );
/** decrypt**/
NSData *dataFrom64 = [NSData dataFromBase64String:base64Str];
NSString *strFromData = [[NSString alloc] initWithData:dataFrom64
encoding:NSUTF8StringEncoding];
char const *cStrFromData = [strFromData UTF8String];
char *newStr2 = (char*)calloc(sizeof(char), strlen(cStrFromData));
strcpy(newStr2, cStrFromData);
for (int i = 0, j = 0; i < lenStr; i++, j++) {
if (j >= lenKey) j = 0;
newStr2[i] = cStrFromData[i] ^ testCStr[j];
}
NSLog(#"--------Decrypt--------");
NSLog(#"data 64 : %#", dataFrom64 );
NSLog(#"data 64 bytes : %s", [dataFrom64 bytes]);
NSLog(#"str from data : %#", strFromData);
NSLog(#"cStr from data : %s", [strFromData UTF8String]);
NSLog(#"decrypt utf8 : %s", newStr2);
NSLog(#"decrypt str : %#", [NSString stringWithUTF8String:newStr2]);
and here is the out put:
--------Test begins--------
chinese : abcd 測試
chinese utf8 : abcd 測試
encrypted utf8 : #!B5aºÄõ–ôá
--------Encrypt--------
encrypted str : #!B5aºÄõ–ôá
temp data bytes : #!B5aºÄõ–ôá6.889 WebSocke
data to str : #!B5aºÄõ–ôá
base64 data : IyFCNWHCusOEw7XigJPDtMOh
data temp : <23214235 61c2bac3 84c3b5e2 8093c3b4 c3a1>
--------Decrypt--------
data 64 : <23214235 61c2bac3 84c3b5e2 8093c3b4 c3a1>
data 64 bytes : #!B5aºÄõ–ôá
str from data : #!B5aºÄõ–ôá
cStr from data : #!B5aºÄõ–ôá
decrypt utf8 : abcd òÇÙºÛî‚Äì√¥√°
decrypt str : (null)
--------test ends--------
The problem is that newCStr is not null-terminated, and does also not represent a valid
UTF-8 string. So this conversion
NSString *tempStr = [NSString stringWithUTF8String:[[NSString stringWithFormat:#"%s",newCStr] UTF8String]];
is bound to fail (or give a wrong result).
The following code avoids unnecessary conversions:
NSLog(#"--------Test begins--------");
NSString *plainText = #"abcd 測試";
NSString *keyString = #"topsecret";
/** encrypt **/
NSMutableData *plainData = [[plainText dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
uint8_t *plainBytes = [plainData mutableBytes];
const uint8_t *keyBytes = [keyData bytes];
for (int i = 0, j = 0; i < [plainData length]; i++, j++) {
if (j >= [keyData length]) j = 0;
plainBytes[i] ^= keyBytes[j];
}
NSString *base64Str = [plainData base64EncodedString];
NSLog(#"chinese : %#", plainText);
NSLog(#"--------Encrypt--------");
NSLog(#"base64 data : %#", base64Str);
/** decrypt**/
NSData *dataFrom64 = [NSData dataFromBase64String:base64Str];
NSMutableData *decodeData = [dataFrom64 mutableCopy];
uint8_t *decodeBytes = [decodeData mutableBytes];
for (int i = 0, j = 0; i < [decodeData length]; i++, j++) {
if (j >= [keyData length]) j = 0;
decodeBytes[i] ^= keyBytes[j];
}
NSString *decrypted = [[NSString alloc] initWithData:decodeData
encoding:NSUTF8StringEncoding];
NSLog(#"--------Decrypt--------");
NSLog(#"decrypt str : %#", decrypted);
Output:
--------Test begins--------
chinese : abcd 測試
--------Encrypt--------
base64 data : FQ0TF0WFysmc3ck=
--------Decrypt--------
decrypt str : abcd 測試

Substring to nth character

I need to substring to the 2nd comma in an NSString.
Input:
NSString *input = #"title, price, Camry, $19798, active";
Desired Output:
NSString *output = #"title, price";
Thanks!
UPDATE:
I have the following but the problem is it needs to skip the last comma:
NSString *output = [input rangeOfString:#"," options:NSBackwardsSearch];
Try this:
- (NSString *)substringOfString:(NSString *)base untilNthOccurrence:(NSInteger)n ofString:(NSString *)delim
{
NSScanner *scanner = [NSScanner scannerWithString:base];
NSInteger i;
for (i = 0; i < n; i++)
{
[scanner scanUpToString:delim intoString:NULL];
[scanner scanString:delim intoString:NULL];
}
return [base substringToIndex:scanner.scanLocation - delim.length];
}
this code should do what you need:
NSString *input = #"title, price, Camry, $19798, active";
NSArray *array = [input componentsSeparatedByString:#","];
NSArray *subArray = [array subarrayWithRange:NSMakeRange(0, 2)];
NSString *output = [subArray componentsJoinedByString:#","];
NSLog(output);
You could split -> splice -> join that string like this in objc:
NSString *input = #"title, price, Camry, $19798, active";
// split by ", "
NSArray *elements = [input componentsSeparatedByString: #", "];
// grab the subarray
NSArray *subelements = [elements subarrayWithRange: NSMakeRange(0, 2)];
// concat by ", " again
NSString *output = [subelements componentsJoinedByString:#", "];
You can try something like this:
NSArray *items = [list componentsSeparatedByString:#", "];
NSString result = #"";
result = [result stringByAppendingString:[items objectAtIndex:0]];
result = [result stringByAppendingString:#", "];
result = [result stringByAppendingString:[items objectAtIndex:1]];
You have to check you have at least two items if you want avoid an exception.
There's really nothing wrong with simply writing the code to do what you want. Eg:
int commaCount = 0;
int i;
for (i = 0; i < input.count; i++) {
if ([input characterAtIndex:i] == (unichar) ',') {
commaCount++;
if (commaCount == 2) break;
}
}
NSString output = nil;
if (commaCount == 2) {
output = [input substringToIndex:i];
}
You could create an NSString category to handle finding nth occurrences of any string. This is example is for ARC.
//NSString+MyExtension.h
#interface NSString(MyExtension)
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string;
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string
options:(NSStringCompareOptions)options;
#end
#implementation NSString(MyExtension)
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string
{
return [self substringToNthOccurrence:nth ofString:string options:0];
}
-(NSString*)substringToNthOccurrence:(NSUInteger)nth
ofString:(NSString*)string
options:(NSStringCompareOptions)options
{
NSUInteger location = 0,
strlength = [string length],
mylength = [self length];
NSRange range = NSMakeRange(location, mylength);
while(nth--)
{
location = [self rangeOfString:string
options:options
range:range].location;
if(location == NSNotFound || (location + strlength) > mylength)
{
return [self copy]; //nth occurrence not found
}
if(nth == 0) strlength = 0; //This prevents the last occurence from being included
range = NSMakeRange(location + strlength, mylength - strlength - location);
}
return [self substringToIndex:location];
}
#end
//main.m
#import "NSString+MyExtension.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSString *output = [#"title, price, Camry, $19798, active" substringToNthOccurrence:2 ofString:#","];
NSLog(#"%#", output);
}
}
*I'll leave it as an exercise for someone to implement the mutable versions.

How to pad NSString with spaces?

For example, I need the NSString have at least 8 chars....instead of using a loop to add the left pad spaces on this, is there anyway to do it?
Examples:
Input: |Output:
Hello | Hello
Bye | Bye
Very Long |Very Long
abc | abc
Here is an example of how you can do it:
int main (int argc, const char * argv[]) {
NSString *str = #"Hello";
int add = 8-[str length];
if (add > 0) {
NSString *pad = [[NSString string] stringByPaddingToLength:add withString:#" " startingAtIndex:0];
str = [pad stringByAppendingString:str];
}
NSLog(#"'%#'", str);
return 0;
}
I just do something like this:
NSLog(#"%*c%#", 14 - theString.length, ' ', theString);
Moreover, 14is the width that you want.
You can use C language printf formatting with -[NSMutableString appendFormat:] and all other NSString "format" methods. It doesn't respect NSString (do formatting on %#), so you need to convert them to ASCII.
String Padding in C
- (NSString *)sample {
NSArray<NSString *> *input = #[#"Hello", #"Bye", #"Very Long", #"abc"];
NSMutableString *output = [[NSMutableString alloc] init];
for (NSString *string in input) {
[output appendFormat:#"%8s\n", string.UTF8String];
}
return output;
}
/*
Return value:
Hello
Bye
Very Long
abc
*/
if you need the same answer in a method, I had to create one for use in my projects. original code by dashblinkenlight
- (NSString *) LeftPadString: (NSString*) originalString LengthAfterPadding: (int)len paddingCharacter: (char) pad
{
int add = (int) (len - originalString.length);
NSString* paddingCharString = [NSString stringWithFormat:#"%c" , pad];
if (add > 0)
{
NSString *pad = [[NSString string] stringByPaddingToLength:add withString: paddingCharString startingAtIndex:0];
return [pad stringByAppendingString:originalString];
}
else
return originalString;
}