I am planning to implement the AES encryption in my application and for this I went through an informative tutorial by Rob Napier :
It was a wonderful read and I was able to encrypt few strings using :
USING ROB NAPIER RNCRYPTOR CLASS
NSString * const
kRNCryptManagerErrorDomain = #"net.robnapier.RNCryptManager";
const CCAlgorithm kAlgorithm = kCCAlgorithmAES128;
const NSUInteger kAlgorithmKeySize = kCCKeySizeAES128;
const NSUInteger kAlgorithmBlockSize = kCCBlockSizeAES128;
const NSUInteger kAlgorithmIVSize = kCCBlockSizeAES128;
const NSUInteger kPBKDFSaltSize = 8;
const NSUInteger kPBKDFRounds = 10000; // ~80ms on an iPhone 4
// ===================
+ (NSData *)encryptedDataForData:(NSData *)data
password:(NSString *)password
iv:(NSData **)iv
salt:(NSData **)salt
error:(NSError **)error {
NSAssert(iv, #"IV must not be NULL");
NSAssert(salt, #"salt must not be NULL");
*iv = [self randomDataOfLength:kAlgorithmIVSize];
*salt = [self randomDataOfLength:kPBKDFSaltSize];
NSData *key = [self AESKeyForPassword:password salt:*salt];
size_t outLength;
NSMutableData *
cipherData = [NSMutableData dataWithLength:data.length +
kAlgorithmBlockSize];
CCCryptorStatus
result = CCCrypt(kCCEncrypt, // operation
kAlgorithm, // Algorithm
kCCOptionPKCS7Padding, // options
key.bytes, // key
key.length, // keylength
(*iv).bytes,// iv
data.bytes, // dataIn
data.length, // dataInLength,
cipherData.mutableBytes, // dataOut
cipherData.length, // dataOutAvailable
&outLength); // dataOutMoved
if (result == kCCSuccess) {
cipherData.length = outLength;
}
else {
if (error) {
*error = [NSError errorWithDomain:kRNCryptManagerErrorDomain
code:result
userInfo:nil];
}
return nil;
}
return cipherData;
}
// ===================
+ (NSData *)randomDataOfLength:(size_t)length {
NSMutableData *data = [NSMutableData dataWithLength:length];
int result = SecRandomCopyBytes(kSecRandomDefault,
length,
data.mutableBytes);
NSAssert(result == 0, #"Unable to generate random bytes: %d",
errno);
return data;
}
// ===================
// Replace this with a 10,000 hash calls if you don't have CCKeyDerivationPBKDF
+ (NSData *)AESKeyForPassword:(NSString *)password
salt:(NSData *)salt {
NSMutableData *
derivedKey = [NSMutableData dataWithLength:kAlgorithmKeySize];
int
result = CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm
password.UTF8String, // password
[password lengthOfBytesUsingEncoding:NSUTF8StringEncoding], // passwordLength
salt.bytes, // salt
salt.length, // saltLen
kCCPRFHmacAlgSHA1, // PRF
kPBKDFRounds, // rounds
derivedKey.mutableBytes, // derivedKey
derivedKey.length); // derivedKeyLen
// Do not log password here
NSAssert(result == kCCSuccess,
#"Unable to create AES key for password: %d", result);
return derivedKey;
}
But while decrypting I am not able to decrypt properly and I am getting null in the scenario: For your reference the decrypt code is :
+ (NSData*)decryptData:(NSData*)data key:(NSData*)key error:(NSError **)error
{
if (key.length != 16 && key.length != 24 && key.length != 32) {
*error = [NSError errorWithDomain:#"keyLengthError" code:-1 userInfo:nil];
return nil;
}
CCCryptorStatus ccStatus = kCCSuccess;
int ivLength = kCCBlockSizeAES128;
size_t clearBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:data.length - ivLength];
NSLog(#"Data Out String Decrypt%#", dataOut);
ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes,
key.length,
data.bytes,
data.bytes + ivLength,
data.length - ivLength,
dataOut.mutableBytes,
dataOut.length,
&clearBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = clearBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError" code:ccStatus userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}
Where I am getting wrong in this scenario? I have been trying for few days to sort it out. Can someone please help me?
the method given in the example you mentioned refers Rob Napiers Github Repo.
Just testet it with your given password, salt, etc.. and it just works!
Yes understood, you want to throw out password: and iv: as well the salt: parameter when decrypting and go only with key:. Well you need at least iv: to do that. But again as Rob commented to your other question, don't re-invent the wheel.
The method i linked above is just working fine with your parameters for decrypting. The only difference to your code is that password, iv and salt are given to decrypt.
apart from the idea you want to develop something that can decrypt without a password you will have to digg deeper into how CCKeyDerivationPBKDF() (CommonKeyDerivation.h) is working.
Edit: As you asked to have a way to pack and unpack your salt, iv and cypher thats pretty simple with NSData.
+ (NSData *)packWithSalt:(NSData*)salt IV:(NSData*)iv Cypher:(NSData*)tocypher {
//adding Salt + IV + Cipher text
NSMutableData *combi = [NSMutableData data];
//[combi appendBytes:salt.bytes length:16];
//[combi appendBytes:iv.bytes length:16]; //16
//[combi appendBytes:tocypher.bytes length:tocypher.length];
[combi appendData:salt];
[combi appendData:iv];
[combi appendData:tocypher];
return combi;
}
+ (NSData*)cypherUnpackToSalt:(NSMutableData**)salt andIV:(NSMutableData**)iv fromPackData:(NSData*)pack {
void *sBuff[16] = {};
void *iBuff[16] = {};
NSUInteger len = pack.length - 16 - 16; //keep length flexible
void *pBuff = malloc(sizeof(Byte)*len); //needs dynamically size of buff
[pack getBytes:sBuff range:NSMakeRange(0, 16)];
[pack getBytes:iBuff range:NSMakeRange(16, 32)];
[pack getBytes:pBuff range:NSMakeRange(32, len)];
[(*salt) replaceBytesInRange:NSMakeRange(0, 16) withBytes:sBuff];
[(*iv) replaceBytesInRange:NSMakeRange(0, 16) withBytes:iBuff];
NSMutableData *unpack = [NSMutableData dataWithLength:len];
[unpack replaceBytesInRange:NSMakeRange(0, len) withBytes:pBuff];
free(pBuff);
return unpack;
}
it should be pretty simple to integrate the encryption and decryption from these two methods.
Proof of concept: Can we pack all together? and Unpack again?
NSData *salt = [CryptAES randomDataOfLength:16];
NSData *iv = [CryptAES randomDataOfLength:16];
NSData *chunk = [CryptAES packWithSalt:salt IV:iv Cypher:plaintextData];
NSLog(#"salt=%# iv=%# pack=%# ",[salt base64EncodedStringWithOptions:0], [iv base64EncodedStringWithOptions:0], [chunk base64EncodedStringWithOptions:0] );
NSMutableData *unSalt = [NSMutableData dataWithLength:16];
NSMutableData *unIv = [NSMutableData dataWithLength:16];
NSData *unchunk = [CryptAES cypherUnpackToSalt:&unSalt andIV:&unIv fromPackData:chunk];
NSString *plainAgain = [[NSString alloc] initWithData:unchunk encoding:NSUTF8StringEncoding];
NSLog(#"salt=%# iv=%# unpack=%#",[unSalt base64EncodedStringWithOptions:0], [unIv base64EncodedStringWithOptions:0], plainAgain );
So your decryption method will still need parameters for a password.
Which is not perfect but as you should never throw around encrypted data together with its password - that should be ok - you just keep the handling of a password on the user side. I mean otherwise the whole encryption is useless!
Related
I am working on an encryption method in the iPhone with the RSA encryption method, so far i could achieve getting the encryption string with this method, the string is successfully decrypted by the server.
SecKeyRef keyRef = [self addPublicKey:pubKey];
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA256;
if (!keyRef) {
return nil;
}
BOOL canEncrypt = SecKeyIsAlgorithmSupported(keyRef, kSecKeyOperationTypeEncrypt, algorithm);
if (canEncrypt) {
CFErrorRef error = NULL;
NSData *encryptedData = (NSData *)CFBridgingRelease(
SecKeyCreateEncryptedData(keyRef, algorithm, (__bridge CFDataRef) content, &error)
);
if (encryptedData) {
return encryptedData;
}else{
NSError *err = CFBridgingRelease(error);
NSLog(#"Ocurrió un error %#", err.localizedDescription);
return nil;
}
}
This method works for ios 10 and newer, what i need is to know how to set the algorithm in prior ios versions, my code is the following
SecKeyRef keyRef = [self addPublicKey:pubKey];
if (!keyRef) {
return nil;
}
size_t cipherBufferSize = SecKeyGetBlockSize(keyRef);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0*0, cipherBufferSize);
NSData *plainTextBytes = content;
size_t blockSize = cipherBufferSize - 11;
size_t blockCount = (size_t)ceil([plainTextBytes length] / (double)blockSize);
NSMutableData *encryptedData = [NSMutableData dataWithCapacity:0];
for (int i=0; i<blockCount; i++) {
int bufferSize = (int)MIN(blockSize,[plainTextBytes length] - i * blockSize);
NSData *buffer = [plainTextBytes subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt(keyRef,
kSecPaddingOAEP,
(const uint8_t *)[buffer bytes],
[buffer length],
cipherBuffer,
&cipherBufferSize);
if (status == noErr){
NSData *encryptedBytes = [NSData dataWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
[encryptedData appendData:encryptedBytes];
}else{
if (cipherBuffer) {
free(cipherBuffer);
}
return nil;
}
}
if (cipherBuffer) free(cipherBuffer);
So far i can see that in the version of ios 10 you can set the algorithm with this line
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA256;
my question is, how do i get that algorithm in the early version of ios, the second code i post can't be decrypted.
Thanks for your help
If you are using OAEP padding with SecKeyEncrypt, you can only use kSecPaddingOAEP, which is SHA1. Unfortunately you cannot use OAEP SHA256 with SecKeyEncrypt.
I'm not an expert about this at all, so I've found a wrapper on github called RSA and tried to use it.
What I need to do is simply encrypt a string using my private key.
This is the method used to do the encryption:
- (NSString *)rsaEncryptWithData:(NSData*)data usingPublicKey:(BOOL)yes {
if (yes) {
[self getKeyRefFor:publicTag];
} else {
[self getKeyRefFor:privateTag];
}
}
SecKeyRef key = self.publicKeyRef;
size_t cipherBufferSize = SecKeyGetBlockSize(key);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0*0, cipherBufferSize);
NSData *plainTextBytes = data;
size_t blockSize = cipherBufferSize - 11;
size_t blockCount = (size_t)ceil([plainTextBytes length] / (double)blockSize);
NSMutableData *encryptedData = [NSMutableData dataWithCapacity:0];
for (int i=0; i<blockCount; i++) {
int bufferSize = (int)MIN(blockSize,[plainTextBytes length] - i * blockSize);
NSData *buffer = [plainTextBytes subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt(key,
kSecPaddingPKCS1,
(const uint8_t *)[buffer bytes],
[buffer length],
cipherBuffer,
&cipherBufferSize);
if (status == noErr){
NSData *encryptedBytes = [NSData dataWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
[encryptedData appendData:encryptedBytes];
}else{
if (cipherBuffer) {
free(cipherBuffer);
}
return nil;
}
}
if (cipherBuffer) free(cipherBuffer);
return [encryptedData base64EncodedStringWithOptions:0];
}
If I use my public key to encrypt, everything is working fine, but if I try to use my private key then the OSStatus variable returns a -4 (Function or operation not implemented.).
Any help would be really appreciated, since I really don't know what to do.
Thanks.
I ended up using the MIHCrypto framework to create a key pair and to sign my messages.
Is this easy:
- (NSData *)signMessageData:(NSData *)data withKey:(MIHRSAPrivateKey *)privKey {
NSError *signingError = nil;
NSData *signatureData = [privKey signWithSHA256:data error:&signingError];
if (signingError != nil) {
NSLog(#"Error: %#", signingError.localizedDescription);
}
return signatureData;
}
I have googled a lot but enable to get Blowfish ECB algorithm with PKCS5 padding in Objective-C.
I have tried code from here but it is not giving me proper encrypted data. Even I have tried codes from here but it is not with PKSC5 padding.
Unfortunately I have to use Blowfish (no other option) to convert following JAVA code in Objective-C
String objVal=<the json>;
SecretKeySpec lKeySpec = new SecretKeySpec(lKey.getBytes("UTF8"),"Blowfish");
Cipher lCipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
lCipher.init(Cipher.ENCRYPT_MODE, lKeySpec);
byte[] lPassword = objVal.getBytes("UTF8");
byte[] lEncryptPassword = lCipher.doFinal(lPassword);
String lEncryptString = new BASE64Encoder().encode(lEncryptPassword);
StringBuffer nString = new StringBuffer();
for (int i = 0; i < lEncryptString.length(); i++) {
int a = lEncryptString.charAt(i);
if (a != 13 && a != 10 && !lEncryptString.substring(i, i + 1).equals(" ")){
nString.append(lEncryptString.charAt(i));
}
return nString.toString();
Then the encrypted json is encoded:
String returnData=<encrypted json>
byte[] inputBytes = returnData.getBytes();
returnData = DatatypeConverter.printBase64Binary(inputBytes);
Any one has tried and tested solution for Blowfish ECB algorithm with PKSC5 padding.
Thank you in advance.
I know this question is already (several times) asked but either not answered or not with PKCS5 padding
Example code:
Add Security.framework
#import <CommonCrypto/CommonCryptor.h>
+ (NSData *)doBlowfish:(NSData *)dataIn
context:(CCOperation)kCCEncrypt_or_kCCDecrypt
key:(NSData *)key
options:(CCOptions)options
iv:(NSData *)iv
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeBlowfish];
ccStatus = CCCrypt( kCCEncrypt_or_kCCDecrypt,
kCCAlgorithmBlowfish,
options,
key.bytes,
key.length,
(iv)?nil:iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}
Test:
NSError *error;
NSData *key = [#"Blowfish" dataUsingEncoding:NSUTF8StringEncoding];
NSString *stringOriginal = #"TestData";
NSData *dataOriginal = [stringOriginal dataUsingEncoding:NSUTF8StringEncoding];;
NSLog(#"key %#", key);
NSLog(#"stringOriginal %#", stringOriginal);
NSLog(#"dataOriginal %#", dataOriginal);
NSData *dataEncrypted = [Test doBlowfish:dataOriginal
context:kCCEncrypt
key:key
options:kCCOptionPKCS7Padding | kCCOptionECBMode
iv:nil
error:&error];
NSLog(#"dataEncrypted %#", dataEncrypted);
NSString *encryptedBase64String = [dataEncrypted base64EncodedStringWithOptions:0];
NSLog(#"encryptedBase64String %#", encryptedBase64String);
NSData *dataToDecrypt = [[NSData alloc] initWithBase64EncodedString:encryptedBase64String options:0];
NSData *dataDecrypted = [Test doBlowfish:dataToDecrypt
context:kCCDecrypt
key:key
options:kCCOptionPKCS7Padding | kCCOptionECBMode
iv:nil
error:&error];
NSLog(#"dataDecrypted %#", dataDecrypted);
NSString *stringDecrypted = [[NSString alloc] initWithData:dataDecrypted encoding:NSUTF8StringEncoding];
NSLog(#"stringDecrypted %#", stringDecrypted);
Output:
key 426c6f77 66697368
stringOriginal TestData
dataOriginal 54657374 44617461
dataEncrypted ba5eb956 7e73ae1a b5513ea1 75a14019
encryptedBase64String ul65Vn5zrhrdmeYV9B5rQA==
dataDecrypted 54657374 44617461
stringDecrypted TestData
I'm new to xCode and Objective-C. I've coded a QR-Scanner, now I need to decrypt the Data which is encryptet with AES128 CBC. I encrypt the plaintext using this snippet in php: http://www.androidsnippets.com/encrypt-decrypt-between-android-and-php
Could you help me pls? Sorry for my bad English, I'm German :D
Use CommonCrypto, See CommonCrypto and CCCryptor
Here is a snippit to get you started:
+ (NSData *)doCipher:(NSData *)dataIn
iv:(NSData *)iv
key:(NSData *)symmetricKey
context:(CCOperation)encryptOrDecrypt
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0; // Number of bytes moved to buffer.
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
symmetricKey.bytes,
kCCKeySizeAES128,
iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}
Also see RNCryptor for a full implementation.
Here is the code:
- (NSData *) doCipher: (NSData *) plainData key: (NSData *) symmetricKey context: (CCOperation) encryptOrDecrypt padding: (CCOptions *) pkcs7
{
// Initialization vector; dummy in this case 0's.
uint8_t iv[kChosenCipherBlockSize];
bzero((void *) iv, (size_t) sizeof(iv));
// We don't want to toss padding on if we don't need to
if (encryptOrDecrypt == kCCEncrypt)
{
if (*pkcs7 != kCCOptionECBMode)
{
if ((plainData.length % kChosenCipherBlockSize) == 0)
*pkcs7 = 0x0000;
else
*pkcs7 = kCCOptionPKCS7Padding;
}
}
else if (encryptOrDecrypt == kCCDecrypt)
{
*pkcs7 = 0x0000;
}
else
{
DLog(#"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7);
return nil;
}
// Actually perform the encryption or decryption.
NSMutableData *dataOut = [NSMutableData dataWithLength: plainData.length + kChosenCipherBlockSize];
size_t movedBytes = 0;
CCCryptorStatus ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithmAES128,
*pkcs7,
symmetricKey.bytes,
kChosenCipherKeySize,
iv,
[plainData bytes],
[plainData length],
[dataOut mutableBytes],
[dataOut length],
&movedBytes
);
if (ccStatus == noErr)
{
dataOut.length = movedBytes;
}
else
{
DLog(#"Problem with encipherment ccStatus == %d", ccStatus);
return nil;
}
return dataOut;
}
When I using kCCOptionPKCS7Padding on kCCDecrypt sometimes I get error code 4304.
I try not using padding when kCCDecrypt as described here Anyone else having trouble with iOS 5 encryption?
and I don't get the error. But sometimes the data length after kCCDecrypt is not the same as original data length before kCCEncrypt. I think this is because original data length is not multiplied by encoding block size.
Anyone else have this trouble?
You cannot just toss out padding. Leave padding on and everything will be alright.
See also: Encrypting 16 bytes of UTF8 with SecKeyWrapper breaks (ccStatus == -4304)