I'm trying to convert an NSString to uint8_t. The problem I'm having is that the NSString and the resulting uint8_t variables do not match. Here is some example code:
NSLog(#"Key now: %#", key);
NSData* keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
const uint8_t *plainBuffer = (const uint8_t*)[data bytes];
size_t plainBufferSize = strlen((char *) plainBuffer);
NSLog(#"Plain buffer: %s", plainBuffer);
NSData* testData = [[NSData alloc] initWithBytes: plainBuffer length:plainBufferSize];
NSString* testString = [[NSString alloc] initWithData: testData encoding: NSUTF8StringEncoding];
NSLog(#"Test string: %#", testString);
And example output:
Key now: 9iIWBpf5R6yu5pJ93l218RsMdWBLidXt
Plain buffer: 9iIWBpf5R6yu5pJ93l218RsMdWBLidXtMdWBLidXt
Test string: 9iIWBpf5R6yu5pJ93l218RsMdWBLidXtMdWBLidXt
Clearly its the NSData -> uint8_t step thats going wrong, but I don't know why!
You're using strlen() to get the size of an NSData*. That's not going to work. The NSData* isn't NUL-terminated. So you're getting garbage on the end.
Don't use strlen(). Just ask the NSData* for its size directly.
Alternatively, don't use NSData* at all and just ask for [key UTF8String]. That hands back a NUL-terminated const char *.
Related
Trying to get the digest using HMac SHA256 with below code but every time it is giving different output.
Here key parameter is in Base64string format while plaintext parameter is without any encoding.
+(NSString *)hmacWithIndicies:(NSString *)plaintext withKey:(NSString *)key {
NSLog(#"Input text::%#",plaintext);
NSLog(#"Input Key::%#",key);
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:key options:0];
NSLog(#"Key Data is::%#",keyData);
const char *cKey = (char *)[keyData bytes];
NSLog(#"Key Length is::%lu",strlen(cKey));
NSData *keyInData = [NSData dataWithBytes:cKey length:sizeof(cKey)];
NSLog(#"Key data = %#", keyInData);
//Data here
const char *cData = [plaintext cStringUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Input Length is::%lu",strlen(cData));
NSData *dataData = [NSData dataWithBytes:cData length:sizeof(cData)];
NSLog(#"Input data = %#", dataData);
uint8_t cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *hMacInData =[[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSLog(#"Hash Mac data generated is %#", hMacInData);
NSString *b64EncStrHmac = [hMacInData base64EncodedStringWithOptions:0];
NSLog(#"Hash Mac generated is %#", b64EncStrHmac);
return b64EncStrHmac;
}
Calling the above method as below:-
NSString * hMacOutput= [KeyGeneration hmacWithIndicies:#"2SagarPra2983688" withKey:#"qDwki5t1SSuKER4mzSMBHXhtt+PRMCv0B2LgXaBZmgE="];
NSLog(#"Output of HMac digest::%#",hMacOutput);
hMacOutput digest is resulting in different output every time it is being called.
You can not use strlen() on non "C" strings, "C" strings are null terminated strings that do not contain any 0x00 bytes. strlen() counts until it finds the first 0x00 byte, on data bytes that could be early or past the end of the data, possible causing a crash.
You are trying to hard, there is no reason for "C" style arrays, just use the bytes member of NSData and NSMutableData along with the length method.
[NSMutableData dataWithLength: ] allocates memory.
Example:
+(NSString *)hmacWithIndicies:(NSString *)plaintext withKey:(NSString *)key {
NSLog(#"Input text: %#", plaintext);
NSLog(#"Input Key: %#", key);
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:key options:0];
NSLog(#"keyData Length: %lu, Data: %#", keyData.length, keyData);
NSData *inData = [plaintext dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"inData Length: %lu, Data: %#", inData.length, inData);
NSMutableData *HMACdata = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, keyData.bytes, keyData.length, inData.bytes, inData.length, (void *)HMACdata.bytes);
NSLog(#"Hash Mac data generated: %#", HMACdata);
NSString *b64EncStrHmac = [HMACdata base64EncodedStringWithOptions:0];
NSLog(#"Hash Mac generated: %#", b64EncStrHmac);
return b64EncStrHmac;
}
Output:
Input text: 2SagarPra2983688
Input Key: qDwki5t1SSuKER4mzSMBHXhtt+PRMCv0B2LgXaBZmgE=
keyData Length: 32, Data: a83c248b 9b75492b 8a111e26 cd23011d 786db7e3 d1302bf4 0762e05d a0599a01
inData Length: 16, Data: 32536167 61725072 61323938 33363838
Hash Mac data generated: b681d2b1 251f1953 3716258c 8eeb9101 db3ecad2 c4a5077e 0cf76617 e45e5459
Hash Mac generated: toHSsSUfGVM3FiWMjuuRAds+ytLEpQd+DPdmF+ReVFk=
Output of HMac digest::toHSsSUfGVM3FiWMjuuRAds+ytLEpQd+DPdmF+ReVFk=
It is not possible to use strlen for binary data. As the key of HMAC can be of any size you may be using more bytes than the key actually contains. If the key changes each time, you will get different output. You need to retrieve the size of the key from keyData, not from a cKey.
For Encryption using following function:
https://gist.github.com/Veelian/7541502
Whenever I read file in binary and apply encryption on it,
I get some encrypted text of that file but whenever I execute
same above operation again then i will get different encrypted text
File is same contents of file also same, still getting different encrypted text
if execute same operation again.
Please find the below code
NSString *filePath = #"/Users/xyx/Desktop/NOTICE.txt";
const char *cfilePath = [filePath cStringUsingEncoding:NSASCIIStringEncoding];
FILE *fileStream= fopen (cfilePath,"rb");
NSDictionary *properties = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
NSNumber *fileSize = [properties objectForKey: NSFileSize];
long l_fileSize = [fileSize longValue];
temp = (char*) malloc (sizeof(char)*l_fileSize);
fgetpos(fileStream,&pos);
int bytesToRead = (int)l_fileSize;
size_t byt=fread(temp,1,bytesToRead,fileStream);
buffer = [NSData dataWithBytes:(const void *)temp length:sizeof(char)*byt];
NSLog(#"Buffer : %#",buffer);
BytesRead = buffer.length;
NSData *encryptedBytes=[buffer AES256EncryptWithKey:[Security key]]; //taking key from other class
size_t length = [encryptedBytes length] + 1;
unsigned char aBuffer[length];
[encryptedBytes getBytes:aBuffer length:length];
NSString *encBytes = [NSString stringWithFormat:#"%s", aBuffer];
NSLog(#"ENcrypted Bytes : %#",encBytes);
I would like to sign a request with HMAC SHA512, but I seem to mess up encoding and decoding from and to NSData and NSString. I desperately tried to figure out what is wrong, but I just don't seem to get it right.
PSEUDOCODE:
function hmac_512(msg, sec) {
sec = Base64Decode(sec);
result = hmac(msg, sec, sha512);
return Base64Encode(result);
}
secret = "7pgj8Dm6";
message = "Test\0Message";
result = hmac_512(message, secret);
if (result == "69H45OZkKcmR9LOszbajUUPGkGT8IqasGPAWqW/1stGC2Mex2qhIB6aDbuoy7eGfMsaZiU8Y0lO3mQxlsWNPrw==")
print("Success!");
else
printf("Error: %s", result);
My implementation:
+(void)doSomeMagic{
NSString *message = #"Test\0Message";
NSString *signedRequest = [self signRequestForParameterString:message];
//checking against CORRECT (from JAVA equivalent) signed request
if ([signedRequest isEqualToString:#"69H45OZkKcmR9LOszbajUUPGkGT8IqasGPAWqW/1stGC2Mex2qhIB6aDbuoy7eGfMsaZiU8Y0lO3mQxlsWNPrw==" ])
NSLog(#"Success!");
else
NSLog(#"Error!");
}
Here is the signing method:
+(NSString *)signRequestForParameterString:(NSString*)paramStr{
NSString *secret = #"7pgj8Dm6";
// secret is base64 encoded, so I decode it
NSData *decodedSecret = [secret base64DecodedData];
NSString *decodedSecretString = [NSString stringWithUTF8String:[decodedSecret bytes]];
NSData *data = [paramStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *dataString = [NSString stringWithUTF8String:[data bytes]];
return [self generateHMACSHA512Hash:decodedSecretString data:dataString];
}
Here is the hashing function:
+(NSString *)generateHMACSHA512Hash:(NSString *)key data:(NSString *)data{
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA512, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC
length:sizeof(cHMAC)];
NSString *hash = [HMAC base64EncodedString];
return hash;
}
I am pretty sure it is due to the encoding of the strings (decodedSecretString and dataString). decodedSecretString (decoded base64) after decoding is encoded in ASCII. However, when I call the hashing method, I encode it in ascii again, which will result in a null error. Everything is confusing me right now.
Your secret doesn't decode to a valid UTF-8 string, and Java allows NUL bytes in strings, but when you're converting "Test\0Message" to a C string and using strlen, its length is 4.
Something like this should work:
+(NSString *)signRequestForParameterString:(NSString*)paramStr{
NSString *secret = #"7pgj8Dm6";
NSData *data = [paramStr dataUsingEncoding:NSUTF8StringEncoding];
return [self generateHMACSHA512Hash:[secret base64DecodedData] data:data];
}
+(NSString *)generateHMACSHA512Hash:(NSData *)key data:(NSData *)data{
unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA512, key.bytes, key.length, data.bytes, data.length, cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
return [HMAC base64EncodedString];
}
When doing HMAC or other cryptographic functions, you should build up some fundamental methods/functions that don't deal with strings first. Then you can create wrapper methods that decode/encode string data or digests in a convenient way.
+ (NSData *)dataBySigningData:(NSData *)data withKey:(NSData *)key
{
unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA512, [key bytes], [key length], [data bytes], [data lenght], cHMAC);
return [[NSData alloc] initWithBytes:cHMAC length:CC_SHA512_DIGEST_LENGTH];
}
+ (NSData *)dataBySigningMessage:(NSString *)message withKey:(NSData *)key
{
return [self dataBySigningData:[message dataUsingEncoding:NSUTF8StringEncoding]
withKey:[key dataUsingEncoding:NSUTF8StringEncoding]];
}
(Note: this code is not tested, just hacked together in a text editor)
Don't worry about the string representation of your key or data. Then you can go from there, e.g. getting the base64 encoding of the digest.
Cryptographic functions DO NOT CARE about strings or text encodings. They care about bytes. Strings (in C, since they are null-terminated) are a mere subset of what can be represented in data. So it would be severely limiting to deal with strings.
I use below code to encode and decode a string on objective C. The encoding is good, I debug and see that it throw a hash string when input is #"1". But when I try to decode this hash string, it return nil.
Please help me.
+(NSString *)encrypt: (NSString*) input
{
//Base64 Encoding
char base64Result[32];
size_t theResultLength = 32;
Base64EncodeData(input, 20, base64Result, &theResultLength);
NSData *theData = [NSData dataWithBytes:base64Result length:theResultLength];
NSString *base64EncodedResult = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
NSString* decryptedString = [self decrypt:base64EncodedResult];
return [base64EncodedResult autorelease];
}
+ (NSString *) decrypt:(NSString*) input{
Byte inputData[[input lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[input dataUsingEncoding:NSUTF8StringEncoding] getBytes:inputData];//get the pointer of the data
size_t inputDataSize = (size_t)[input length];
size_t outputDataSize = EstimateBas64DecodedDataSize(inputDataSize);//calculate the decoded data size
Byte outputData[outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(inputData, inputDataSize, outputData, &outputDataSize);//decode the data
NSData *theData = [[NSData alloc] initWithBytes:outputData length:outputDataSize];//create a NSData object from the decoded data
NSString *result = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
return [result autorelease];
}
In you encoding method, you have to convert the input string to a byte buffer and feed that to Base64EncodeData:
NSData *inputData = [input dataUsingEncoding:NSUTF8StringEncoding];
Base64EncodeData([inputData bytes], [inputData length], base64Result, &theResultLength, NO);
(The NSString *input argument in the encoding method points to an Objective-C structure, not to a C string. So your encoding method seems to work. It encodes something, but not the input string. The decoding method then fails at
NSString *result = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
because the decoded data does not contain valid UTF-8.)
I'm trying to use the BEncoding ObjC class to decode a .torrent file.
NSData *rawdata = [NSData dataWithContentsOfFile:#"/path/to/the.torrent"];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
When I NSLog torrent I get the following:
{
announce = <68747470 3a2f2f74 6f727265 6e742e75 62756e74 752e636f 6d3a3639 36392f61 6e6e6f75 6e6365>;
comment = <5562756e 74752043 44207265 6c656173 65732e75 62756e74 752e636f 6d>;
"creation date" = 1225365524;
info = {
length = 732766208;
name = <7562756e 74752d38 2e31302d 6465736b 746f702d 69333836 2e69736f>;
"piece length" = 524288;
....
How do I convert the name into a NSString? I have tried..
NSData *info = [torrent valueForKey:#"info"];
NSData *name = [info valueForKey:#"name"];
unsigned char aBuffer[[name length]];
[name getBytes:aBuffer length:[name length]];
NSLog(#"File name: %s", aBuffer);
..which retrives the data, but seems to have additional unicode rubbish after it:
File name: ubuntu-8.10-desktop-i386.iso)
I have also tried (from here)..
NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length] / sizeof(unichar)];
..but this seems to return a bunch of random characters:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
The fact the first way (as mentioned in the Apple documentation) returns most of the data correctly, with some additional bytes makes me think it might be an error in the BEncoding library.. but my lack of knowledge about ObjC is more likely to be at fault..
That's an important point that should be re-emphasized I think. It turns out that,
NSString *content = [NSString stringWithUTF8String:[responseData bytes]];
is not the same as,
NSString *content = [[NSString alloc] initWithBytes:[responseData bytes]
length:[responseData length] encoding: NSUTF8StringEncoding];
the first expects a NULL terminated byte string, the second doesn't. In the above two cases content will be NULL in the first example if the byte string isn't correctly terminated.
How about
NSString *content = [[[NSString alloc] initWithData:myData
encoding:NSUTF8StringEncoding] autorelease];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
When I NSLog torrent I get the following:
{
⋮
}
That would be an NSDictionary, then, not an NSData.
unsigned char aBuffer[[name length]];
[name getBytes:aBuffer length:[name length]];
NSLog(#"File name: %s", aBuffer);
..which retrives the data, but seems to have additional unicode rubbish after it:
File name: ubuntu-8.10-desktop-i386.iso)
No, it retrieved the filename just fine; you simply printed it incorrectly. %s takes a C string, which is null-terminated; the bytes of a data object are not null-terminated (they are just bytes, not necessarily characters in any encoding, and 0—which is null as a character—is a perfectly valid byte). You would have to allocate one more character, and set the last one in the array to 0:
size_t length = [name length] + 1;
unsigned char aBuffer[length];
[name getBytes:aBuffer length:length];
aBuffer[length - 1] = 0;
NSLog(#"File name: %s", aBuffer);
But null-terminating the data in an NSData object is wrong (except when you really do need a C string). I'll get to the right way in a moment.
I have also tried […]..
NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length] / sizeof(unichar)];
..but this seems to return random Chinese characters:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
That's because your bytes are UTF-8, which encodes one character in (usually) one byte.
unichar is, and stringWithCharacters:length: accepts, UTF-16. In that encoding, one character is (usually) two bytes. (Hence the division by sizeof(unichar): it divides the number of bytes by 2 to get the number of characters.)
So you said “here's some UTF-16 data”, and it went and made characters from every two bytes; each pair of bytes was supposed to be two characters, not one, so you got garbage (which turned out to be mostly CJK ideographs).
You answered your own question pretty well, except that stringWithUTF8String: is simpler than stringWithCString:encoding: for UTF-8-encoded strings.
However, when you have the length (as you do when you have an NSData), it is even easier—and more proper—to use initWithBytes:length:encoding:. It's easier because it does not require null-terminated data; it simply uses the length you already have. (Don't forget to release or autorelease it.)
A nice quick and dirty approach is to use NSString's stringWithFormat initializer to help you out. One of the less-often used features of string formatting is the ability to specify a mximum string length when outputting a string. Using this handy feature allows you to convert NSData into a string pretty easily:
NSData *myData = [self getDataFromSomewhere];
NSString *string = [NSString stringWithFormat:#"%.*s", [myData length], [myData bytes]];
If you want to output it to the log, it can be even easier:
NSLog(#"my Data: %.*s", [myData length], [myData bytes]);
Aha, the NSString method stringWithCString works correctly:
With the bencoding.h/.m files added to your project, the complete .m file:
#import <Foundation/Foundation.h>
#import "BEncoding.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Read raw file, and de-bencode
NSData *rawdata = [NSData dataWithContentsOfFile:#"/path/to/a.torrent"];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
// Get the file name
NSData *infoData = [torrent valueForKey:#"info"];
NSData *nameData = [infoData valueForKey:#"name"];
NSString *filename = [NSString stringWithCString:[nameData bytes] encoding:NSUTF8StringEncoding];
NSLog(#"%#", filename);
[pool drain];
return 0;
}
..and the output:
ubuntu-8.10-desktop-i386.iso
In cases where I don't have control over the data being transformed into a string, such as reading from the network, I prefer to use NSString -initWithBytes:length:encoding: so that I'm not dependent upon having a NULL terminated string in order to get defined results. Note that Apple's documentation says if cString is not a NULL terminated string, that the results are undefined.
Use a category on NSData:
NSData+NSString.h
#interface NSData (NSString)
- (NSString *)toString;
#end
NSData+NSString.m
#import "NSData+NSString.h"
#implementation NSData (NSString)
- (NSString *)toString
{
Byte *dataPointer = (Byte *)[self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:0];
NSUInteger index;
for (index = 0; index < [self length]; index++)
{
[result appendFormat:#"0x%02x,", dataPointer[index]];
}
return result;
}
#end
Then just NSLog(#"Data is %#", [nsData toString])"
You can try this. Fine with me.
DLog(#"responeData: %#", [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]);
Sometimes you need to create Base64 encoded string from NSData. For instance, when you create a e-mail MIME. In this case use the following:
#import "NSData+Base64.h"
NSString *string = [data base64EncodedString];
This will work.
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];