I'm facing a problem of password encryption. I'm using AES128 to encrypt the data. Here is the following code:
LoginController.m
#import <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonKeyDerivation.h>
#import "NSData+AES.h"
- (IBAction)loginButtonClick:(id)sender
{
NSData *toencrypt = [password.text dataUsingEncoding:NSUTF8StringEncoding];
NSString *credentials = [self md5:[NSString stringWithFormat:#"63jhdf83hf73haf3983f"]]; //returns 679fb1ddf7d81bee0aff2ef251fe6bf5
NSString *iv = #"kdf67398DF7383fd";
NSData *data = [toencrypt AES128EncryptWithKey:credentials iv:iv];
NSString *postdata = [data base64Encoding]; //base64Encoding is implemented in NSData+AES.m
NSLog(#"Original Password: %#",password);
NSLog(#"Encrypted Password: %#",postdata);
}
**N.B: md5 conversion and base64Encoding is working fine.
NSData+AES.m
static char encodingTable[64] =
{
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
#implementation NSData (AES)
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv
{
char keyPtr[kCCKeySizeAES128+1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCKeySizeAES128+1];
bzero(ivPtr, sizeof(ivPtr));
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
int newSize = 0;
if(diff > 0){
newSize = (int)dataLength + diff;
}
char dataPtr[newSize];
memcpy(dataPtr, [self bytes], [self length]);
for(int i = 0; i < diff; i++){
dataPtr[i + dataLength] = 0x00;
}
size_t bufferSize = newSize + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
0x00, //No padding
keyPtr,
kCCKeySizeAES128,
ivPtr,
dataPtr,
sizeof(dataPtr),
buffer,
bufferSize,
&numBytesEncrypted);
if(cryptStatus == kCCSuccess)
{
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
return nil;
}
Now If a send "brad" it should return "ur/bq4Rz". But unfortunately it returning "83+eQZW3eI4UWGdNk4nnUw==". Whats the problem? Please help me out. Thanks in advance.
Note that this code is pretty insecure. It reuses the same Key+IV for every message, which greatly simplifies the job of decrypting the message for an attacker (especially short messages). And it has other security problems (poor keyspace, bad padding scheme)
That said, the piece that is specifically broken is that you're passing a 32-byte (256-bit) "key" but you're only using 128-bits of it. You probably meant to use kCCKeySizeAES256 as the key length. This is unrelated to kCCAlgorithmAES128 which refers to the block size, not the key length. In the diff code, you meant to use kCCBlockSizeAES128 rather than kCCKeySizeAES128, but they are both 16 so it doesn't impact the result.
As #Codo notes, standard CBC mode cannot output cipher text that is not a multiple of 16 bytes in length. Why do you believe "ur/bq4Rz" is the correct encryption? There is a modification of CBC that can do this (it adds what is called "cipher text stealing" or CTS), but this isn't supported by iOS.
Your code is doing zero-padding, which is a very poor way to do it (you're just adding zeros until you get to a 16-byte boundary). The standard (and more secure) solution is PKCS#7 padding, which you're turning off.
If I had to guess, you're copying code that zero-pads the data and then truncates the cipher text to length. The decryption then probably pads the cipher text with zeros and then truncates the garbage at the end (probably based on knowing the length beforehand). I see how this can work, but it's not the normal approach.
If you want an example of how to do this simply, see Properly Encrypting With AES and CommonCrypto. If you want a more secure approach that does all of it for you, see RNCryptor. Neither of these will generate cipher text that is the same length as your plaintext, however.
I found some issues, for example you don't check if getCString:maxLength:endocind failed or not. This is deprecated function and it may fail here, as a result encryption method may use completly different key and IV
Here is my version of this method. This should do the job right:
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv
{
int diff = kCCKeySizeAES128 - (self.length % kCCKeySizeAES128);
NSMutableData *adjustedData = [NSMutableData dataWithData:self];
[adjustedData appendData:[[NSMutableData alloc] initWithLength:diff]];
size_t bufferSize = adjustedData.length + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
0x00, //No padding
[key UTF8String], kCCKeySizeAES128,
[iv UTF8String],
adjustedData.bytes,
adjustedData.length,
buffer,
bufferSize,
&numBytesEncrypted);
if(cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
return nil;
}
Related
I am trying to decrypt a string that was first encrypted using des in ecb mode, and then encoded in base64.
This is my code:
+ (NSString *)decrypt:(NSString *)encryptedText
{
NSString *key = #"12345678";
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:encryptedText options:0];
size_t numBytesDecrypted = 0;
size_t bufferSize = [decodedData length] + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
char keyPtr[kCCKeySizeDES+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
keyPtr,
kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[decodedData bytes], [decodedData length], /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
NSData *val = [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
return [[NSString alloc] initWithData:val encoding:NSUTF8StringEncoding];
}
However I am getting a nil string in return...any ideas?
You are using DES but are specifying the key size as: kCCKeySizeAES256 in the call to: CCCrypt.
There are so many things wrong with this code from a security standpoint, don't use this in a real app. This is no longer best practice. Among other things the password should be converted to key with a Password-Based Key Derivation Function such as PBKDF2. Also using DES and ECB mode is a weaknesses.
I am working on AES encryption and decryption.
I am giving single data and single key to decrypt but am getting different outputs every time.
I am using like this:
+(NSData*)decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv
{
NSData* result = nil;
// setup key
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:FBENCRYPT_KEY_SIZE];
// setup iv
char cIv[FBENCRYPT_BLOCK_SIZE];
bzero(cIv, FBENCRYPT_BLOCK_SIZE);
if (iv) {
[iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
}
// setup output buffer
size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
void *buffer = malloc(bufferSize);
// do decrypt
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
FBENCRYPT_ALGORITHM,
kCCOptionECBMode,
cKey,
FBENCRYPT_KEY_SIZE,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&decryptedSize);
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(#"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus);
}
return result;
}
Best guess without any example data, etc is that the key or iv are the incorrect size and/or the padding or mode is different.
I try to decrypt a String message with AES decryption.
- (NSData *)AES256DecryptWithKey:(NSString *)key andIV:(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];
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,
//[iv cStringUsingEncoding:NSUTF8StringEncoding] /* initialization vector (optional) */,
NULL,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted );
if( cryptStatus == kCCSuccess )
{
NSLog(#"CRYPTSTATUS %d",cryptStatus);
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
NSLog(#"CRYPTSTATUS %d",cryptStatus);
free( buffer ); //free the buffer
return nil;
}
But the Result is truncated, do anyone have a suggestion? It seems to be a problem with the padding, but I dont know. The AES key will be later (RSA encrypted) send.
Would be nice if you could give me suggestions.
EDIT: Input (base64 encoded)
NSData *keydata = [[NSData alloc]initWithBase64EncodedString:#"QUFBQUE5MThEOTMyOEJCQkJCQkJCODhFMTM3MURFREQ="];
NSString *key = [[NSString alloc]initWithData:keydata encoding:NSUTF8StringEncoding];
NSData *msgnormal = [[NSData alloc]initWithBase64EncodedString:#"oE4LOCjOfjPeggXsDbLQ4ko+57kdb/5EBUcmlTBvaaI="];
NSData *decrypted = [msgnormal AES256DecryptWithKey:key andIV:#""];
NSLog(#"DECRYPTED: %#",[[NSString alloc]initWithData:decrypted encoding:NSUTF8StringEncoding]);
The input should be padded to the nearest block also. If the input ends on a block boundary, you actually still add a whole other block just so you always have padding (that you will remove later).
You must know where the end of the decrypted text is and where the padding starts. Typically, this is handled with padding such as PKCS7. Since you will know how many bytes are the padding, it's easy to strip off later.
I am encrypting a file using the following command in Terminal on my Mac
openssl enc -aes-256-cbc -salt -in testin.txt -out test.txt
I am then uploading my file to the web and then downloading it in my iOS app, once the download has finished I am decrypting it.
NSString *password = #"myPasswordTextWhichIs32CharsLong";
NSData *decryptedData = [self.testData AES256DecryptWithKey:password];
NSString* decryptedStr = [[NSString alloc] initWithData:decryptedData encoding:NSASCIIStringEncoding];
NSLog(#"decrypted string = %#",decryptedStr);
I then have a NSData-AES.m file containing:
#import "NSData-AES.h"
#import <CommonCrypto/CommonCryptor.h>
#implementation NSData(AES)
- (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;
}
The NSLog for decrypted string returns a different string than in the test.txt so I know some sort of decryption is occurring.
Any ideas where I am going wrong and what needs to be changed.
NOTE: I tried the openssl terminal command without -salt but no different
I am trying encrypt data on an iPhone and send up the encrypted text to a web service for them to decrypt it. If the decryption works then it returns the First name in the xml as a confirmation things worked. Here is my Xcode
Note: The 'key' is the same in both xcode and web service
The information I want encrypted:
NSString *fnameencrypted = [[NSString alloc] AES256EncryptWithKey:f_name.text withKey:key]];
NSString *lnameencrypted = [[NSString alloc] AES256EncryptWithKey:l_name.text withKey:key]];
The NSString method
-(NSString *)AES256EncryptWithKey:(NSString *)plaintext withKey:(NSString *)key{
NSData *plainData = [plaintext dataUsingEncoding:NSASCIIStringEncoding];
NSData *encryptedData = [plainData AES256EncryptWithKey:key];
NSString *encryptedString = [encryptedData base64Encoding];
return encryptedString;
}
EDIT
The encryption method
-(NSData *)AES256EncryptWithKey:(NSString *)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:NSASCIIStringEncoding];
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, kCCOptionECBMode + 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;
}
Here is my web service code
private static string Decrypt(string encryptedText, string completeEncodedKey, int keySize)
{
RijndealManaged aesEncryption = new RijndealManaged();
aesEncryption.KeySize = keySize; //keySize is 256
aesEncryption.BlockSize = 128;
aesEncryption.Mode = CipherMode.ECB;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = Convert.FromBase64String(ASCIIEncoding.ACSII.GetString(Convert.FromBase64String(completeEncodedString)).Split(',')[0]);
aesEncryption.Key = Convert.FromBase64String(ASCIIEncoding.ACSII.GetString(Convert.FromBase64String(completeEncodedString)).Split(',')[1]);
ICryptoTransform decrypto = aesEncryption.CreateDecryptor();
byte[] encryptedBytes = Convert.FromBase64CharArray(encryptedText.ToCharArray(), 0, encryptedText.Length);
return ASCIIEncoding.ASCII.GetString(decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
}
This code does not work because it returns
<response><return>0</return></response>