I am developing an application for iPhone on Snow Leopard with Xcode 3.1 that receives from a restful web service an encrypted text in hexadecimal format with the algorithm AES 128-bit (CBC). The algorithm uses an initialization vector + secret key. How do I decrypt this text? Thanks to everyone for the tips that I will succeed in giving.
EDIT:
I get response from REST Server in hex AND crypted format,I try with this code but i receive always bad param error. Can you help me to find the error?
Is it possible that i firstly convert the string response into binary format?
NSString *response = [request responseString];
NSData *encryptedData = [response dataUsingEncoding: NSASCIIStringEncoding];
NSString *key = #"32charlength";
NSString *iv = #"16charlength";
NSData *unencryptedData = NULL;
size_t unencryptedLength = [unencryptedData length];
CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, key, kCCKeySizeAES128, iv, [encryptedData bytes], [encryptedData length], unencryptedData, [encryptedData length], &unencryptedLength);
NSString *output = [[NSString alloc] initWithBytes:unencryptedData length:unencryptedLength encoding:NSUTF8StringEncoding];
if (ccStatus == kCCSuccess) risultato.text = #"SUCCESS";
else if (ccStatus == kCCParamError) risultato.text = #"BAD PARAM";
else if (ccStatus == kCCBufferTooSmall) risultato.text = #"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) risultato.text = #"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) risultato.text = #"ALIGNMENT";
else if (ccStatus == kCCDecodeError) risultato.text = #"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) risultato.text = #"UNIMPLEMENTED";
EDIT2:
this function return BAD PARAM because haven't the right buffer size where allocate decrypted data. I edit the function in this working way:
NSData *encryptedData = [response dataUsingEncoding: NSASCIIStringEncoding];
const void *key = #"32charlength;
uint8_t *iv = #"16charlength";
char buffer[4*4096];
memset(buffer, '\0', sizeof(buffer));
size_t size = sizeof(buffer);
CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
0,
key,
kCCKeySizeAES128,
iv,
[encryptedData bytes],
[encryptedData length],
buffer,
sizeof(buffer),
&size);
This function working for me.. thanks so much.
EDIT 23 MARCH------
Now the system work form me with 16 byte key size. Now i have a question, what i can do to implement 32 byte key size ? thanks so much.
This is possible using the CCCryptor functions included in the <CommonCrypto/CommonCryptor.h> header. Check man CCCryptor for the gory details, in your case it sounds like you can use a single call to CCCrypt() to decode the received data:
CCCryptorStatus
CCCrypt(CCOperation op, CCAlgorithm alg, CCOptions options, const void *key, size_t keyLength,
const void *iv, const void *dataIn, size_t dataInLength, void *dataOut, size_t dataOutAvailable,
size_t *dataOutMoved);
Assuming you have the data to be decrypted in NSData *encryptedData you could try something like:
char * key = "shouldbe16chars.";
NSUInteger dataLength = [encryptedData length];
uint8_t unencryptedData[dataLength + kCCKeySizeAES128];
size_t unencryptedLength;
CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, key, kCCKeySizeAES128, NULL, [encryptedData bytes], dataLength, unencryptedData, dataLength, &unencryptedLength);
NSString *output = [[NSString alloc] initWithBytes:unencryptedData length:unencryptedLength encoding:NSUTF8StringEncoding];
This is untested, make sure you check the return value of CCCrypt for errors. Check the header file for details, it is well documented.
Related
I am trying to encrypt an xml string.After the encryption is done then the decrypt the encrypted d I am getting the string as ALIGNMENT.
I dont understand what is the reason
(NSString*) doCipher:(NSString*)plainText:(CCOperation)encryptOrDecrypt {
const void *vplainText;
size_t plainTextBufferSize;
if (encryptOrDecrypt == kCCDecrypt)
{
NSData *EncryptData = [NSData dataFromBase64String:plainText];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else
{
plainTextBufferSize = [plainText length];
vplainText = (const void *) [plainText UTF8String];
}
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
// uint8_t iv[kCCBlockSize3DES];
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
// memset((void *) iv, 0x0, (size_t) sizeof(iv));
NSString *key = #"123456789012345678901234";
NSString *initVec = #"init Vec";
const void *vkey = (const void *) [key UTF8String];
const void *vinitVec = (const void *) [initVec UTF8String];
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding,
vkey, //"123456789012345678901234", //key
kCCKeySize3DES,
vinitVec, //"init Vec", //iv,
vplainText, //"Your Name", //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
//if (ccStatus == kCCSuccess) NSLog(#"SUCCESS");
//else/
if (ccStatus == kCCParamError) return #"PARAM ERROR";
else if (ccStatus == kCCBufferTooSmall) return #"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) return #"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) return #"ALIGNMENT";
else if (ccStatus == kCCDecodeError) return #"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) return #"UNIMPLEMENTED";
NSString *result;
if (encryptOrDecrypt == kCCDecrypt)
{
result = [ [NSString alloc] initWithData: [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIStringEncoding];
}
else
{
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [myData base64EncodedString];
}
return result;
}
kCCAlignmentError means that the data you are passing is not correctly padded (specifically that the entire data length including padding is not a multiple of the block size). The most likely cause is that you are truncating your encrypted data somewhere, possibly outside of this routine.
You should check your encrypted data at each step and make sure that it is always a multiple of the block size (8 bytes). You'll need to base64-decode before checking the length.
i am fairly new to iOS development and objective c.
I am developing an application which will send encrypted data to a server.
The server uses 3des with cbc and no padding.
I have read most of the related questions in stackoverflow but still unable to get it work.
Been working on this for few days but still unable to get it to match with the server encryption.
Here is what i have work out:
NSString* plaintexthex = #"536176696E67204163636F756E747C313233343536000000";
NSData *dTextIn = [self dataFromHexString:plaintexthex]; //my own way of convert hex to data
NSString* keyhex = #"6E7B336FD2051BA165A9362BD9735531";
NSData *_keyData = [self dataFromHexString:keyhex]; //my own way of convert hex to data
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = ([dTextIn length] + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x00, bufferPtrSize);
uint8_t iv[kCCBlockSize3DES];
memset((void *) iv, 0x00, (size_t) sizeof(iv));
unsigned char *bytePtr = (unsigned char *)[_keyData bytes];
ccStatus = CCCrypt(kCCEncrypt, // CCoperation op
kCCAlgorithm3DES, // CCAlgorithm alg
kCCModeCBC, // CCOptions
[_keyData bytes], // const void *key
kCCKeySize3DES, // 3DES key size length 24 bit
iv, // const void *iv,
[dTextIn bytes], // const void *dataIn
[dTextIn length], // size_t dataInLength
bufferPtr, // void *dataOut
bufferPtrSize, // size_t dataOutAvailable
&movedBytes); // size_t *dataOutMoved
NSString *result;
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length: (NSUInteger)movedBytes];
result = [self hexStringFromData:myData];
NSLog(#"Data to encrypt %#",dTextIn);
NSLog(#"Encryption key %#",_keyData);
NSLog(#"Bytes of key are %s ", bytePtr);
NSLog(#"Key length %d ",[_keyData length]);
NSLog(#"Encrypted bytes %#", myData);
NSLog(#"Encrypted string %#", result);
NSLog(#"Encrypted string length %d", [result length]);
- (NSData *)dataFromHexString:(NSString *)string
{
NSMutableData *stringData = [[[NSMutableData alloc] init] autorelease];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [string length] / 2; i++) {
byte_chars[0] = [string characterAtIndex:i*2];
byte_chars[1] = [string characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[stringData appendBytes:&whole_byte length:1];
}
return stringData;
}
I have develop a similar application on the Android platform and it works well with the server.
Heres the encryption of the function i used on the Android platform.
public byte[] encrypt(byte[] key, byte[] message) throws Exception {
byte [] plainTextBytes = message;
byte[] encryptKey = key;
SecretKey theKey = new SecretKeySpec(encryptKey, "DESede");
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
IvParameterSpec IvParameters = new IvParameterSpec(new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00});
cipher.init(Cipher.ENCRYPT_MODE, theKey, IvParameters);
byte[] encrypted = cipher.doFinal(plainTextBytes);
return encrypted;
}
Basically i want to replicate this similar encryption to be used on the iOS platform.
Any help will be welcome and thank you in advance.
kCCModeCBC is a mode, not an option. The option you want is 0. CBC is the default mode for CCCrypt(). The default is also no padding.
I am an iOS user, not a developer, but as far as I know, iOS no longer supports 3DES. I use an iPad for VPN, and iOS 3 worked fine with 3DES encryption, but as of iOS 4, the minimum encryption level required is AES128.
Hope that helps.
I have the following code inside the NSData+AES256 class. I am trying AES CBC encryption to a NSString with the following code. I have a key and a iv. But getting null in result. Can not find out what's wrong. This is what I tried-
NSString *initV= #"somekey*********";
NSData *input= [initV dataUsingEncoding:NSUTF8StringEncoding];
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCModeCBC,
keyPtr, kCCKeySizeAES256,
[input bytes] /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
This is the code that worked for me finally-
-(NSData *)AES256EncryptWithKey:(NSString *)key {
NSUInteger dataLength = [self length];
NSData *keyGiven= [key dataUsingEncoding:NSUTF8StringEncoding];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
NSString *initV= #"***************";
NSData *input= [initV dataUsingEncoding:NSUTF8StringEncoding];
size_t numBytesEncrypted = 0;
CCCryptorRef ccRef;
CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, 0, (const void *)[keyGiven bytes], kCCKeySizeAES256, (const void *)[input bytes], &ccRef);
CCCryptorStatus cryptStatus = CCCryptorUpdate(ccRef, [self bytes], dataLength, buffer, bufferSize, &numBytesEncrypted);
CCCryptorRelease(ccRef);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
I am converting an NSString to NSData and passing it for encryption. The NSString must be of 16 characters. If it is of less than 16 characters I make it 16 by appending spaces.
I don't know if this is going to help anybody. But I just thought I should share. Thanks.
I need to perform AES 256 decryption in my iphone app. The scenario is that the plain text is base64 encoded and encrypted using AES256 bit (OFB mode) in an online webpage.
In my app, I retrieve the encrypted text from that webpage as a query string. Here I have done base64 decoding and AES256 Decryption.
But I am getting -4304 status.
I have used kCCOptionPKCS7Padding. Even if I change the padding also, I am not getting proper Decrypted Plain text. Only unreadable text is displayed.
I have used http://isv.appspot.com/app/enc for checking the AES256 encryption with base64 by setting OFB mode.
Following code has two methods which I used for base64 decoding and AES256 decryption
+ (NSString*)decryptBase64String:(NSString*)encryptedBase64String keyString:(NSString*)keyString
{
NSData* encryptedData = [NSData dataFromBase64String:encryptedBase64String];
NSLog(#"encryptedData %#",encryptedData);
// NSData *strData = [encryptedData subdataWithRange:NSMakeRange(0, [encryptedData length] - 2)];
// NSString* newStr = nil;
NSData* keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
NSData* data = [self decryptData:encryptedData
key:keyData
iv:nil];
// newStr = [NSString stringWithCString:[strData bytes] encoding:NSUTF8StringEncoding];
if (data) {
return [[[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding] autorelease];
} else {
return nil;
}
}
+ (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,
kCCOptionPKCS7Padding,
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;
}
Could anyone help me out to get the plain text by using this method?
Based on the facts in your question, the base64 encoded data is encrypted, not the decoded variation of that data. Because of this, you need to decrypt the base64 encoded data and then base64 decode it.
I've seen quite a lot of posts about CCCrypt and 3DES on the iPhone at various places around the Net, but there does not seem to be a working example of using it with ECB mode, always with PKCS7Padding.
Does anyone have any working code to encrypt and decrypt a passed string using 3DES and ECB mode using the CCCrypt function?
Currently my code (which I admit came from a site somewhere, I think it was Apple's own developer forums) is as follows:
+ (NSString*)doCipher:(NSString*)plainText action:(CCOperation)encryptOrDecrypt {
const void *vplainText;
size_t plainTextBufferSize;
if (encryptOrDecrypt == kCCDecrypt) {
NSData *EncryptData = [[NSData alloc] initWithBase64EncodedString:plainText];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else {
//plainTextBufferSize = [plainText length];
//vplainText = (const void *)[plainText UTF8String];
NSData *plainTextData = [plainText dataUsingEncoding: NSUTF8StringEncoding];
plainTextBufferSize = [plainTextData length];
vplainText = [plainTextData bytes];
}
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
// uint8_t ivkCCBlockSize3DES;
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
// memset((void *) iv, 0x0, (size_t) sizeof(iv));
NSString *key = #"123456789012345678901234";
NSString *initVec = #"init Vec";
const void *vkey = (const void *)[key UTF8String];
const void *vinitVec = (const void *)[initVec UTF8String];
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
kCCOptionECBMode,
vkey, //"123456789012345678901234", //key
kCCKeySize3DES,
nil, //"init Vec", //iv,
vplainText, //"Your Name", //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
//if (ccStatus == kCCSuccess) NSLog(#"SUCCESS");
/*else*/ if (ccStatus == kCCParamError) return #"PARAM ERROR";
else if (ccStatus == kCCBufferTooSmall) return #"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) return #"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) return #"ALIGNMENT";
else if (ccStatus == kCCDecodeError) return #"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) return #"UNIMPLEMENTED";
NSString *result;
if (encryptOrDecrypt == kCCDecrypt) {
result = [[[NSString alloc] initWithData:[NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIStringEncoding] autorelease];
}
else {
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [myData base64EncodingWithLineLength:movedBytes];
}
return result; }
The above ALWAYS fails with PARAM ERROR.
- (NSData *) encrypt:(NSString *) dataToEncrypt{
NSUInteger data_length= [dataToEncrypt length];
uint8_t input_raw_data[data_length];
//The [dataToEncrypt length] gives the number of chars present in the string.So say there are 10 chars.
//Now,the getBytes needs to get the raw bytes from this i.e. binary NSData.But suppose the encoding was
//full 16 bit encoding then the number of bytes needed wd have been double- 20.But as we are using the
//NSUTF8StringEncoding,the number of byes needed is 1 per char as the chars(even if originally unicode are
//compressed into an 8 bit UTF8 encoding.)
[dataToEncrypt getBytes:&input_raw_data maxLength:data_length usedLength:NULL encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,data_length) remainingRange:NULL];
//According to 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 buffer_size = data_length + kCCBlockSizeAES128;
void* buffer = malloc(buffer_size);
size_t num_bytes_encrypted = 0;
CCCryptorStatus crypt_status = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[self.symmetricKey bytes], kCCKeySizeAES256,
NULL,
input_raw_data, data_length,
buffer, buffer_size,
&num_bytes_encrypted);
NSLog(#"~~num bytes encrypted: %d",num_bytes_encrypted);
if (crypt_status == kCCSuccess){
NSLog(#"~~Data encoded successfully...");
return [NSData dataWithBytesNoCopy:buffer length:num_bytes_encrypted];
}
free(buffer); //free the buffer;
return nil;
}
- (NSData *) decrypt:(NSData *) dataToDecrypt{
NSUInteger data_length= [dataToDecrypt 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 buffer_size = data_length + kCCBlockSizeAES128;
void* buffer = malloc(buffer_size);
size_t num_bytes_decrypted = 0;
CCCryptorStatus crypt_status = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
[self.symmetricKey bytes], kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[dataToDecrypt bytes], data_length, /* input */
buffer, buffer_size, /* output */
&num_bytes_decrypted);
NSLog(#"~~num bytes decrypted: %d",num_bytes_decrypted);
if (crypt_status == kCCSuccess){
NSLog(#"~~Data decoded successfully...");
return [NSData dataWithBytesNoCopy:buffer length:num_bytes_decrypted];
}
free(buffer); //free the buffer;
return nil;
}
The above code worked for me, with some changes. The changes are i decelare the moveBytes and bufferPtf as instance variable and did following chandge.
if (encryptOrDecrypt == kCCDecrypt) {
plainTextBufferSize = movedBytes;
vplainText = bufferPtr;
}
else {
plainTextBufferSize = [plainText length];
vplainText = (const void *)[plainText UTF8String];
}
instead base64encoding use nsasciiencoding.