I have .Net web service response containing a byte[] entry, among other fields.
The data is a PDF file.
I extract the Dictionary from the received data with:
[NSJSONSerialization JSONObjectWithData]
Hereafter I use the following code to convert the byte[] to NSData.
I then save the result to disk (see last line).
When opening the resulting PDF file, I get the following error:
"failed to find PDF header: `%PDF' not found."
NSArray *byteArray = [rootDictionary objectForKey:#"file"];
unsigned c = byteArray.count;
uint8_t *bytes = malloc(sizeof(*bytes) * c);
unsigned i;
for (i = 0; i < c; i++)
{
NSString *str = [byteArray objectAtIndex:i];
int byte = [str intValue];
bytes[i] = (uint8_t)byte;
}
NSData* data = [NSData dataWithBytes:(const void *)byteArray length:sizeof(unsigned char)*c];
//Save to disk using svc class.
NSString *localPath = [svc saveReport:data ToFile:[rootDictionary objectForKey:#"name"]];
I also tried converting the byte[] to a base64 NSString (on the service side) and then back to NSData in my app, which worked (**mostly) but I was told that it's sloppy code.
** When pulling multiple PDF asynchronously at the same time, some of these reports received as base64 strings were also corrupted.
PS. Please let me know if I must supply the code from my svc class as well, but I don't think the problem is there.
Edit:
I created a new web service method which takes a byte[] as input, then modified my iOS app to send the byteArray variable back to the service, where it get's saved to a file.
The resulting PDF file is a valid file readable by Adobe. Meaning there is no corruption during transfer.
Thank you!
O.k, finally sorted this out after fine-tooth-combing my code (as inspired by snadeep.gvn from http://www.raywenderlich.com/forums/viewtopic.php?f=2&p=38590#p38590).
I made a stupid mistake, which I overlooked 100+ times.
This line of code:
NSData* data = [NSData dataWithBytes:(const void *)byteArray length:sizeof(unsigned char)*c];
Should change to:
NSData* data = [NSData dataWithBytes:(const void *)bytes length:sizeof(unsigned char)*c];
Good times, now I can finally get some sleep :-)
Related
I'm receiving data in a http header from a server which is base64 encoded and am having a problem decoding it. I've got some existing java code which I've converted to Objective-C and have determined the reason for the base 64 decoding issue is due to a difference in the UTF-8 decoding results between java and Objective-C. Here's a a java snippet replicating the java conversion:
import java.util.Base64;
public class HelloWorld{
public static void main(String []args){
System.out.println("Hello World");
try {
String s = "t%2BX";
System.out.println(s);
String utfDecoded = java.net.URLDecoder.decode(s, "UTF-8");
System.out.println("Decoded UTF");
System.out.println(utfDecoded);
byte[] b = Base64.getDecoder().decode(utfDecoded);
System.out.println("Decoded bytes");
System.out.println(b);
} catch(Exception e) {
System.out.println("exception");
}
}
}
When run the original string and decoded string is output as:
t%2BX
t+X
Now here's my Obj-C code:
NSString* utf8EncodedString = #"t%2BX";
const char *cString = [utf8EncodedString cStringUsingEncoding:NSISOLatin1StringEncoding];
NSString *decodedUTF8 = [NSString stringWithCString: cString encoding:NSUTF8StringEncoding];
NSLog(utf8EncodedString);
NSLog(decodedUTF8);
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:decodedUTF8 options: 0];
When this is run, decodedData is nil which means initWithBase64EncodedString doesn't think the input is valid base54 encoded and the output in the console is:
t2BX
t2X
How can I get the UTF-8 data decoded properly so that I can feed it to initWithBase64EncodedString?
(This isn't the full actual string that's being decoded, but I've narrowed the problem down to this small portion of it that is resulting in the call to initWithBase64EncodedString retiring nil).
(Its not a padding issue NSData won't accept valid base64 encoded string)
Your string is in UTF8 and has URL encoding. You need to use NSString's stringByRemovingPercentEncoding method, just as in Java you are using URLDecoder. There is no need to use an intermediate C string. HTH
Taking CRD's answer one step further, here's the code
NSString* utf8EncodedString = #"t%2BX";
NSString* decodedUTF8 = [utf8EncodedString stringByRemovingPercentEncoding];
NSLog(#"%#", utf8EncodedString);
NSLog(#"%#", decodedUTF8);
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:decodedUTF8 options: 0];
Will output
t%2BX
t+X
Right now I'm investigating possibility to implement video streaming through MultipeerConnectivity framework. For that purpose I'm using NSInputStream and NSOutputStream.
The problem is: I can't receive any picture so far. Right now I'm trying to pass simple picture and show it on the receiver. Here's a little snippet of my code:
Sending picture via NSOutputStream:
- (void)sendMessageToStream
{
NSData *imgData = UIImagePNGRepresentation(_testImage);
int img_length = (int)[imgData length];
NSMutableData *msgData = [[NSMutableData alloc] initWithBytes:&img_length length:sizeof(img_length)];
[msgData appendData:imgData];
int msg_length = (int)[msgData length];
uint8_t *readBytes = (uint8_t *)[msgData bytes];
uint8_t buf[msg_length];
(void)memcpy(buf, readBytes, msg_length);
int stream_len = [_stream writeData:(uint8_t*)buf maxLength:msg_length];
//int stream_len = [_stream writeData:(uint8_t *)buf maxLength:data_length];
//NSLog(#"stream_len = %d", stream_len);
_tmpCounter++;
dispatch_async(dispatch_get_main_queue(), ^{
_lblOperationsCounter.text = [NSString stringWithFormat:#"Sent: %ld", (long)_tmpCounter];
});
}
The code above works totally fine. stream_len parameter after writing equals to 29627 bytes which is expected value, because image's size is around 25-26 kb.
Receiving picture via NSinputStream:
- (void)readDataFromStream
{
UInt32 length;
if (_currentFrameSize == 0) {
uint8_t frameSize[4];
length = [_stream readData:frameSize maxLength:sizeof(int)];
unsigned int b = frameSize[3];
b <<= 8;
b |= frameSize[2];
b <<= 8;
b |= frameSize[1];
b <<= 8;
b |= frameSize[0];
_currentFrameSize = b;
}
uint8_t bytes[1024];
length = [_stream readData:bytes maxLength:1024];
[_frameData appendBytes:bytes length:length];
if ([_frameData length] >= _currentFrameSize) {
UIImage *img = [UIImage imageWithData:_frameData];
NSLog(#"SETUP IMAGE!");
_imgView.image = img;
_currentFrameSize = 0;
[_frameData setLength:0];
}
_tmpCounter++;
dispatch_async(dispatch_get_main_queue(), ^{
_lblOperationsCounter.text = [NSString stringWithFormat:#"Received: %ld", (long)_tmpCounter];
});
}
As you can see I'm trying to receive picture in several steps, and here's why. When I'm trying to read data from stream, it's always reading maximum 1095 bytes no matter what number I put in maxLength: parameter. But when I send the picture in the first snippet of code, it's sending absolutely ok (29627 bytes . Btw, image's size is around 29 kb.
That's the place where my question come up - why is that? Why is sending 29 kb via NSOutputStream works totally fine when receiving is causing problems? And is there a solid way to make video streaming work through NSInputStream and NSOutputStream? I just didn't find much information about this technology, all I found were some simple things which I knew already.
Here's an app I wrote that shows you how:
https://app.box.com/s/94dcm9qjk8giuar08305qspdbe0pc784
Build the project with Xcode 9 and run the app on two iOS 11 devices.
To stream live video, touch the Camera icon on one of two devices.
If you don't have two devices, you can run one app in the Simulator; however, you can only use the camera on the real device (the Simulator will display the video broadcasted).
Just so you know: this is not the ideal way to stream real-time video between devices (it should probably be your last choice). Data packets (versus streaming) are way more efficient and faster.
Regardless, I'm really confused by your NSInputStream-related code. Here's something that makes a little more sense, I think:
case NSStreamEventHasBytesAvailable: {
// len is a global variable set to a non-zero value;
// mdata is a NSMutableData object that is reset when a new input
// stream is created.
// displayImage is a block that accepts the image data and a reference
// to the layer on which the image will be rendered
uint8_t * buf[len];
len = [aStream read:(uint8_t *)buf maxLength:len];
if (len > 0) {
[mdata appendBytes:(const void *)buf length:len];
} else {
displayImage(mdata, wLayer);
}
break;
}
The output stream code should look something like this:
// data is an NSData object that contains the image data from the video
// camera;
// len is a global variable set to a non-zero value
// byteIndex is a global variable set to zero each time a new output
// stream is created
if (data.length > 0 && len >= 0 && (byteIndex <= data.length)) {
len = (data.length - byteIndex) < DATA_LENGTH ? (data.length - byteIndex) : DATA_LENGTH;
uint8_t * bytes[len];
[data getBytes:&bytes range:NSMakeRange(byteIndex, len)];
byteIndex += [oStream write:(const uint8_t *)bytes maxLength:len];
}
There's a lot more to streaming video than setting up the NSStream classes correctly—a lot more. You'll notice in my app, I created a cache for the input and output streams. This solved a myriad of issues that you would likely encounter if you don't do the same.
I have never seen anyone successfully use NSStreams for video streaming...ever. It's highly complex, for one reason.
There are many different (and better) ways to stream video; I wouldn't go this route. I just took it on because no one else has been able to do it successfully.
I think that the problem is in your assumption that all data will be available in NSInputStream all the time while you are reading it. NSInputStream made from NSURL object has an asynchronous nature and it should be accessed accordingly using NSStreamDelegate. You can look at example in the README of POSInputStreamLibrary.
I'm successfully encrypting/decrypting data in iOS using RNCryptor.
I'm trying to get the public key to send to a server, so it can encrypt some data.
NSString *saltString = #"salt'n'peppa";
NSData *salt = [saltString dataUsingEncoding:NSUTF8StringEncoding];
NSData *key = [RNCryptor keyForPassword:password
salt:salt
settings:kRNCryptorAES256Settings.keySettings];
At this point, key has some data in it. However, I can't seem to work out how to get the public key as a string:
NSString *publicKey = [[NSString alloc] initWithData:key encoding:NSUTF8StringEncoding];
I've tried different encodings but nothing seems to work.
Here is the keyForPassword method from RNCryptor:
+ (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt settings:(RNCryptorKeyDerivationSettings)keySettings
{
NSMutableData *derivedKey = [NSMutableData dataWithLength:keySettings.keySize];
// See Issue #77. V2 incorrectly calculated key for multi-byte characters.
NSData *passwordData;
if (keySettings.hasV2Password) {
passwordData = [NSData dataWithBytes:[password UTF8String] length:[password length]];
}
else {
passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
}
// Use the built-in PBKDF2 if it's available. Otherwise, we have our own. Hello crazy function pointer.
int result;
int (*PBKDF)(CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen,
const uint8_t *salt, size_t saltLen,
CCPseudoRandomAlgorithm prf, uint rounds,
uint8_t *derivedKey, size_t derivedKeyLen);
PBKDF = CCKeyDerivationPBKDF ?: RN_CCKeyDerivationPBKDF;
result = PBKDF(keySettings.PBKDFAlgorithm, // algorithm
passwordData.bytes, // password
passwordData.length, // passwordLength
salt.bytes, // salt
salt.length, // saltLen
keySettings.PRF, // PRF
keySettings.rounds, // rounds
derivedKey.mutableBytes, // derivedKey
derivedKey.length); // derivedKeyLen
// Do not log password here
NSAssert(result == kCCSuccess, #"Unable to create AES key for password: %d", result);
return derivedKey;
}
I get the feeling I'm doing something majorly wrong as googling comes up with very little.
The key isn't a string, it's data. Just a random (sort of) series of bytes. The only real way to convert it to a string to send to a server would be to encode the bytes. A common method would be to use a base 64 encoding. Then the server could covert the base 64 encoded string back into the raw bytes of the key.
I'm not sure how secure is my solution.
Rob Napier has an extraordinary Framework (RNCryptor) to encrypt and decrypt in iOS and other systems.
As far as I know, he is using AES-CBC, that in fact is the standard of CommonCryptor.h
However, my requirements, has forced me to use AES-CTR. Both are really similar, so in theory it had to be something easy. But was not.
There is a lack of information around CommonCryptor.h. It is one of the worst explained frameworks ever.
Working with CBC you just need to call CCCrypt(). However, to work with CTR you should call: CCCrytorCreate(), CCCryptorUpdate(), CCCryptorFinal() and CCCryptorRelease()
Trying to encrypt my data, I was receiving different data every time, of course decrypting it had incorrect results.
I had 2 big problems in my first approach: the length of the key and the number of bytes written to dataOut.
I sorted the problems with:
1.- A NSString key of 32 characters
NSString *key = #"1234567890ABCDEFGHIJKLMNOPQRSTUV";
2.- To cut dataOut with needed length
Finally this is my code to Encrypt and Decrypt:
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonKeyDerivation.h>
#import <Security/Security.h>
+ (NSMutableData*) encryptString: (NSString*) stringToEncrypt withKey: (NSString*) keyString
{
//Key to Data
NSData *key = [keyString dataUsingEncoding:NSUTF8StringEncoding];
//String to encrypt to Data
NSData *data = [stringToEncrypt dataUsingEncoding:NSUTF8StringEncoding];
// Init cryptor
CCCryptorRef cryptor = NULL;
// Alloc Data Out
NSMutableData *cipherData = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128];
//Empty IV: initialization vector
NSMutableData *iv = [NSMutableData dataWithLength:kCCBlockSizeAES128];
//Create Cryptor
CCCryptorStatus create = CCCryptorCreateWithMode(kCCEncrypt,
kCCModeCTR,
kCCAlgorithmAES,
ccPKCS7Padding,
iv.bytes, // can be NULL, because null is full of zeros
key.bytes,
key.length,
NULL,
0,
0,
kCCModeOptionCTR_BE,
&cryptor);
if (create == kCCSuccess)
{
//alloc number of bytes written to data Out
size_t outLength;
//Update Cryptor
CCCryptorStatus update = CCCryptorUpdate(cryptor,
data.bytes,
data.length,
cipherData.mutableBytes,
cipherData.length,
&outLength);
if (update == kCCSuccess)
{
//Cut Data Out with nedded length
cipherData.length = outLength;
//Final Cryptor
CCCryptorStatus final = CCCryptorFinal(cryptor, //CCCryptorRef cryptorRef,
cipherData.mutableBytes, //void *dataOut,
cipherData.length, // size_t dataOutAvailable,
&outLength); // size_t *dataOutMoved)
if (final == kCCSuccess)
{
//Release Cryptor
//CCCryptorStatus release =
CCCryptorRelease(cryptor ); //CCCryptorRef cryptorRef
}
return cipherData;
}
}
else
{
//error
}
return nil;
}
+ (NSString*) decryptData: (NSData*) data withKey: (NSString*) keyString
{
//Key to Data
NSData *key = [keyString dataUsingEncoding:NSUTF8StringEncoding];
// Init cryptor
CCCryptorRef cryptor = NULL;
//Empty IV: initialization vector
NSMutableData *iv = [NSMutableData dataWithLength:kCCBlockSizeAES128];
// Create Cryptor
CCCryptorStatus createDecrypt = CCCryptorCreateWithMode(kCCDecrypt, // operation
kCCModeCTR, // mode CTR
kCCAlgorithmAES, // Algorithm
ccPKCS7Padding, // padding
iv.bytes, // can be NULL, because null is full of zeros
key.bytes, // key
key.length, // keylength
NULL, //const void *tweak
0, //size_t tweakLength,
0, //int numRounds,
kCCModeOptionCTR_BE, //CCModeOptions options,
&cryptor); //CCCryptorRef *cryptorRef
if (createDecrypt == kCCSuccess)
{
// Alloc Data Out
NSMutableData *cipherDataDecrypt = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128];
//alloc number of bytes written to data Out
size_t outLengthDecrypt;
//Update Cryptor
CCCryptorStatus updateDecrypt = CCCryptorUpdate(cryptor,
data.bytes, //const void *dataIn,
data.length, //size_t dataInLength,
cipherDataDecrypt.mutableBytes, //void *dataOut,
cipherDataDecrypt.length, // size_t dataOutAvailable,
&outLengthDecrypt); // size_t *dataOutMoved)
if (updateDecrypt == kCCSuccess)
{
//Cut Data Out with nedded length
cipherDataDecrypt.length = outLengthDecrypt;
// Data to String
NSString* cipherFinalDecrypt = [[NSString alloc] initWithData:cipherDataDecrypt encoding:NSUTF8StringEncoding];
//Final Cryptor
CCCryptorStatus final = CCCryptorFinal(cryptor, //CCCryptorRef cryptorRef,
cipherDataDecrypt.mutableBytes, //void *dataOut,
cipherDataDecrypt.length, // size_t dataOutAvailable,
&outLengthDecrypt); // size_t *dataOutMoved)
if (final == kCCSuccess)
{
//Release Cryptor
//CCCryptorStatus release =
CCCryptorRelease(cryptor); //CCCryptorRef cryptorRef
}
return cipherFinalDecrypt;
}
}
else
{
//error
}
return nil;
}
To call it:
NSString *key = #"1234567890ABCDEFGHIJKLMNOPQRSTUV";
NSString *stringToEncrypt = #"Gabriel.Massana";
NSData* encrypted = [GM_AES128_CTR encryptString:stringToEncrypt withKey:key];
NSString *decrypted = [GM_AES128_CTR decryptData:encrypted withKey:key];
I'm posting my solution because there are not to much questions for AES CTR in Stackoverflow. Likewise, if someone want to check it and tell me if something is wrong will be very appreciated.
My example in GitHub
How secure is this solution? It is easy to crack the system? What are my possibilities to add more security to AES-CTR?
I'm listing this as a separate answer, but I'm just amplifying what Zaph has already said:
This is totally broken encryption.
It's not surprising that this has happened to you. It is a very common problem when you try to build your own scheme. There are just a lot of places you can mess up. But I don't want to understate just how insecure this scheme is. It is really, really broken.
CTR cannot ever repeat the same nonce+key, and you reuse the nonce every time. This is very different from CBC. In CBC if you reuse the IV, then you make it somewhat easier on the attacker to break your encryption. In CTR, if you reuse the nonce+key it is pretty easy to decrypt the message once you have a few ciphertexts. Some good discussion can be found in RFC3686.
When used correctly, AES-CTR provides a high level of
confidentiality. Unfortunately, AES-CTR is easy to use incorrectly.
Being a stream cipher, any reuse of the per-packet value, called the
IV, with the same nonce and key is catastrophic. An IV collision
immediately leaks information about the plaintext in both packets.
For this reason, it is inappropriate to use this mode of operation
with static keys. Extraordinary measures would be needed to prevent
reuse of an IV value with the static key across power cycles. To be
safe, implementations MUST use fresh keys with AES-CTR. The Internet
Key Exchange (IKE) [IKE] protocol can be used to establish fresh
keys. IKE can also provide the nonce value.
Note that RNCryptor originally used CTR. I moved back to CBC on the recommendation of Apple after talking with them about how hard easy it is to screw up CTR. If you can avoid CTR, you absolutely should. It is extremely useful for certain problems, but for general file encryption it is seldom appropriate.
That said, I understand you have a problem in the chip. How is your chip going to get its key? It seems strange to use symmetric encryption with a chip this way. In any case, RNCryptor v1 may meet your needs. You'd likely need to use encryptFromStream:toStream:encryptionKey:HMACKey:error: since I assume the chip can't handle PBKDF2.
Trying to encrypt my data, I was receiving different data every time, of course decrypting it had incorrect results.
Any good encryption system will have this property. That's why you need to send your nonce/IV (and if you use passwords, salts) along with the ciphertext.
NSString *key = #"1234567890ABCDEFGHIJKLMNOPQRSTUV";
This is not a key. This is a password and dramatically reduces your available keyspace. Keys are typically going to be NSData since they need to be chosen over all possible values, not just ASCII.
I am trying to a read JSON file containing contact info objects consisting of NSString types and NSMutableArrays. Currently, I am using NSData to read the whole file and then parsing through it. I have utilised Stig's example as mentioned here: SBJson4Parser Example
SBJson4ValueBlock block = ^(id obj, BOOL *stop) {
NSLog(#"Found: %#", #([obj isKindOfClass:[NSDictionary class]]));
//contactsData *contact = obj;
NSDictionary *contact = obj;
NSLog(#"Contact: %#",contact);
/* NSString *fName, *lName;
fName = [contact objectForKey:#"mFirstName"];
lName = [contact objectForKey:#"mLastName"];
NSLog(#"First Name: %#",fName);
NSLog(#"Last Name: %#",lName);
*/
};
SBJson4ErrorBlock eh = ^(NSError* err){
NSLog(#"Oops: %#",error);
};
NSLog(#"Parse work");
id parser = [SBJson4Parser multiRootParserWithBlock:block
errorHandler:eh];
//uint8_t buf[1024];
//unsigned int len = 0;
NSLog(#"Trying to push stream to data");
//[inputStream read:buf maxLength:len];
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSUTF8StringEncoding error:NULL];
//id data = [json da:NSUTF8StringEncoding];
SBJson4ParserStatus status = [parser parse:data];
NSLog(#"Status: %u",status);
These days people seem to have hundreds or even thousands of contacts, thanks to social networks. Will this lead to a larger memory footprint on an iOS device ? If so, how do I parse a single object from a stream ? If I have to use a delegate, an example would be greatly appreciated.
Please note that I am new to the world of iOS development as well as Objective-C.
The structure of the json file in question:
{
"mAddresses": [
],
"mContactPhoto": "",
"mDisplayName": ",tarun,,,,israni,,",
"mPhoneNumberList": [
{
"mLabel": "_$!<Home>!$_",
"mNumber": "(988) 034-5678",
"mType": 1
}
]
}{
"mAddresses": [
],
"mContactPhoto": "",
"mDisplayName": ",Sumit,,,,Kumar,,",
"mPhoneNumberList": [
{
"mLabel": "_$!<Home>!$_",
"mNumber": "(789) 034-5123",
"mType": 1
}
]
}
Your solution looks like it should work to me. If your file so big that you don't want to hold it all in memory, i.e. you want to avoid this line:
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSUTF8StringEncoding error:NULL];
then you can use an NSInputStream (untested, but hopefully you get the gist):
id parser = [SBJson4Parser multiRootParserWithBlock:block
errorHandler:eh];
id is = [NSInputStream inputStreamWithFileAtPath:filePath];
[is open];
// buffer to read from the input stream
uint8_t buf[1024];
// read from input stream until empty, or an error;
// better error handling is left as an exercise for the reader
while (0 > [is read:buffer maxLength: sizeof buffer]) {
SBJson4ParserStatus status = [parser parse:data];
NSLog(#"Status: %u",status);
// handle parser errors here
}
[is close];
However, you still have to read and parse the whole file to guarantee that you find a particular contact. There is no way to read just a specific contact this way. If that is something you do often, you may want to store your contacts a different way that supports that scenario better. One way would be to use e.g. SQLLite.