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=
Related
I am trying to generate hmac SHA256 in objective C in the following way:
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
This works fine as long as the key is a string. Problem is on the external server command used to generate mac is:
openssl dgst -binary -sha256 -mac HMAC -macopt hexkey:$key
There hmac is getting generated considering key as hexkey . So obviously hmac generated is different.
How do i tell the CCMac function in objective C to consider the key as hex key? I already tried converting the string key to byte array and passing it to CCMAC but still didn't work.
In android I have achieved the same by converting the hex number to a Big Integer and then doing getBytes on it, use it to create the secret key.
Hope some of you will be able to guide me.
Is this what you're looking for?
- (NSData *)dataFromHexString:(NSString *)sHex {
const char *chars = [sHex UTF8String];
int i = 0;
NSUInteger len = sHex.length;
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:1];
}
return data;
}
- (NSData *)hmacForHexKey:(NSString *)hexkey andStringData:(NSString *)data
{
NSData *keyData = [self dataFromHexString:hexkey];
const char *cKey = [keyData bytes];
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, keyData.length, cData, strlen(cData), cHMAC);
return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}
- (NSData *)hmacForKey:(NSString *)key andStringData:(NSString *)data
{
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}
- (void)testIt {
NSString *key = #"This is my random key.";
NSString *hexKey = #"54686973206973206d792072616e646f6d206b65792e";
NSString *data = #"This is a data string.";
NSData *hmac1 = [self hmacForKey:key andStringData:data];
NSLog(#"hmacForKey : %#", hmac1);
NSData *hmac2 = [self hmacForHexKey:hexKey andStringData:data];
NSLog(#"hmacForHexKey: %#", hmac2);
}
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.
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.
So I'm trying to figure out how to do a hmacshad256 hash on ios as that's the hash I did for the wcf service api I made. I've been trying to look for some info about it but would usually just end up getting a SHA-256 hash.
This is the only reference I have:
Need to generate HMAC SHA256 hash in Objective C as in Java
And I'm not sure if that's the only way to do it (importing a java hmac class)
Any help is appreciated.
Thanks!
NSString * parameters = #"string to hash"
NSString *salt = #"saltStringHere";
NSData *saltData = [salt dataUsingEncoding:NSUTF8StringEncoding];
NSData *paramData = [parameters dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH ];
CCHmac(kCCHmacAlgSHA256, saltData.bytes, saltData.length, paramData.bytes, paramData.length, hash.mutableBytes);
NSString *base64Hash = [hash base64Encoding];
and also
#import <CommonCrypto/CommonHMAC.h>
Since base64Encoding is deprecated from iOS 7.0, the last line should be:
NSString *base64Hash = [hash base64EncodedStringWithOptions:0];
Here is the solution I'm submitting that I put together from other answers on the matter:
This is easily adapted to other hash types by changing CC_SHA256_DIGEST_LENGTH and kCCHmacAlgSHA256.
If you're interested in doing that, check out the CommonDigest.h file within the CommonCrypto library.
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>
+ (NSString *)hmac:(NSString *)plaintext withKey:(NSString *)key
{
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [plaintext cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[HMAC appendFormat:#"%02x", buffer[i]];
}
return HMAC;
}
This has been tested on iOS 8.x and iOS 7.x
+ (NSString *)hmacSHA256EncryptString{
NSString * parameterSecret = #"input secret key";
NSString *plainString = #"input encrypt content string";
const char *secretKey = [parameterSecret cStringUsingEncoding:NSUTF8StringEncoding];
const char *plainData = [plainString cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, secretKey, strlen(secretKey), plainData, strlen(plainData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *bufferChar = (const unsigned char *)[HMACData bytes];
NSMutableString *hmacString = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[hmacString appendFormat:#"%02x", bufferChar[i]];
}
return hmacString;
}