0

On Android I have no problem encrypting a message and getting the iv.

String Test = "Lorem ipsum dolor sit amet, ...";
String password = "test";

KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(password.getBytes("UTF8"));
kgen.init(256, sr);
SecretKey skey = kgen.generateKey();

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skeySpec = new SecretKeySpec(skey.getEncoded(), "AES");
c.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] decrypted = c.doFinal(Test.getBytes());
decrypted = Base64.encodeBase64(decrypted);
byte[] iv = Base64.encodeBase64(c.getIV());
Log.d("encryptString", new String(decrypted));
Log.d("encryptString iv", new String(iv));

Output example:

encryptString: 2NVoJzMkPphwUJc2h/4LfsmAwyJlejbWKGLG2ACNbaI=
encryptString iv: YX5SF+cFwzv1I4OiGrJk3A==

When I move over to the JavaScript side I first convert the base64 encoding to bytes. Then I run it through the CryptoJS AES Decrypt function.

var decrypt = CryptoJS.enc.Base64.parse("2NVoJzMkPphwUJc2h/4LfsmAwyJlejbWKGLG2ACNbaI=");
var iv = CryptoJS.enc.Base64.parse("YX5SF+cFwzv1I4OiGrJk3A==");
var password = "test";

var encrypted = CryptoJS.AES.decrypt(decrypt.toString(), password, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

Output is always empty. Is there something else I am missing on Android that I also need to pass to CryptoJS?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Tangela
  • 161
  • 2
  • 13
  • If you're using only symmetric encryption you need the exact same key at the server and the client. If you send the encryption key from the server to the client or the other way around you need to encrypt your symmetric encryption key. The easiest way to do this would be to use TLS. If you use TLS, then the data as well as key are encrypted, so you don't need to encrypt it yourself. This doesn't provide any security, just a little bit of obfuscation. You should read: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/ – Artjom B. Jul 11 '17 at 18:12
  • Never use SHA1PRNG or other `SecureRandom` implementations to derive a key from a password. It will not always work, because the password that the PRNG is seeded with might not be the sole source of randomness. Please use PBKDF2 instead to derive a key from a password. For reference: https://stackoverflow.com/a/20134336/1816580 – Artjom B. Jul 11 '17 at 18:17
  • 1
    The key is derived in a completely different way. CryptoJS uses EVP_BytesToKey and Android uses *something* that changes with API version. Just use the same procedure that is supported by both (i.e. PBKDF2). – Artjom B. Jul 11 '17 at 18:19

1 Answers1

4

Found the problem and it was not an padding issue.

As other people have stated it have to do with the fact I was using SecureRandom.getInstance("SHA1PRNG").

I corrected the problem in my code was generating a key with PBEKeySpec on Android and PBKDF2 on CryptoJS. Then just following the same steps:

String Test = "Lorem ipsum dolor sit amet, ...";
String password = "test";

byte[] salt = new String("12345678").getBytes("Utf8");
int iterationCount = 2048;
int keyStrength = 256;

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyStrength);
SecretKey tmp = factory.generateSecret(spec);

Log.d("encryptString Key: ", new String(Base64.encodeBase64(tmp.getEncoded())));

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, tmp);
byte[] decrypted = c.doFinal(Test.getBytes());
decrypted = Base64.encodeBase64(decrypted);
byte[] iv = c.getIV();

Log.d("encryptString: ", new String(decrypted));
Log.d("encryptString iv:", new String(Base64.encodeBase64(iv)));

Example output from run code on Android:

encryptString Key:: ueTU6u4PXbm86zy+UtlQfeh55xZorA58W3fKKBypheM=
encryptString:: ii8UNoi4xG1zGC8RyzHKu6JMkxixkK7LTPxGMaCHGNk=
encryptString iv:: nwy2VHctPnXOd/rahPFiWg==

Now we generate the same PBKDF2 key in a JavaScript and input the output above into our sample code below:

var salt = CryptoJS.enc.Utf8.parse("12345678");
var password = "test";
var keyBits = CryptoJS.PBKDF2(password, salt, {
  hasher: CryptoJS.algo.SHA1,
  keySize: 8,
  iterations: 2048
});

var iv = CryptoJS.enc.Base64.parse("nwy2VHctPnXOd/rahPFiWg==");
var message = CryptoJS.enc.Base64.parse("ii8UNoi4xG1zGC8RyzHKu6JMkxixkK7LTPxGMaCHGNk=");

var encrypted = CryptoJS.AES.decrypt("ii8UNoi4xG1zGC8RyzHKu6JMkxixkK7LTPxGMaCHGNk=", keyBits, {
  iv: iv,
  padding: CryptoJS.pad.Pkcs7,
  mode: CryptoJS.mode.CBC
});

console.log(encrypted.toString(CryptoJS.enc.Utf8));
<!doctype html>

<html>

<head>
</head>

<body>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/pbkdf2.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/aes.min.js"></script>
</body>

</html>

Output in JavaScript:

"Lorem ipsum dolor sit amet, ..."
Tangela
  • 161
  • 2
  • 13
  • @ Tangela wiz, I am getting error at hasher: CryptoJS.algo.SHA1, – KCN Sep 26 '17 at 09:59
  • can you post the same for android decryption and javascript encryption – KCN Sep 27 '17 at 04:28
  • Update : Base64.encodeBase64(iv) change to Base64.encode(iv,Base64.DEFAULT) Base64.encodeBase64(decrypted); change to Base64.encode(decrypted,Base64.DEFAULT); – comeback4you Nov 27 '17 at 08:38