AES Encryption not the same result iOS and Android - ios

I've been struggling for many days now finding out how to achieve a same result when encrypting plaintext password using AES.
I have codes below in Java (Android) and Objective-C (iOS). Honestly, the ideal result is the one produced by Java because our existing passwords are encrypted with this implementation.
Hopefully someone can pin-point my error for Objective-C. I am a super newbie on this programming language, so please bear with and don't be so hard on me. :)
Many thanks!
The plain text password is:
12345abc
The Java code:
Encrypted text result is:
BS/WyqmZ6AD68YbmFERn9w==
private static String encryptionKey="ERVwiiYMFlDcZ0wp";
private static String decryptionKey="ERVwiiYMFlDcZ0wp";
JSONObject resultJSON=new JSONObject();
private static byte[] initializationVector = new byte[] { 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10 };
public JSONObject encryptMethod(JSONObject jObject)
{
try {
textToEncrypt = jObject.getString("text");
textToEncrypt = replaceSlashes(textToEncrypt);
Log.d("STATE", "textToEncrypt: "+textToEncrypt);
try {
cipherText = encryptData(textToEncrypt);
Log.d("STATE", "cipherText: "+cipherText);
resultType=true;
resultData=cipherText;
resultMessage="Encryption success";
}
catch(Exception e)
{
e.printStackTrace();
resultMessage="Encryption is Not Possible, Please try some other text";
}
}
catch(Exception e)
{
e.printStackTrace();
resultMessage="Wrong Text Input, Please try again";
}
try {
resultJSON.put("type", resultType);
resultJSON.put("message", resultMessage);
resultJSON.put("data", resultData);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return resultJSON;
}
public static String encryptData(String plainText) {
String cipherText = null;
byte[] encryptedBytes = null;
String skeySpecString = null;
if (plainText == null || "".equals(plainText)) {
return null;
}
try {
if(encryptionKey==null || encryptionKey.equals("")) {
return null;
}
SecretKeySpec skeySpec = new SecretKeySpec(encryptionKey.getBytes(), "AES");
IvParameterSpec ivps = new IvParameterSpec(initializationVector);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivps);
encryptedBytes = cipher.doFinal(plainText.getBytes());
encryptedBytes = Base64.encode(encryptedBytes, 0);
cipherText = new String(encryptedBytes);
} catch (BadPaddingException bpe) {
Log.e("Plain", "<===== BadPaddingException =====>", bpe);
return null;
} catch (IllegalBlockSizeException ibse) {
Log.e("Plain", "<===== BadPaddingException =====>", ibse);
return null;
} catch (InvalidAlgorithmParameterException iape) {
Log.e("Plain", "<===== InvalidAlgorithmParameterException =====>",
iape);
return null;
} catch (InvalidKeyException ike) {
Log.e("Plain", "<===== InvalidKeyException =====>", ike);
return null;
} catch (NoSuchAlgorithmException nae) {
Log.e("Plain", "<===== NoSuchAlgorithmException =====>", nae);
return null;
} catch (NoSuchPaddingException nspe) {
Log.e("Plain", "<===== NoSuchPaddingException =====>", nspe);
return null;
} catch (Exception ex) {
Log.e("Plain", "<===== Exception: {0} =====>", ex);
return null;
}
return cipherText;
}
The Objective-C code:
Encrypted text result is:
V+RZjcXVE+J1aG9kwt7CTA==
Encrypt.m file
-(NSString *) encryptData:(NSString *)plainText
{
NSString *passphrase = #"ERVwiiYMFlDcZ0wp";
NSData *key=[passphrase dataUsingEncoding:NSUTF8StringEncoding];
NSData *password = [plainText dataUsingEncoding:NSUTF8StringEncoding];
StringEncryption *crypt = [[StringEncryption alloc] init];
NSData *encryptedData = [crypt AES256EncryptWithKey:passphrase password:plainText];
NSString *ciphertext =[encryptedData base64EncodingWithLineLength:0];
return ciphertext;
}
Crypt.m file
- (NSData *)AES256EncryptWithKey:(NSString *)key password:(NSString *)plainText {
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [plainText length];
NSData *myData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
const char myByteArray[] = {
0x01,0x02,0x03,0x04,
0x05,0x06,0x07,0x08,
0x09,0x0a,0x0b,0x0c,
0x0d,0x0e,0x0f,0x10 };
NSData *vector = [NSData dataWithBytes: myByteArray length:16];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
(__bridge const void *)(vector),
myData.bytes, dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer);
return nil;
}

Related

Objective C - Encryption with AES256 GCM

Currently I'm using the below method for Encryption
+ (NSData*) encrypt:(NSData*)plainText key:(NSData*)key iv:(NSData*)iv {
NSUInteger dataLength = [plainText length];
size_t buffSize = dataLength + kCCBlockSizeAES128;
void *buff = malloc(buffSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
key.bytes, kCCKeySizeAES256, /* key and its length */
iv.bytes, /* initialization vector - use random IV everytime */
[plainText bytes], [plainText length], /* input */
buff, buffSize,/* data RETURNED */
&numBytesEncrypted);
if (status == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
} else {
// Encryption Failed
}
free(buff);
return nil;
}
I need to use now AES256 GCM for encrypting, I don't see kCCAlgorithmAES256 in CCCrypt. Someone please guide me, I'm using the framework pod 'RNCryptor-objc'.
Below is the decrypt logic handled in backend.
public static byte[] decrypt(byte[] key, byte[] data) {
// TODO utilize GCMAES#decrypt method
try {
if (data.length < NONCE_LENGTH + TAG_LENGTH) {
throw new IllegalArgumentException("data packet too short");
}
int cipherTextLength = data.length - NONCE_LENGTH - TAG_LENGTH;
byte[] nonce = Arrays.copyOf(data, NONCE_LENGTH);
GCMBlockCipher cipher = new GCMBlockCipher(new AESFastEngine());
AEADParameters parameters = new AEADParameters(new KeyParameter(key), TAG_LENGTH * 8, nonce);
cipher.init(false, parameters);
byte[] out = new byte[cipher.getOutputSize(cipherTextLength + TAG_LENGTH)];
int pos = cipher.processBytes(data, NONCE_LENGTH, data.length - NONCE_LENGTH, out, 0);
pos += cipher.doFinal(out, pos);
return Arrays.copyOf(out, pos);
} catch (IllegalStateException | InvalidCipherTextException ex) {
throw new IllegalArgumentException(ex);
}}}

AES/CBC/PKCS5Padding in iOS objective c result differs from Android

I am using the AES/CBC/PKCS5Padding in Android application. Code is like-
private static String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static String ALGORITHM = "AES";
private static String DIGEST = "MD5";
private static Cipher cipher;
private static SecretKey password;
private static IvParameterSpec IVParamSpec;
private final static String pvtkey="GDNBCGDRFSC$%#%=";
//16-byte private key
private static byte[] IV = pvtkey.getBytes();
public PassWordEncryptor() {
try {
//Encode digest
MessageDigest digest;
digest = MessageDigest.getInstance(DIGEST);
password = new SecretKeySpec(digest.digest(pvtkey.getBytes()), ALGORITHM);
//Initialize objects
cipher = Cipher.getInstance(TRANSFORMATION);
IVParamSpec = new IvParameterSpec(IV);
} catch (NoSuchAlgorithmException e) {
Log.i(Lams4gApp.TAG, "No such algorithm " + ALGORITHM);
} catch (NoSuchPaddingException e) {
System.out.println( "No such padding PKCS7"+ e);
}
}
/**
Encryptor.
#text String to be encrypted
#return Base64 encrypted text
*/
public String encrypt(byte[] text) {
byte[] encryptedData;
try {
cipher.init(Cipher.ENCRYPT_MODE, password, IVParamSpec);
encryptedData = cipher.doFinal(text);
} catch (InvalidKeyException e) {
System.out.println( "Invalid key (invalid encoding, wrong length, uninitialized, etc)."+ e);
return null;
} catch (InvalidAlgorithmParameterException e) {
System.out.println( "Invalid or inappropriate algorithm parameters for " + ALGORITHM+ e);
return null;
} catch (IllegalBlockSizeException e) {
System.out.println( "The length of data provided to a block cipher is incorrect"+ e);
return null;
} catch (BadPaddingException e) {
System.out.println( "The input data but the data is not padded properly."+ e);
return null;
}
return Base64.encodeToString(encryptedData,Base64.DEFAULT);
}
I need similar code in iOS Objective C. Encryption and Decryption results should be same in android and iOS.
Kindly provide the same algorithm for Objective C.
I am using iOS code as-
- (void)viewDidLoad {
[super viewDidLoad];
NSData *encodingData=[self encrypt:[#"slapkh"
dataUsingEncoding:NSUTF8StringEncoding]];
NSString *encodingResult = [NSString base64StringFromData:encodingData length:[encodingData length]];
}
- (NSData *) encrypt:(NSData *) plainText {
return [self transform:kCCEncrypt data:plainText];
}
- (NSData *) decrypt:(NSData *) cipherText {
return [self transform:kCCDecrypt data:cipherText];
}
- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {
Cipher* cipher = [[Cipher alloc]initWithKey:#"GDNBCGDRFSC$%#%="];
NSString* Key = cipher.cipherKey;
// kCCKeySizeAES128 = 16 bytes
// CC_MD5_DIGEST_LENGTH = 16 bytes
NSData* secretKey = [Cipher md5:Key];
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
uint8_t iv[kCCBlockSizeAES128];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
status = CCCryptorCreate(encryptOrDecrypt,
kCCAlgorithmAES128,kCCOptionPKCS7Padding,
[secretKey bytes], kCCKeySizeAES128, iv, &cryptor);
if (status != kCCSuccess) {
return nil;
}
size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length],
true);
void * buf = malloc(bufsize * sizeof(uint8_t));
memset(buf, 0x0, bufsize);
size_t bufused = 0;
size_t bytesTotal = 0;
status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
buf, bufsize, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
CCCryptorRelease(cryptor);
return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}
But the results for Android and iOS Differs as-
Text to encrypt: slapkh
key: GDNBCGDRFSC$%#%=
Android result: jN2p1yAdBJLRmoHq+k9KtA==\n
iOS Resut: tbaSJFv5mGyZ9t/+kOw+gg==
After spending time dealing with this, I got success in ANDROID(java) and IOS (Objc) using AES with the codes below:
ANDROID CODE
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class SecurityUtils {
private static final String ALGORITHM = "AES";
private static final String MODE = "AES";
private static final String IV = "AEE0715D0778A4E4";
private static final String KEY= "9336365521W5F092BB5909E8E033BC69";
public static String encrypt(String value ) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
byte[] values = cipher.doFinal(value.getBytes());
return Base64.encodeBytes(values);
}
public static String decrypt(String value) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
byte[] values = Base64.decode(value);
SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
return new String(cipher.doFinal(values));
}
}
TESTING ANDROID
try {
String encrypted = SecurityUtils.encrypt("My Secret Text");
String decrypted = SecurityUtils.decrypt(encrypted);
Log.e("encrypted", encrypted);
Log.e("decrypted", decrypted);
}catch(Exception ex){
Log.e("AES", ex.getMessage());
}
IOS CODE
Header file
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>
NS_ASSUME_NONNULL_BEGIN
#interface SecurityUtils : NSObject
+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error;
+ (NSString *)decrypt:(NSString *)plainText error:(NSError **)error;
#end
NS_ASSUME_NONNULL_END
Implementation file
NSString *const IV = #"AEE0515D0B08A4E4";
NSString *const KEY = #"9336565521E5F082BB5929E8E033BC69";
#import "SecurityUtils.h"
#implementation SecurityUtils
+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error {
NSMutableData *result = [SecurityUtils doAES:[plainText dataUsingEncoding:NSUTF8StringEncoding] context: kCCEncrypt error:error];
return [result base64EncodedStringWithOptions:0];
}
+ (NSString *)decrypt:(NSString *)encryptedBase64String error:(NSError **)error {
NSData *dataToDecrypt = [[NSData alloc] initWithBase64EncodedString:encryptedBase64String options:0];
NSMutableData *result = [SecurityUtils doAES:dataToDecrypt context: kCCDecrypt error:error];
return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
+ (NSMutableData *)doAES:(NSData *)dataIn context:(CCOperation)kCCEncrypt_or_kCCDecrypt error:(NSError **)error {
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeBlowfish];
NSData *key =[KEY dataUsingEncoding:NSUTF8StringEncoding];
NSData *iv = [IV dataUsingEncoding:NSUTF8StringEncoding];
ccStatus = CCCrypt( kCCEncrypt_or_kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
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;
}
#end
IOS TESTING
NSError *error;
NSString *encrypted = [SecurityUtils encrypt:#"My Secret Text" error:&error];
NSLog(#"encrypted: %#",encrypted);
NSLog(#"decrypted: %#",[SecurityUtils decrypt:encrypted error:&error]);
Finally, the outputs of the test:
ANDROID OUTPUT
2019-05-16 21:35:01.215 4920-4920/br.com.my.app E/encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:35:01.215 4920-4920/br.com.my.app E/decrypted: My Secret Text
IOS OUTPUT
2019-05-16 21:38:02.947043-0300 MyApp[63392:1590665] encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:38:02.947270-0300 MyApp[63392:1590665] decrypted: My Secret Text
My repo on GitHub with this example.

how to generate hash key by predefine phrase and salt value in IOS?

I have some .net code to generate hash key for encryption, i want to that code in iOS but i can't found appropriate solution, If anyone have the proper solution please help me
i add the my .net code and it is working fine, i want to convert that code in iOS with same result
Public Shared Function Encrypt(ByVal plainText As String) As String
Dim passPhrase As String = "passPhrase"
Dim saltValue As String = "saltValue"
Dim hashAlgorithm As String = "SHA256"
Dim passwordIterations As Integer = 2
Dim initVector As String = "abc123def456gh78"
Dim keySize As Integer = 256
Dim initVectorBytes As Byte() = Encoding.ASCII.GetBytes(initVector)
Dim saltValueBytes As Byte() = Encoding.ASCII.GetBytes(saltValue)
Dim plainTextBytes As Byte() = Encoding.UTF8.GetBytes(plainText)
Dim password As New PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations)
Dim keyBytes As Byte() = password.GetBytes(keySize \ 8)
End Function
I have also facing same issue like this and i found the solution may be it will help full for you.
Get FBEncryptorAES library from Github.
Define IV and Key as per your .net algorithms.
Use this method for encrypt and decrypt your text
+ (NSData*)encryptData:(NSData*)data
{
NSData* result = nil;
// 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
{
NSData* result = nil;
// 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;
}

DES encrypted value mismatching android and ios

IOS code is
#import "DESCodec.h"
#import <CommonCrypto/CommonCryptor.h>
#implementation DESCodec
{
NSString *key;
}
-(id) init{
self=[super init];
if(self){
key=#"12345678";
}
return self;
}
-(NSString *) decode:(NSString *)encoded{
NSData *inputData = [[NSData alloc] initWithBase64EncodedString:encoded options:0];
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
size_t outLength;
NSMutableData *outputData = [NSMutableData dataWithLength:(inputData.length +
kCCBlockSizeDES)];
CCCryptorStatus
result = CCCrypt(kCCDecrypt, // operation
kCCAlgorithmDES, // Algorithm
kCCOptionPKCS7Padding , // options
keyData.bytes, // key
keyData.length, // keylength
nil,// iv
inputData.bytes, // dataIn
inputData.length, // dataInLength,
outputData.mutableBytes, // dataOut
outputData.length, // dataOutAvailable
&outLength); // dataOutMoved
if (result != kCCSuccess) {
return nil;
}
[outputData setLength:outLength];
return [[NSString alloc] initWithData:outputData `encoding:NSUTF8StringEncoding];`
}
-(NSString *) encode:(NSString *)decoded{
NSData *inputData = [decoded dataUsingEncoding:NSUTF8StringEncoding];
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
size_t outLength;
NSMutableData *outputData = [NSMutableData dataWithLength:(inputData.length + kCCBlockSizeDES)];
CCCryptorStatus result = CCCrypt(kCCEncrypt, // operation
kCCAlgorithmDES, // Algorithm
kCCOptionPKCS7Padding, // options
keyData.bytes, // key
keyData.length, // keylength
nil,// iv
inputData.bytes, // dataIn
inputData.length, // dataInLength,
outputData.mutableBytes, // dataOut
outputData.length, // dataOutAvailable
&outLength); // dataOutMoved
if (result != kCCSuccess) {
return nil;
}
[outputData setLength:outLength];
NSString *r = [outputData base64EncodedStringWithOptions:0];
return r;
}
#end
DESCodec *codec=[[DESCodec alloc] init];
NSString *encoded=[codec encode:#"12345678"];
NSString decoded=[codec decode:encoded];
NSLog(#" %# %#",encoded,decoded);
value is ltACiHjVjImOJQ1fTHZkSw== and 12345678
but in java encypted text is "ltACiHjVjIn+uVm31GQvyw=="
I not good in Objective C and I can't able to trigger out the problem.
can anybody please help me. Thanks and regards
Java code is
public class DESCodec {
/**
* Secret key that shall be used for encryption and decryption.
*/
private String strSecretKey = "12345678";
private static final String UNICODE_FORMAT = "UTF-8";
private static final String DES_ENCRYPTION_SCHEME = "DES";
private static final String TAG = "DESCodec";
private Cipher cipher;
private SecretKey key;
public DESCodec() {
try {
this.strSecretKey = strSecretKey;
String myEncryptionScheme = DES_ENCRYPTION_SCHEME;
byte[] keyAsBytes = strSecretKey.getBytes(UNICODE_FORMAT);
DESKeySpec myKeySpec = new DESKeySpec(keyAsBytes);
SecretKeyFactory mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = mySecretKeyFactory.generateSecret(myKeySpec);
} catch (Exception e) {
e.printStackTrace();
}
}
public String desEncrypt(String message) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = message.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = Base64.encodeToString(encryptedText, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String desDecrypt(String message) {
String decryptedText = null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decode(message, Base64.DEFAULT);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText = bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
private String bytes2String(byte[] bytes) {
try {
return new String(bytes, UNICODE_FORMAT);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
It's obviously only a problem with the mode of operation, because the first block matches. In Java you're using ECB mode, because "DES" defaults to "DES/ECB/PKCS5Padding". I think that CCCryptor defaults to CBC.
Don't ever use ECB mode. It's not semantically secure. You need to use at least CBC mode with a random IV. The IV doesn't have to be secret, so you can prepend it to the ciphertext. Please look at RNCryptor that has additional security features like authentication of ciphertext. It also has a Java implementation.
Don't use DES anymore. It's not secure anymore. You should use AES. Triple DES is also not that bad.
I hava the same problem when i develop an iOS app.And the android client is used by many people, so i couldn't change the algorithm to AES or others.As Artjom B. said in Java 'DES' defaults to 'DES/ECB/PKCS5Padding', in the source you can find that
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
but unfortunately in iOS you just find that
enum {
kCCOptionPKCS7Padding = 0x0001,
kCCOptionECBMode = 0x0002
}
But finally i find a solution like this
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
[key UTF8String],
kCCKeySizeDES,
nil,
[cipherData bytes],
[cipherData length],
buffer,
1024,
&numBytesDecrypted);
The importance is kCCOptionPKCS7Padding | kCCOptionECBMode, you can try this method.

Objective C encryption code not working like Android AES 256, md5 encryption

I am encrypting a text to send server using "AES256/PKCS5Padding/ECB" encryption. The following code for Android works well so that the encrypted data is properly decrypted in NodeJS server.
public static String encryptAES_Java_Node(String content, String key) {
 
              byte[] input;
              String query = null;
              try {
                     input = content.getBytes("utf-8");
 
                     MessageDigest md = MessageDigest.getInstance("MD5");
                     byte[] thedigest = md.digest(key.getBytes("UTF-8"));
                     SecretKeySpec skc = new SecretKeySpec(thedigest,
                                  "AES/ECB/PKCS5Padding");
                     Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                     cipher.init(Cipher.ENCRYPT_MODE, skc);
 
                     byte[] cipherText = newbyte[cipher.getOutputSize(input.length)];
                     int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
                     ctLength += cipher.doFinal(cipherText, ctLength);
 
                     query = Base64.encodeToString(cipherText, Base64.DEFAULT);
                    
              } catch (UnsupportedEncodingException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (NoSuchAlgorithmException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (NoSuchPaddingException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (InvalidKeyException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (IllegalBlockSizeException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (ShortBufferException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              } catch (BadPaddingException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
              return query;
 
       }
Here is my code in ObjC
(NSString *)encryptText:(NSString *)rawText withKey:(NSString *)key {
// '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)
NSData *rawData = [rawText dataUsingEncoding:NSUTF8StringEncoding];
//Convert to hash
NSString *md5Key = [self MD5String:key];
// fetch key data
[md5Key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [rawData 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, kCCOptionPKCS7Padding + kCCOptionECBMode,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[rawData bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
NSData *tempData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
NSString* encrypted64 = [tempData base64EncodedStringWithOptions:0];//Even i have tried base 64 encding with other options available
return encrypted64;
}
free(buffer); //free the buffer;
return nil;
}
Am I making a mistake while converting key to MD5 as done in android?
Adding code for decryption in NodeJS service for reference.
var decipher = crypto.createDecipher('aes-128-ecb', encryption_key);
chunks = []
chunks.push( decipher.update( new Buffer(fullBuffer, "base64").toString("binary")) );
chunks.push( decipher.final('binary') );
var txt = chunks.join("");
txt = new Buffer(txt, "binary").toString("utf-8");
// where encryption_key = key, fullBuffer is the input and txt is output
MD5 produces a 16-byte result.
The Java will see this key and use AES128 which uses a 16-byte key. It would be better to explicitly specify key size in the Java version by using: "AES/ECB/NoPadding (128)".
The iOS version is explicitly specifying AES256 which uses a 32-byte key and will null pad the 16-byts to 32-bytes.
In the iOS version change the encryption to AES16: kCCKeySizeAES128.
There are security issues:
Using MD5 to create a key from a string is not secure, the current practice is to use a key derivation function such as PBKDF2 where the iteration count and random salt are passed pre-pended to the encrypted data.
ECB mode is not very secure, it is much better to use CBC mode with a random iv and pre-pend the iv to the encrypted data.
But if security is not important MD5 and ECB are fine.

Resources