Update : found the solution. I will update this question soon with the actual working code and command.
A client is encrypting a file server-side with C++, and I need to decrypt it in an iPhone application.
My client can crypt and decrypt on his side, and so do I on the iPhone, but we can't decrypt the file encrypted by each other.
I saw many related questions on SO, but none could help me find an implementation that works the same way on both side.
I want to output some sample values that we will accept as the common implementation.
I tried to crypt a file with openssl and decrypt it with cocoa, but couldn't.
Here is what I use for encryption:
echo "123456789ABCDEFG" | openssl enc -aes-128-ecb -nosalt -K "41414141414141414141414141414141" -iv 0 > hello.txt.bin
Adding the option -p to openssl call shows that the expected key and iv are used:
key=41414141414141414141414141414141
iv =00000000000000000000000000000000
And for cocoa decryption (in an NSData category):
- (NSData *)AESDecryptWithKey:(NSString *)key {
char keyPtr[kCCKeySizeAES128+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;
char iv[32];
for (int i = 0; i < 32; i++) {
iv[i] = 0;
}
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
iv, //"00000000000000000000000000000000" /* 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;
}
called this way:
- (void)testBinaryFileDecryption {
NSString *databasePath = [[NSBundle mainBundle] pathForResource:#"hello" ofType:#"txt.bin"];
NSData *data = [NSData dataWithContentsOfFile:databasePath];
NSAssert(nil != data, #"Encrypted data, freshly loaded from file should not be nil");
NSData *plain = [data AESDecryptWithKey:#"AAAAAAAAAAAAAAAA"];
NSAssert(nil != plain, #"Decrypted plain data should not be nil");
NSLog(#"Result: '%#'", [[NSString alloc] initWithData:plain encoding:NSASCIIStringEncoding]);
}
Result logs:
Result: '4¨µ¢Ä½Pk£N
What option am I forgetting? Is the encoding of the NSData returned something else than NSASCIIStringEncoding ?
I know nothing of iPhone development, but looking at this code, it appears you're trying to use the ascii-of-hex-encoding of the actual key to decrypt the packet. OpenSSL's enc requires the hex encoding because it converts the hex into bytes. Your actual key looks more like this, when converted directly to ascii.
["\037", " ", "!", "\"", "#", "$", "%", "&", "'", "\036", "\037", " ", "!", "\"", "#", "$"]
(All that might be obtuse. If you were to encode the string you're using for decrypting into the same format that OpenSSL enc accepts, the key would be 3331333233333334333533363337333833393330333133323333333433353336.)
Try using a key specification of 41414141414141414141414141414141 to OpenSSL and use AAAAAAAAAAAAAAAA in your iPhone code.
Also, I strongly suggest your initial tests be made with data that is exactly N*16 bytes long. OpenSSL enc uses PKCS#5 padding (unless you use -nopad), and your iPhone code is using PKCS#7 padding. On a cursory glance at RFCs, they seem to be the same padding mechanism, but I could be wrong.
And I know you're just trying things out here, but in real production code, please do not use ECB mode.
I'm using Crypt::OpenSSL::AES to encrypt files that are decrypted in my iOS app, which decrypts using CommonCryptor.
cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0,
keyPtr, kCCKeySizeAES256,
IVECTOR /* initialization vector (optional) -- was NULL*/,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
To initialize the IVECTOR I'm using bzero.
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
To encrypt under OpenSSL in perl I do this:
my $cipher = Crypt::CBC->new( -key => $key,
-literal_key => 1,
-header => 'none',
-iv => '0000000000000000',
-cipher => 'Crypt::OpenSSL::AES' );
OpenSSL seems to accept the '0000000000000000' IV as the same thing as ASCII 0 (null) characters. Seems plausible in retrospect, but it required a lot of hair pulling because every crypto failure looks like every other crypto failure: garbage out.
Related
I have been looking and looking at this for hours. I am desperately trying to get iOS to encrypt a short piece of text using AES-256 encryption that can then be decrypted by openssl.
Straight forward? Nope.
The code I've found for iOS is not compatible with the keys and IVs for openssl, so I've had to adapt it, but it's plainly not working.
So here is the code to encrypt I am using... passing in a string to encrypt (dataString) a string key (key) and a string initialisation vector (iv)...
- (NSData *)AES256Encrypt:(NSString *)dataString WithKey:(NSString *)key iv:(NSString *)iv {
// '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];
//NSLog(#"keyPtr: '%s'", keyPtr);
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"keyPtr: '%s'", keyData.bytes);
NSData *dataToEncrypt = [dataString dataUsingEncoding:NSUTF8StringEncoding];
NSData *ivData = [iv dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [dataToEncrypt 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,
keyData.bytes, kCCKeySizeAES256,
ivData.bytes, // initialisation vector
dataToEncrypt.bytes,
dataToEncrypt.length, /* 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;
}
For the same string to encode, this does not produce the same value as when using openssl with the same key and iv... e.g. this command line:
openssl enc -aes-256-cbc -e -in secrets.txt -a -iv 0000 -K 0000 -p
secrets.txt is just a text file containing the string to be encrypted
This outputs something like this:
salt=3C66000000000000
key=0000000000000000000000000000000000000000000000000000000000000000
iv =00000000000000000000000000000000
qTMfgtAxbF8Yyh27ZDrcIQ==
And to decrypt, do the opposite operation (assuming the encrypted last line of data above is in test.secrets.out)
openssl enc -aes-256-cbc -d -in test.secrets.out -a -iv 0000 -K 0000 -p
salt=3C66000000000000
key=0000000000000000000000000000000000000000000000000000000000000000
iv =00000000000000000000000000000000
< text of the secrets.txt file >
Now, if I use the key and iv of 4 chars, this doesn't encode correctly in iOS. If I use the full length key and iv, this doesn't encode correctly either.
Basically, this is a check to see that if I send a piece of encrypted data it is the right piece of data.
What am I missing?
Some code I've looked through to try to find an answer...
http://robnapier.net/blog/aes-commoncrypto-564
https://github.com/rnapier/RNCryptor
http://pastie.org/426530
Have searched extensively on here too and cannot find an answer.
Any help appreciated.
OpenSSL has a unique (read "not close to any standard, and also not particularly secure") method for converting passwords into IV and key. If you look at RNOpenSSLCryptor, you'll see the algorithm used:
// For aes-128:
//
// key = MD5(password + salt)
// IV = MD5(Key + password + salt)
//
// For aes-256:
//
// Hash0 = ''
// Hash1 = MD5(Hash0 + Password + Salt)
// Hash2 = MD5(Hash1 + Password + Salt)
// Hash3 = MD5(Hash2 + Password + Salt)
// Hash4 = MD5(Hash3 + Password + Salt)
//
// Key = Hash1 + Hash2
// IV = Hash3 + Hash4
//
// File Format:
//
// |Salted___|<salt>|<ciphertext>|
//
Using RNOpenSSLCryptor allows RNCryptor support for the OpenSSL format. I am currently in the midst of reworking this code on the async branch to support async operations, and that branch doesn't yet support OpenSSL, but I do plan to rework that shortly (by mid-July 2012).
If you want code that implements this for your own use, look at keyForPassword:salt: and IVForKey:password:salt:.
Note that the the OpenSSL file format has several security problems and I don't recommend it if you can avoid it. It does not use a very good KDF to generate its key, does not have as random an IV as it should, and provides no HMAC for authentication. These security problems are why I designed a different file format, much as I hated creating "yet another incompatible container."
I'm just coding basic "encrypt" and "decrypt" methods for AES on iPhone, using CCrypt.
I've been running a few tests and I was really struck about finding that, sometimes, if you try to decrypt an encrypted text using a key different than the one that was used to encrypt the plain text CCrypt would not return any errors.
Here is an example:
- (void) testDecryptTextWithTheWrongKey {
NSData *encryptKey = [Base64 decodeBase64WithString:#"+LtNYThpgIlQs2CaL00R6AuG2C/i6U1Vt1+6wfFeFMk="];
NSData *decryptKey = [Base64 decodeBase64WithString:#"yg7BvhM8npVGpAFpAESDn3IRWpe6qeQWaa1rwHiTsyU="];
NSString *plainText = #"The text to be encrypted";
NSData *plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSData *encrypted = [LocalCrypto encryptText:plainTextData key:encryptKey error:&error];
assertThat(error, nilValue());
assertThat(encrypted, notNilValue());
error = nil;
NSData *decrypted = [LocalCrypto decryptText:encrypted key:decryptKey error:&error];
assertThat(error, notNilValue());
assertThat(decrypted, nilValue());
}
My encrypt and decrypt methods defined in LocalCrypto simply call an internal "executeCryptoOperation" method indicating that they want to encrypt or decrypt:
+ (NSData *) executeCryptoOperation:(CCOperation)op key:(NSData *) key input:(NSData *) input error:(NSError **)error {
size_t outLength;
NSMutableData *output = [NSMutableData dataWithLength:input.length + kCCBlockSizeAES128];
CCCryptorStatus result = CCCrypt(op, // operation
kCCAlgorithmAES128, // Algorithm
kCCOptionPKCS7Padding | kCCOptionECBMode, // options
key.bytes, // key
key.length, // keylength
nil, // iv
input.bytes, // dataIn
input.length, // dataInLength,
output.mutableBytes, // dataOut
output.length, // dataOutAvailable
&outLength); // dataOutMoved
if (result == kCCSuccess) {
output.length = outLength;
} else {
*error = [NSError errorWithDomain:kCryptoErrorDomain code:result userInfo:nil];
return nil;
}
return output;
}
Well, my question is: is it normal that CCrypt returns kCCSuccess when we try to decrypt the encrypted text with a different key than the one used during the encrpytion? Am I missing something or doing something wrong?
It is true that even when CCrypt returns success for the decryption, I can't get a proper NSString out of the resulting data but I would certainly expect CCrypt to return some sort of error in this situation (as Java would probably do).
If this is the normal behavior, how am I supposed to know if the decrypt operation returned the real plain text or just a bunch of bytes that don't make any sense?
There is a similar question here, but the answer doesn't really convince me: Returning wrong decryption text when using invalid key
Thanks!
There are cipher algorithms which include padding (like the PKCS#5 padding in your Java implementation), and there are ones which don't.
If your encryption algorithm used padding, the corresponding decryption algorithm expects that there will be well-formed padding in the decrypted plaintext, too. This serves as a cheap partial integrity check, since with a wrong key the output likely will not have a right padding. (The chance that a random n-byte block (n=16 for AES) has a valid PKCS#5 padding is 1/256 + 1/(256^2) + ... + 1/(256^n), which is only slightly more than 1/256.)
It might be that your objective-C CCCrypt function does not check that the padding is valid, only its last byte (or even only some bits of this last byte), to see how many bytes were padded (and are now to be cut off).
If you want to make sure that the key is right, encrypt some known part of the plaintext, and error out if it is not in the decrypted part. (But don't do this with ECB mode, see below.)
If you also want to make sure that the data was not modified, also use a MAC, or use a combined authenticated encryption mode of operation for your block cipher.
Another note: You should not use ECB mode, but instead a secure mode of operation (about any other mode indicated in this article and supported by your implementation will do - standard nowadays is CBC or CTR mode). Some modes (like CFB, OFB and CTR) don't need padding at all.
You're missing the fact that the decryption function has no idea whatsoever what the plaintext (decrypted data) is supposed to look like.
As far as the decryption function is concerned, it got a key and a ciphertext from you, applied the decryption routine to the ciphertext using the key you provided, and no errors arose. Hence, success.
It's your job to verify that the plaintext you got was actually correct/on the format you expected it to be.
I want to write encrypt function in iPhone application that would be decrypt on server in php,
Decrypt function in php is this
function decrypt($input_text)
{
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$enc_key = "0e9d083f3514a69243bb8f1395d332c1";
$out = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $enc_key, $input_text, MCRYPT_MODE_ECB, $iv);
return $out;
}
I'm using following encryption code in iPhone
+(NSString*) encrypt:(NSString*) str key:(NSString*) key
{
NSString *key =key;
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];
char *dataIn = [str UTF8String];
char dataOut[500];// set it acc ur data
bzero(dataOut, sizeof(dataOut));
size_t numBytesEncrypted = 0;
CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,kCCOptionPKCS7Padding|kCCOptionECBMode, keyPtr,kCCKeySizeAES128, NULL, dataIn, strlen(dataIn), dataOut, sizeof(dataOut), &numBytesEncrypted);
NSString* strResult = [[[NSString alloc] initWithCString:dataOut] autorelease];
return [strResult copy];
}
but didn't get the original result.
Please anybody can correct me where i'm wrong
Try:
As said, use the same key length in both cases.
In the CCCrypt call you don't store the iv that was generated, instead you pass NULL. You should store this and somehow transport it along the message to your PHP code. There, instead of generating a new, random iv you would reuse the existing one generated by the iPhone.
On top of that, mcrypt does not support PKCS7Padding (also often referred to as PKCS5Padding), so unfortunately you need to implement it yourself. In your case, you first decrypt the iPhone's input and then apply the pkcs5_unpad function described in that article afterwards.
You are using AES with 256-bit key in the decryption and with 128-bit key in the encryption. These are entirely different algorithms.
My app "streams" content (fixed sized files, hence quotation marks) from an HTTP server into a local file. Then there is another component of the app that opens that same file and displays it (plays it).
This is done for caching purposes, so that when the same file is requested next time, it will no longer need to be downloaded from the server.
App's spec requires that all local content is encrypted (even with the most light weight encryption)
Question: has there been done any work, allowing one to simply redirect the stream to a library which will then save the stream encrypted into a file? And then, when I request the stream from the local file, the library returns an on the fly decrypted stream?
I've been searching for a solution with no results so far
Thanks
I ended up writing a custom solution that uses RC4 encryption from the built in Crypt library. It was surprisingly straight forward. Basically it involved creating a function that encrypts/decrypts chunks of NSData and then read/write those chunks to files... Here's the function that does the encryption in case someone else is interested:
- (NSData*)RC4EncryptDecryptWithKey:(NSString *)key operation:(CCOperation)operation
{
// convert to C string..
int keySize = [key length];
char keyPtr[keySize];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr
maxLength:sizeof(keyPtr)
encoding:NSUTF8StringEncoding];
// encode/decode
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength;
void *buffer = malloc(bufferSize);
size_t numBytesOut = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmRC4,
kCCOptionECBMode,
keyPtr,
8,
NULL,
[self bytes],
dataLength,
buffer,
bufferSize,
&numBytesOut);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer
length:numBytesOut
freeWhenDone:YES];
}
free(buffer);
return nil;
}
- (NSData*)RC4EncryptWithKey:(NSString*)key {
return [self RC4EncryptDecryptWithKey:key operation:kCCEncrypt];
}
- (NSData*)RC4DecryptWithKey:(NSString*)key {
return [self RC4EncryptDecryptWithKey:key operation:kCCDecrypt];
}
Obviously one could create something more secure (eg AES) or whatever (in fact I used examples of other encryption wrappers to write this one)
I wouldn't worry about encryption just because Apple says so.
Make this work how you want it (without encryption, it sounds like) and submit it for approval. If approved, you're good. If not, worry about it then. If your design requires you to make a decision now, your design might be flawed.
I was to encrypt data on the device and send it by http to our web server then decrypt the data on out .net web app. Is this possible? If yes, which encryption method I should use? and if their are any articles out there?
Thanks
SSL should be the standard solution for HTTP encryption. NSURLConnection supports it out of the box (just load an https:// request), so you would just have to set up your server accordingly.
As you don't want to use SSL (and I agree there are many good reasons not to do so) you can use the built in CommonCrypto framework to encrypt just the data you need to. Here is a simple NSData category to encrypt arbitrary data:
#implementation NSData (AES256)
- (NSData*) encryptedWithKey: (NSString *) key;
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyBuffer[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero( keyBuffer, sizeof(keyBuffer) ); // fill with zeroes (for padding)
[key getCString: keyBuffer maxLength: sizeof(keyBuffer) encoding: NSUTF8StringEncoding];
// encrypts in-place, since this is a mutable data object
size_t numBytesEncrypted = 0;
size_t returnLength = ([self length] + kCCKeySizeAES256) & ~(kCCKeySizeAES256 - 1);
// NSMutableData* returnBuffer = [NSMutableData dataWithLength:returnLength];
char* returnBuffer = malloc(returnLength * sizeof(uint8_t) );
CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128 , kCCOptionPKCS7Padding | kCCOptionECBMode,
keyBuffer, kCCKeySizeAES128, nil,
[self bytes], [self length],
returnBuffer, returnLength,
&numBytesEncrypted);
if(result == kCCSuccess)
return [NSData dataWithBytes:returnBuffer length:numBytesEncrypted];
else
return nil;
}
#end
Note that this also turns on ECB Mode which you may not want. Also remember that the data that comes back from this call is not suitable for use in URLs you will have to base 64 encode it.
If SSL is not an option use AES encryption in CBC mode. 128 encryption bit is all you need and you can use anything (0 is acceptable) as the IV.