I am trying to use DES encryption to encrypt passwords (Don't ask why DES, I know it's less secure). I am doing it for the first time in iOS, thus had to rely on another post about how to do it.
When I run the encryption it returns null, same with decrypting an already encrypted string(I used an online tool to encrypt). When I put a breakpoint to see what is going on, it stopped at CCCrypt mentioning EXC_BAD_ACCESS (Code 2).
I tried using different CCOptions, but it returns the same thing always.
Any hint what's going wrong? Is iv string required?
I have used the following NSString category to encrypt or decrypt Strings -
#import "NSString+DES.h"
#implementation NSString(DES)
- (NSString*) encryptDES: (NSString *) key
{
const void *vplainText;
size_t plainTextBufferSize;
plainTextBufferSize = [self length];
vplainText = (const void *) [self UTF8String];
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t *movedBytes;
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 *initVec = #"init Vec";
const void *vkey = (const void *) [key UTF8String];
//const void *vinitVec = (const void *) [initVec UTF8String];
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
vkey,
kCCKeySizeDES,
NULL,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
movedBytes);
NSString *result;
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [myData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return result;
}
- (NSString *) decryptDES: (NSString *) key
{
const void *vplainText;
size_t plainTextBufferSize;
plainTextBufferSize = [self length];
vplainText = (const void *) [self UTF8String];
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t *movedBytes;
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 *initVec = #"init Vec";
const void *vkey = (const void *) [key UTF8String];
//const void *vinitVec = (const void *) [initVec UTF8String];
ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
vkey, //"123456789012345678901234", //key
kCCKeySizeDES,
NULL,// vinitVec, //"init Vec", //iv,
vplainText, //"Your Name", //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
movedBytes);
NSString *result;
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [myData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return result;
}
#end
Update:
I have checked few more places and changed the code a little bit, the encryption works but doesn't get decrypted with correct value.
For example when I use YourName as string and 12345 as the key, I get Fu2sK61e7l5rkXRhAKjPWA== as the encrypted code, but the decryption returns +54qWCYTB5LkdARDZjAow== and not YourName.
Updated Code:
#import "NSString+DES.h"
#implementation NSString(DES)
- (NSString*) encryptDES: (NSString *) key
{
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSData *stringData = [self dataUsingEncoding:NSUTF8StringEncoding];
size_t numBytesEncrypted = 0;
size_t bufferSize = stringData.length + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
CCCryptorStatus result = CCCrypt( kCCEncrypt, kCCAlgorithmDES, kCCOptionPKCS7Padding,
keyData.bytes, kCCKeySizeDES,
NULL,
stringData.bytes, stringData.length,
buffer, bufferSize,
&numBytesEncrypted);
NSData *output = [NSData dataWithBytes:buffer length:numBytesEncrypted];
free(buffer);
if( result == kCCSuccess )
{
NSString *resultStr = [output base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return resultStr;
} else {
NSLog(#"Failed DES encrypt...");
return nil;
}
}
- (NSString *) decryptDES: (NSString *) key
{
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSData *stringData = [[NSData alloc] initWithBase64EncodedString:self options:0];
size_t numBytesEncrypted = 0;
size_t bufferSize = stringData.length + kCCBlockSizeDES;
void *buffer = malloc(bufferSize);
CCCryptorStatus result = CCCrypt( kCCDecrypt, kCCAlgorithmDES, kCCOptionPKCS7Padding,
keyData.bytes, kCCKeySizeDES,
NULL,
stringData.bytes, stringData.length,
buffer, bufferSize,
&numBytesEncrypted);
NSData *output = [NSData dataWithBytes:buffer length:numBytesEncrypted];
free(buffer);
if( result == kCCSuccess )
{
NSString *resultStr = [output base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return resultStr;
} else {
NSLog(#"Failed DES decrypt...");
return nil;
}
}
#end
There seems to be general confusion about the algorithm, DES or 3DES, a mix is being used but the key is 3DES (24-bytes). The key needs to be changed to 8-bytes. The block size constant should also be changed to kCCBlockSizeDES but that does not cause an error since it is the same value.
For the method:
- (NSString *) decryptDES: (NSString *) key
The bad access error is caused because no storage is allocated for movedBytes, just a pointer. Change the declaration to:
size_t movedBytes;
Change the reference to movedBytes in CCCrypt to &movedBytes.
Test output for encryption:
string: "Your Name"
key: "12345678"
movedBytes: 16
myData: 136142f6 6cd98e01 af1eef46 28d36499
result: E2FC9mzZjgGvHu9GKNNkmQ==
Notes from comments by request:
ECB mode does not use an iv.
The key needs to be exactly 8-bytes for DES, if it is to short the result will be undefined. In the updated code the key is 5-bytes but the length is given as 8-bytes (kCCKeySizeDES) so the three missing key bytes will be whatever bytes follow keyData.
The updated answer did not specify ECB mode, the default is CBC mode. Add kCCOptionECBMode.
In decrypt do not use Base64 encode, convert the data to a NSString:
NSString * resultStr = [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding];
If using an online encryption that uses the php mcrypt function the last block of the data will be incorrect because mcrypt does not support PKCS#7 padding, it uses non-standard and insecure null padding.
Related
I have used AES algorithm for encryption in android.
The following code we have used for encryption.
String seed = "somekey";
Key key = null;
// 128 bit key
byte[] byteKey = seed.substring(0, 16).getBytes("UTF-8");
key = new SecretKeySpec(byteKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(
new byte[16]));
byte[] encValue = cipher.doFinal(pValue.getBytes());
encryptedText = new BASE64Encoder().encode(encValue);
Can any one please provide the above logic for IOS.
Thanking in Advance.
It works both on android with:
#define FBENCRYPT_KEY_SIZE kCCKeySizeAES128
instead:
#define FBENCRYPT_KEY_SIZE kCCKeySizeAES256
You can use the following snippet as start point:
+ (NSData*)encryptData:(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 encrypt
size_t encryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
FBENCRYPT_ALGORITHM,
kCCOptionPKCS7Padding,
cKey,
FBENCRYPT_KEY_SIZE,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&encryptedSize);
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
} else {
free(buffer);
NSLog(#"[ERROR] failed to encrypt|CCCryptoStatus: %d", cryptStatus);
}
return result;
}
+ (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;
}
Constants
#define FBENCRYPT_ALGORITHM kCCAlgorithmAES128
#define FBENCRYPT_BLOCK_SIZE kCCBlockSizeAES128
#define FBENCRYPT_KEY_SIZE kCCKeySizeAES256
For more information, see FBEncryptor
Hope this helps.
Since AES has a bock size of 16-bytes PKCS#7Padding is required. Some earlier AES library implementers specified PKCS#5Padding in error.
PKCS#5Padding is only specified for a block size up to 8-bytes and PKCS#7Padding is specified for a block size up to 255-bytes. See Wikipedia: Padding. IOW PKCS#7Padding can be used in place of PKCS#5Padding.
I have the following code in objective-c
NSString* v_plainText = #"1234567890123456789012345678";
NSString* plainText = (const void *) [v_plainText UTF8String];
size_t plainTextBufferSize = [v_plainText length];
size_t bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
size_t movedBytes = 0;
uint8_t *bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
Byte iv[8] = {0,0,0,0,0,0,0,0};
NSString *key = (const void *) [#"12345678ABCDEFGH!##$%^&*" UTF8String];
CCCryptorStatus ccStatus;
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding & kCCModeCBC,
key,
kCCKeySize3DES,
iv,
plainText,
plainTextBufferSize,
(void *)bufferPtr, // output
bufferPtrSize,
&movedBytes);
NSData* result = [NSData dataWithBytes:(const void*)bufferPtr length:(NSUInteger)movedBytes];
NSString* str = [result base64EncodedStringWithOptions:0];
This gives this result:
geHFnvoept2aKiruo6InSvc7WVPdHNq2
When I run simular code in .NET it gives me this result:
geHFnvoept2aKiruo6InSvc7WVPdHNq2TENQX5q9Beg=
For some reason the objective-c version only returns 24 bytes while the input is 28 bytes. I would expect it to be 32 bytes like in the .NET version. I was unable to determine what I'm doing wrong here.
The encryption error is:
kCCOptionPKCS7Padding & kCCModeCBC,
You need |, not & but kCCModeCBC is not a valid option and no option is needed because CBC is the default mode, you simply need:
kCCOptionPKCS7Padding,
Unfortunatly there are other objectiveC coding errors, mainly:
NSString* plainText = (const void *) [v_plainText UTF8String];
NSString *key = (const void *) [#"12345678ABCDEFGH!##$%^&*" UTF8String];
Instead use:
NSData* plainText = [v_plainText dataUsingEncoding:NSUTF8StringEncoding];
NSData *key = [#"12345678ABCDEFGH!##$%^&*" dataUsingEncoding:NSUTF8StringEncoding];
Here is the complete code converted to use NSData:
NSString* plainText = #"1234567890123456789012345678";
NSString* keyText = #"12345678ABCDEFGH!##$%^&*";
NSData* plainData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
NSData *keyData = [keyText dataUsingEncoding:NSUTF8StringEncoding];
Byte iv[8] = {0,0,0,0,0,0,0,0};
size_t bufferSize = plainData.length + kCCBlockSize3DES;
NSMutableData *cypherData = [NSMutableData dataWithLength:bufferSize];
size_t movedBytes = 0;
CCCryptorStatus ccStatus;
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding,
keyData.bytes,
kCCKeySize3DES,
iv,
plainData.bytes,
plainData.length,
cypherData.mutableBytes,
cypherData.length,
&movedBytes);
cypherData.length = movedBytes;
NSString* str = [cypherData base64EncodedStringWithOptions:0];
NSLog(#"str: %#", str);
Oputput:
str: geHFnvoept2aKiruo6InSvc7WVPdHNq2TENQX5q9Beg=
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 developing an application which will send encrypted data to a server. The server uses 3des with CFB8 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 tried-
+ (NSString*) doCipher:(NSString*)plainText operation:(CCOperation)encryptOrDecrypt {
const void *vplainText;
NSData* plainTextData;
size_t plainTextBufferSize;
if (encryptOrDecrypt == kCCDecrypt)
{
NSData *EncryptData =[NSData dataWithBase64EncodedString:plainText];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else
{
plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
plainTextBufferSize = [plainTextData length];
}
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));
const void *vkey = kPrivateKey;
unsigned char IV[8]={0,0,0,0,0,0,0,0};
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
0,
[keyData bytes],
[key length],
IV,
[plainTextData bytes],
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;
}
It seems CCOptions currently supports kCCOptionPKCS7Padding and kCCOptionECBMode.How to achieve 3des with CFB8 and no padding.Any suggestion would be appreciated.
EDIT: Sorry; I overlooked a key point previously. You're never asking for CFB8 mode. The default is CBC mode.
You can't use CCCrypt() for this. You have to use CCCryptorCreateWithMode() so you can pass the mode. Then call CCCryptorUpdate() with the data and CCCryptorFinal() to finish.
If you don't want padding, why are you requesting padding? Remove kCCOptionPKCS7Padding. If you don't want any options (which you appear not to), just pass 0.
CFB-8 takes an initialization vector. You've set it to all 0's. Is this what the server is using? (This is a very poor IV; the IV for CFB should be random, not fixed.)
These lines are dangerous:
plainTextBufferSize = [plainText length];
vplainText = (const void *) [plainText UTF8String];
This will truncate any multi-byte strings. The better solution to this is to create an NSData:
plainTextData = [self.plainText dataUsingEncoding:NSUTF8StringEncoding];
You can then use bytes and length on plainTextData.
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>