I'm using RNCryptor to crypt files and for large files I use NSInputStream to separate file into chunks and to encode / decode those chunks. It worked great until I needed to base64 encode / decode chunks which are passed to streams.
Following code works perfectly fine for encoding, except it doesn't do base64 encoding:
NSInputStream *tmpStream = [[NSInputStream alloc] initWithURL:fileURL];
NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];
[tmpStream open];
[outputStream open];
__block NSMutableData *data = [NSMutableData dataWithLength:kBlockSize];
__block RNEncryptor *encryptor = nil;
dispatch_block_t readStreamBlock = ^{
[data setLength:kBlockSize];
NSInteger bytesRead = [tmpStream read:[data mutableBytes] maxLength:kBlockSize];
if (bytesRead < 0) {
NSLog(#"An error occurred while decrypting file stream");
if (resultBlock) { resultBlock(nil); }
return;
}
else if (bytesRead == 0){
[encryptor finish];
}
else {
[data setLength:bytesRead];
[encryptor addData:data];
}
};
encryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
password:HUCryptorPassword
handler:^(RNCryptor *cryptor, NSData *data) {
[outputStream write:data.bytes maxLength:data.length];
if(cryptor.isFinished){
[outputStream close];
if (resultBlock) {
resultBlock([NSString stringWithFormat:#"%#", filePath]);
}
}
else {
readStreamBlock();
}
}];
readStreamBlock();
I tried adding base64 encoding after the data is encrypted and before writing to output stream:
NSData *base64Data = [data base64EncodedDataWithOptions:kNilOptions]; //Tried with all options
[outputStream write:base64Data.bytes maxLength:base64Data.length];
But that gives me incorrect results in output file.
Only correct way I get correctly encoded / decoded data is to load whole file in memory and call encode / decode methods.
NSData *resultData = [NSData dataWithContentsOfFile:filePath]; //This is location of output file stream
NSData *encodedData = [resultData base64EncodedDataWithOptions:kNilOptions]; //Gives me correct base64 encoded data
I want to avoid that since I need to make encoding / decoding big files, such as videos, sounds and images.
Is there any safe way to accomplish this?
4 base64 characters fit in three bytes.
When encoding make sure you encode chunks of 4 characters, holding over any remainder for the next portion of the stream. As the end of the stream encode what is left. Base64 pads the end of data that is not mod4, that is the "=" characters at the end.
For decoding decode in chunks that are mod3 encoded characters in length.
If you set it up so that you encode in 3 block (or multiples or 3) then the base64 encoding will also fall on a boundary.
Related
I have an encryption function that gets data and key, with inner iv and returns an encrypted string. I can encrypt every string that contains just English characters but not about Arabic. This is my function. Please help me to find the problem. Thanks
-(NSString*)Encrypt:(NSString*)data second:(NSString*)key
{
size_t outLength;
NSMutableData * cipherData = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128];
Byte byte[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,}; //It`s not valid. The main iv is secret
NSData *datakey = [NSData dataWithBytes:key.UTF8String length:key.length];
NSData *datadata = [NSData dataWithBytes:data.UTF8String length:data.length];
CCCryptorStatus result = CCCrypt( kCCEncrypt
, kCCAlgorithmAES128
, kCCOptionPKCS7Padding
, datakey.bytes
, [datakey length]
, byte
, datadata.bytes
, [datadata length]
, cipherData.mutableBytes
, cipherData.length
, &outLength);
if (result == kCCSuccess) {
cipherData.length = outLength;
}
else {
}
NSData *encryptedData=cipherData;
NSString *str=[encryptedData base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength];
return str;
}
The problem is that cipherData is to short. It need to be a block longer than datadata which may (such as in this case) be longer than data.length.
When creating data with utf-8 encoding from a string that contains multiple-byte data such as Arabic, emoji and etc the data will be longer than the string characters.
Incorrect code:
NSMutableData * cipherData = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128];
NSData *datadata = [NSData dataWithBytes:data.UTF8String length:data.length];
Correct code:
NSData *datadata = [data dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData * cipherData = [NSMutableData dataWithLength: datadata.length + kCCBlockSizeAES128];
Note that misnaming the input string data does not help, it is a string. renaming data -> tex and then ``datadata->data` makes the code more clear. Good naming solves many code problems.
In my app I'm using NSStreams for client server communication. In the delegate method in event hasbytesAvailable when I'm reading the data its returning null
Case: when the length is 4096 then read is fails and returns nil; Means when the length is equal to buffer size its failing to read, even if I put the maxlength to 4000 and buffer size to 4096, then also whenever 4000 bytes are read its failing. what to do?
Here is the code:
case NSStreamEventHasBytesAvailable:
if (aStream == inputStream) {
uint8_t buffer[4096];
int len;
while ([inputStream hasBytesAvailable]) {
len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
NSLog(#"\nThe length is -- %d\n",len);
if (len > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:len];
output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
}
}
}
NSLog(#"\n\n%#\n\n",output);
Data read from a network connection will not always be received in the same sized chunks it was sent in. This means the receiver needs to:
Know exactly how much data to expect in a message.
Remember any "left over" data from the current message as that belongs to the next message.
One of the easiest ways of doing this properly is to prefix the message with a byte-count and then only attempt to read that much data from the network connection. That leaves any remaining data in the "network buffer" until the client wants it.
Your code is receiving a string, which will be NUL-terminated, so that means you need to read the data in fixed-sized chunks, check every byte until you find the end-of-string and then tack together the chunks before converting it to a string. You then need to remember any "left over" data for the next message. Complicated stuff, eh?
I'd go with the message size prefix, as that is what pretty much everyone else does.
I think the code is absolutely fine and it should read the data, may be after you've read 4096 bytes there might be some more bytes available and loop continues, and you are initialising the output variable again, So you might be missing it.
Use the following snippet:
if (aStream == inputStream) {
uint8_t buffer[4096];
int len;
NSString* tempOutput = #"";
while ([inputStream hasBytesAvailable]) {
len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
NSLog(#"\nThe length is -- %d\n",len);
if (len > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:len];
output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
tempOutput = [tempOutput stringByAppendingString:output];
// output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
}
}
output = tempOutput;
}
I am getting chunks of NSData sequentially from server, more than approx. 4096 bytes at a time, sequentially. Each received chunk may differ in its size.
What I would like to do, is to append all these bytes somewhere, and at the same time start reading from the beginning of the data, sequentially, 512 bytes at a time maximum.
While searching I've learned about using NSInputStream for this, and here is the code snippet:
uint8_t bytes[512];
UInt32 length;
NSInputStream *stream = [[NSInputStream alloc] initWithData:aData];
[stream open];
while (((length = [stream read:bytes maxLength:512]) > 0)) {
if ([self.inputStreamer isKindOfClass:[PLAudioInputStreamerNoOpenClose class]]) {
[self.inputStreamer hasData:bytes length:length];
}
}
While this just works, the initialized NSInputStream does not seem to allow appending additional bytes after it is initialized, so the only way I could think of is, to initialize NSInputStreams for every chunk of data, and block until it has reached its end, going on to do the same for next chunk of bytes, as the code above does.
Is there any more preferred solution for this kind of task? Any help will be appreciated. Thank you,
You need a 'read and write' stream. NSInputStream is read only and NSOutputStream is write only.
If I were you, I just use a NSMutableData and one int variables for 'current reading position'.
NSMutableData* myData = [[NSMutableData alloc] init];
NSInteger myPos = 0;
[myData appendData:..];
...
// need to check the range (myPos ~ [myData length])
NSData* nextBlockToRead = [NSData dataWithBytesNoCopy:((char*)[myData bytes] + myPos) length:512];
myPos += 512;
Can anybody point me in the right direction to be able to encrypt a string, returning another string with the encrypted data? (I've been trying with AES256 encryption.) I want to write a method which takes two NSString instances, one being the message to encrypt and the other being a 'passcode' to encrypt it with - I suspect I'd have to generate the encryption key with the passcode, in a way that can be reversed if the passcode is supplied with the encrypted data. The method should then return an NSString created from the encrypted data.
I've tried the technique detailed in the first comment on this post, but I've had no luck so far. Apple's CryptoExercise certainly has something, but I can't make sense of it... I've seen lots of references to CCCrypt, but it's failed in every case I've used it.
I would also have to be able to decrypt an encrypted string, but I hope that's as simple as kCCEncrypt/kCCDecrypt.
Since you haven't posted any code, it's difficult to know exactly which problems you're encountering. However, the blog post you link to does seem to work pretty decently... aside from the extra comma in each call to CCCrypt() which caused compile errors.
A later comment on that post includes this adapted code, which works for me, and seems a bit more straightforward. If you include their code for the NSData category, you can write something like this: (Note: The printf() calls are only for demonstrating the state of the data at various points — in a real application, it wouldn't make sense to print such values.)
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *key = #"my password";
NSString *secret = #"text to encrypt";
NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding];
NSData *cipher = [plain AES256EncryptWithKey:key];
printf("%s\n", [[cipher description] UTF8String]);
plain = [cipher AES256DecryptWithKey:key];
printf("%s\n", [[plain description] UTF8String]);
printf("%s\n", [[[NSString alloc] initWithData:plain encoding:NSUTF8StringEncoding] UTF8String]);
[pool drain];
return 0;
}
Given this code, and the fact that encrypted data will not always translate nicely into an NSString, it may be more convenient to write two methods that wrap the functionality you need, in forward and reverse...
- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}
- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
encoding:NSUTF8StringEncoding] autorelease];
}
This definitely works on Snow Leopard, and #Boz reports that CommonCrypto is part of the Core OS on the iPhone. Both 10.4 and 10.5 have /usr/include/CommonCrypto, although 10.5 has a man page for CCCryptor.3cc and 10.4 doesn't, so YMMV.
EDIT: See this follow-up question on using Base64 encoding for representing encrypted data bytes as a string (if desired) using safe, lossless conversions.
I have put together a collection of categories for NSData and NSString which uses solutions found on Jeff LaMarche's blog and some hints by Quinn Taylor here on Stack Overflow.
It uses categories to extend NSData to provide AES256 encryption and also offers an extension of NSString to BASE64-encode encrypted data safely to strings.
Here's an example to show the usage for encrypting strings:
NSString *plainString = #"This string will be encrypted";
NSString *key = #"YourEncryptionKey"; // should be provided by a user
NSLog( #"Original String: %#", plainString );
NSString *encryptedString = [plainString AES256EncryptWithKey:key];
NSLog( #"Encrypted String: %#", encryptedString );
NSLog( #"Decrypted String: %#", [encryptedString AES256DecryptWithKey:key] );
Get the full source code here:
https://gist.github.com/838614
Thanks for all the helpful hints!
-- Michael
#owlstead, regarding your request for "a cryptographically secure variant of one of the given answers," please see RNCryptor. It was designed to do exactly what you're requesting (and was built in response to the problems with the code listed here).
RNCryptor uses PBKDF2 with salt, provides a random IV, and attaches HMAC (also generated from PBKDF2 with its own salt. It support synchronous and asynchronous operation.
I waited a bit on #QuinnTaylor to update his answer, but since he didn't, here's the answer a bit more clearly and in a way that it will load on XCode7 (and perhaps greater). I used this in a Cocoa application, but it likely will work okay with an iOS application as well. Has no ARC errors.
Paste before any #implementation section in your AppDelegate.m or AppDelegate.mm file.
#import <CommonCrypto/CommonCryptor.h>
#implementation NSData (AES256)
- (NSData *)AES256EncryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
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 numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
- (NSData *)AES256DecryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
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 cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free(buffer); //free the buffer;
return nil;
}
#end
Paste these two functions in the #implementation class you desire. In my case, I chose #implementation AppDelegate in my AppDelegate.mm or AppDelegate.m file.
- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
return [data base64EncodedStringWithOptions:kNilOptions];
}
- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions];
return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding];
}
Please use the below mentioned URL to encrypt string using AES excryption with
key and IV values.
https://github.com/muneebahmad/AESiOSObjC
Using the NSStreamEventHasSpace available event, I am trying to write a simple NSString to to an NSOutputStream. Here is the contents of that event:
uint8_t *readBytes = (uint8_t *)[data mutableBytes];
readBytes += byteIndex; // instance variable to move pointer
int data_len = [data length];
unsigned int len = ((data_len - byteIndex >= 12) ?
12 : (data_len-byteIndex));
uint8_t buf[len];
(void)memcpy(buf, readBytes, len);
len = [output write:(const uint8_t *)buf maxLength:len];
NSLog(#"wrote: %s", buf);
byteIndex += len;
I pretty much took it right from Apple. The data is initialized in my viewDidLoad method with
data = [NSMutableData dataWithData:[#"test message" dataUsingEncoding:NSUTF8StringEncoding]];
[data retain];
The HasSpaceAvailable event is called twice. In the first one, the entire message is written with the characters "N." appended to it. In the second time, NSLog reports that a blank message was written (not null). Then, the EndEncountered event occurs. In that event, I have
NSLog(#"event: end encountered");
assert([stream isEqual:output]);
NSData *newData = [output propertyForKey: NSStreamDataWrittenToMemoryStreamKey];
if (!newData) {
NSLog(#"No data written to memory!");
} else {
NSLog(#"finished writing: %#", newData);
}
[stream close];
[stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[stream release];
output = nil;
break;
I also got this from Apple. However, "No data written to memory!" is logged. No errors occur at anytime, and no data appears to have been received on the other end.
I seem to have fixed this by using low level Core Foundation methods instead of higher level NSStream methods. I used this article as a starting point:
http://oreilly.com/iphone/excerpts/iphone-sdk/network-programming.html
It covers input and output streams in great lenghts and has code examples.
Hope this helps.