Creating iOS SHA1 Hash with UTF-8 characters - objective-c

I'm using this code to create hash from giving string to devolving IOS App.
-(NSString*) sha1:(NSString*)input
{
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, data.length, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x", digest[i]];
return output;
}
my PHP code is
sha1(json_encode($array));
I have string that contain Arabic languages.
when I create hash from English string and compare it with hash created from php code under Ubuntu*strong text* the result will be the same.
but when I create the hash with Arabic character it will and compare it with hash created from php code does't mach the result.
so what is the man problem with this code.
Thanks

input.length is the number of characters, not the number of bytes, the difference is that many bytes under UTF8 encoding are multiple bytes in length.
Replace:
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];
with the NSString method:
NSData *data = [input dataUsingEncoding: NSUTF8StringEncoding];
There is no need for the intermediate const char *cstr.

I found my problem that come from my PHP json_encode($array) function.
so I found this function in http://php.net/manual/en/function.json-encode.php that encode my json with UTF-8 character
function my_json_encode($arr)
{
//convmap since 0x80 char codes so it takes all multibyte codes (above ASCII 127). So such characters are being "hidden" from normal json_encoding
array_walk_recursive($arr, function (&$item, $key) { if (is_string($item)) $item = mb_encode_numericentity($item, array (0x80, 0xffff, 0, 0xffff), 'UTF-8'); });
return mb_decode_numericentity(json_encode($arr), array (0x80, 0xffff, 0, 0xffff), 'UTF-8');
}
EX:
sha1(my_json_encode($newArray)

Related

Hex string to NSString

I have an hex string coming from server, I have to convert that hex string to NSString. I used some of the methods found in this post How to convert an NSString to hex values
I almost got the same text except that Ú appear randomly in some place.
I've change Ú into bold text to be easy to find in the following example.
Can you guys help me what's wrong with the algorithm.
My algorithm
- (NSString *) stringFromHex:(NSString *)str
{
NSMutableData *stringData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [str length] / 2; i++) {
byte_chars[0] = [str characterAtIndex:i*2];
byte_chars[1] = [str characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[stringData appendBytes:&whole_byte length:1];
}
return [[NSString alloc] initWithData:stringData encoding:NSASCIIStringEncoding];
}
Hex string from server
7539686132564968635943666c6348532f564a656261736b526a47585a75734a625a61316c5753475a645575625a5253386f473574347678694c4930695648704c52473170547a4f6b356552da554675394e58672b6943743857524637557748755961634435306936437a7a535a726d6e53664e6e523862433232594a4f593638655464314c6747324763544f56357a736f544c49734e734ada672f77472f6b6b4b48446c644659426d51334d3dda
Normal string from server which is RSA encoded string
u9ha2VIhcYCflcHS/VJebaskRjGXZusJbZa1lWSGZdUubZRS8oG5t4vxiLI0iVHpLRG1pTzOk5eR
UFu9NXg+iCt8WRF7UwHuYacD50i6CzzSZrmnSfNnR8bC22YJOY68eTd1LgG2GcTOV5zsoTLIsNsJ
g/wG/kkKHDldFYBmQ3M=
Normal string converted using my above algorithm
u9ha2VIhcYCflcHS/VJebaskRjGXZusJbZa1lWSGZdUubZRS8oG5t4vxiLI0iVHpLRG1pTzOk5eRÚUFu9NXg+iCt8WRF7UwHuYacD50i6CzzSZrmnSfNnR8bC22YJOY68eTd1LgG2GcTOV5zsoTLIsNsJÚg/wG/kkKHDldFYBmQ3M=Ú
The server seems to convert DA to a space, probably because DA lies outside the ASCII range, which only runs up to 7A due to it using only 7 bits.
The algorithm converts DA to Ú, which seems to be ok when using Latin-1 encoding. Using a different encoding from the list here, such as NSUTF8StringEncoding, might help.

Objective-C SHA2 hash not working correctly with non-ASCII

I am using xcode and this is my sha512 method:
-(NSString*) sha512:(NSString*)input
{
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];
uint8_t digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512(data.bytes, data.length, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++) {
[output appendFormat:#"%02x", digest[i]];
}
return output;
}
When I try to pass the input "test", it returns:
"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"
which matches other sha512 hash tools (including my Java program and "http://hash.online-convert.com/sha512-generator").
However, when I input non-ascii char like "é", it returns something different than all my other sha512 tools.
For input "é", my method returns:
"60313f8521d3016916d876f7ad11cf42a30dfd4ff9bc557f1e2f90e0d37c56b76ab5e42c8a16db20c18086b0d769c08542429c262cc21ee4fba02bfc689a4797"
when other tools (again including my Java program and "http://hash.online-convert.com/sha512-generator") return "9e2ad28633f24451bd4f3c1cb20586a21a44c3aeedbdc01b9cc8fa72917ea7bd689c82b8bf1fef89b911cf8cc46fa2c1ccc10087b2094fd4d3350ecd88526a2c".
Did I miss anything? Any ideas about this?
Thanks!
Create your NSData object like this:
NSData *data = [input dataUsingEncoding:NSUTF8StringEncoding];
Just double checked and it works correctly like that.

how convert [NSData length] to a NSData

ex:
NSData *data = [NSData dataWithContentsOfFile:filePath];
int len = [data length];
if len = 10000,
i hope i can convert 1000 to a NSData look like
char hoperesult[] = {0x10, 0x27, 0x00, 0x00}
and hoperesult[] must always 4 Bytes
So you want the length in 4 little-endian bytes, correct? I think this will do it:
unsigned int len = [data length];
uint32_t little = (uint32_t)NSSwapHostIntToLittle(len);
NSData *byteData = [NSData dataWithBytes:&little length:4];
(Note that most network protocols use big-endian, but you showed little-endian so that's what this does.)
I'm not 100% sure what you mean here, but I think you are attempting to fill hoperesult with the values found in the file at 'filePath'.
struct _hoperesult {
char data[4];
} *hoperesult;
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSUInteger len = [data length];
NSRange offset;
offset.location = 0;
offset.length = sizeof(_hoperesult);
NSData *hoperesultData;
while( (offset.location + offset.length) < len ) {
hoperesultData = [data subdataWithRange:offset];
// the equivalent of your char hoperesult[] content...
hoperesult = [hoperesultData bytes]
}
An instance of NSData can return a pointer to the actual bytes of data using the "bytes" method. It returns a (const void *). You could in theory simply cast [data bytes] to a char * and use the offset directly; or you can do like in the above code and return smaller chucks of NSData.
Hope that helps!

How to display hexadecimal bytes using NSLog

How can I display the following bytes using NSLog?
const void *devTokenBytes = [devToken bytes];
Assuming that devToken is of type NSData * (from the bytes call), you can use the description method on NSData to get a string containing the hexadecimal representation of the data's bytes. See the NSData class reference.
NSLog(#"bytes in hex: %#", [devToken description]);
If you want a hex sequence:
NSMutableString *hex = [NSMutableString stringWithCapacity:[devToken length]];
for (int i=0; i < [devToken length]; i++) {
[hex appendFormat:#"%02x", [devToken bytes][i]];
}

Using Objective C/Cocoa to unescape unicode characters, ie \u1234

Some sites that I am fetching data from are returning UTF-8 strings, with the UTF-8 characters escaped, ie: \u5404\u500b\u90fd
Is there a built in cocoa function that might assist with this or will I have to write my own decoding algorithm.
It's correct that Cocoa does not offer a solution, yet Core Foundation does: CFStringTransform.
CFStringTransform lives in a dusty, remote corner of Mac OS (and iOS) and so it's a little know gem. It is the front end to Apple's ICU compatible string transformation engine. It can perform real magic like transliterations between greek and latin (or about any known scripts), but it can also be used to do mundane tasks like unescaping strings from a crappy server:
NSString *input = #"\\u5404\\u500b\\u90fd";
NSString *convertedString = [input mutableCopy];
CFStringRef transform = CFSTR("Any-Hex/Java");
CFStringTransform((__bridge CFMutableStringRef)convertedString, NULL, transform, YES);
NSLog(#"convertedString: %#", convertedString);
// prints: 各個都, tada!
As I said, CFStringTransform is really powerful. It supports a number of predefined transforms, like case mappings, normalizations or unicode character name conversion. You can even design your own transformations.
I have no idea why Apple does not make it available from Cocoa.
Edit 2015:
OS X 10.11 and iOS 9 add the following method to Foundation:
- (nullable NSString *)stringByApplyingTransform:(NSString *)transform reverse:(BOOL)reverse;
So the example from above becomes...
NSString *input = #"\\u5404\\u500b\\u90fd";
NSString *convertedString = [input stringByApplyingTransform:#"Any-Hex/Java"
reverse:YES];
NSLog(#"convertedString: %#", convertedString);
Thanks #nschmidt for the heads up.
There is no built-in function to do C unescaping.
You can cheat a little with NSPropertyListSerialization since an "old text style" plist supports C escaping via \Uxxxx:
NSString* input = #"ab\"cA\"BC\\u2345\\u0123";
// will cause trouble if you have "abc\\\\uvw"
NSString* esc1 = [input stringByReplacingOccurrencesOfString:#"\\u" withString:#"\\U"];
NSString* esc2 = [esc1 stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
NSString* quoted = [[#"\"" stringByAppendingString:esc2] stringByAppendingString:#"\""];
NSData* data = [quoted dataUsingEncoding:NSUTF8StringEncoding];
NSString* unesc = [NSPropertyListSerialization propertyListFromData:data
mutabilityOption:NSPropertyListImmutable format:NULL
errorDescription:NULL];
assert([unesc isKindOfClass:[NSString class]]);
NSLog(#"Output = %#", unesc);
but mind that this isn't very efficient. It's far better if you write up your own parser. (BTW are you decoding JSON strings? If yes you could use the existing JSON parsers.)
Here's what I ended up writing. Hopefully this will help some people along.
+ (NSString*) unescapeUnicodeString:(NSString*)string
{
// unescape quotes and backwards slash
NSString* unescapedString = [string stringByReplacingOccurrencesOfString:#"\\\"" withString:#"\""];
unescapedString = [unescapedString stringByReplacingOccurrencesOfString:#"\\\\" withString:#"\\"];
// tokenize based on unicode escape char
NSMutableString* tokenizedString = [NSMutableString string];
NSScanner* scanner = [NSScanner scannerWithString:unescapedString];
while ([scanner isAtEnd] == NO)
{
// read up to the first unicode marker
// if a string has been scanned, it's a token
// and should be appended to the tokenized string
NSString* token = #"";
[scanner scanUpToString:#"\\u" intoString:&token];
if (token != nil && token.length > 0)
{
[tokenizedString appendString:token];
continue;
}
// skip two characters to get past the marker
// check if the range of unicode characters is
// beyond the end of the string (could be malformed)
// and if it is, move the scanner to the end
// and skip this token
NSUInteger location = [scanner scanLocation];
NSInteger extra = scanner.string.length - location - 4 - 2;
if (extra < 0)
{
NSRange range = {location, -extra};
[tokenizedString appendString:[scanner.string substringWithRange:range]];
[scanner setScanLocation:location - extra];
continue;
}
// move the location pas the unicode marker
// then read in the next 4 characters
location += 2;
NSRange range = {location, 4};
token = [scanner.string substringWithRange:range];
unichar codeValue = (unichar) strtol([token UTF8String], NULL, 16);
[tokenizedString appendString:[NSString stringWithFormat:#"%C", codeValue]];
// move the scanner past the 4 characters
// then keep scanning
location += 4;
[scanner setScanLocation:location];
}
// done
return tokenizedString;
}
+ (NSString*) escapeUnicodeString:(NSString*)string
{
// lastly escaped quotes and back slash
// note that the backslash has to be escaped before the quote
// otherwise it will end up with an extra backslash
NSString* escapedString = [string stringByReplacingOccurrencesOfString:#"\\" withString:#"\\\\"];
escapedString = [escapedString stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
// convert to encoded unicode
// do this by getting the data for the string
// in UTF16 little endian (for network byte order)
NSData* data = [escapedString dataUsingEncoding:NSUTF16LittleEndianStringEncoding allowLossyConversion:YES];
size_t bytesRead = 0;
const char* bytes = data.bytes;
NSMutableString* encodedString = [NSMutableString string];
// loop through the byte array
// read two bytes at a time, if the bytes
// are above a certain value they are unicode
// otherwise the bytes are ASCII characters
// the %C format will write the character value of bytes
while (bytesRead < data.length)
{
uint16_t code = *((uint16_t*) &bytes[bytesRead]);
if (code > 0x007E)
{
[encodedString appendFormat:#"\\u%04X", code];
}
else
{
[encodedString appendFormat:#"%C", code];
}
bytesRead += sizeof(uint16_t);
}
// done
return encodedString;
}
simple code:
const char *cString = [unicodeStr cStringUsingEncoding:NSUTF8StringEncoding];
NSString *resultStr = [NSString stringWithCString:cString encoding:NSNonLossyASCIIStringEncoding];
from: https://stackoverflow.com/a/7861345