I am trying to decrypt an AES encryption with the following information provided:
Encryption type: AES 128
Mode: CBC
Padding: PKCS5 (.net uses PKCS7)
Password(key): jayjay
Encryption ref: https://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/
Example provided:
Encrypted string: vsiv+Tv0H9Tz0GHtf2S4KBXVV+0L6Wr0xCWwA1QOPs8rnyRxNDDgH4ogo5Mm+wrzEW6uFD2kdFI2IcSTx+RMu
3817A7n1/ZLCjqq6ZTXI3QZbWgr6AH3SOGM/lcnud4EROGUiZuTiWIBjxTy+q/8tA==
Decrypted string (expected): BadgeID^CustIDLookup^First Name^Last Name^Title^Company^Address 1^Address
2^City^State^Zip^Country^Phone^Email
Unfortunately without actually knowing how it has been encrypted, it seems that the decoding is not fully working.
I've created a category of NSString with the following method:
- (NSString *)decryptWithKey:(NSString *)key {
NSData *stringData = [self dataUsingEncoding:NSUTF8StringEncoding];
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
size_t decryptedDataLength = [stringData length] + kCCKeySizeAES128;
NSMutableData *decryptedData = [NSMutableData dataWithLength:kCCKeySizeAES128 + decryptedDataLength];
size_t numberOfBytes;
CCCryptorStatus status = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
[keyData bytes],
kCCKeySizeAES128,
nil,
[stringData bytes], [stringData length],
decryptedData.mutableBytes, [decryptedData length],
&numberOfBytes);
if (status == kCCSuccess) {
return [decryptedData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
} else {
NSLog(#"Status: %d", status);
return #"";
}
}
I get a kCCSuccess status but the string is not as expected. Any help will be greatly appreciated.
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;
}
I'm trying to encrypt a request, I made this function but I'm getting a different encrypt value in the .NET server. This is the function I'm using in iOS:
- (NSData *)Encrypt3DESWithInput:(NSString *)input
key:(NSString *)key
{
NSData *inputData = [input dataUsingEncoding:NSUTF8StringEncoding];
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [inputData length];
size_t bufferSize = dataLength + kCCBlockSize3DES;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding,
[keyData bytes],
kCCKeySize3DES,
initializationVector,
[inputData bytes],
dataLength,
buffer,
bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSLog(#"Success");
//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;
}
These are the values im using in the private key and the initialization vector:
static NSString *const privateKey = #"";
static const char initializationVector[8] = {31, 10, 35, 17, 0, 42, 151, 64};
Use a key that is the natural length of the encryption/decryption routine. Don't rely on key padding, that is undefined. If you have a password use PBKDF2 to create a full length secure key..
The key string is #"" yet the key data is created by [key dataUsingEncoding:NSUTF8StringEncoding]
NSString *keyString = #"";
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
NSLog output:
keyString: ''
keyData: <>
Note the key length is 0 so the DES method will access random data from memory.
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 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.