How to decrypt Java AES on Nodejs - java

I have the following code on Java that decrypts AES encryption and I need to do the same on Node.js
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(String myKey) {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String decrypt(String strToDecrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
I have tried using Crypt under the following code, but it doesn't give me the same results
var aesDecrypt = (text, password, bit) => {
var decipher = crypto.createDecipheriv('aes-' + bit + '-ecb', password, Buffer.alloc(0));
decipher.setAutoPadding(false);
return Buffer.concat([
decipher.update(text, 'base64'),
decipher.final()
]).toString();
};
How could I mimick that Java code from above into Node.js?

As James says, the Java code is hashing (and truncating) the password to form the key. Also it does use standard padding. The following works for ASCII data:
const crypto = require ('crypto');
const mydecrypt = (pw,ctx) => {
var h = crypto.createHash('sha1'); h.update(pw,'utf8'); var k = h.digest().slice(0,16);
var d = crypto.createDecipheriv('aes-128-ecb', k, Buffer.alloc(0));
return Buffer.concat([d.update(ctx,'base64'), d.final()]) .toString();
}
console.log(mydecrypt('password','ks7qtmk7kt5riV/Qyy3glQ=='));
->
testdata
It may not work for non-ASCII data. Java new String(byte[]) uses a JVM-dependent encoding which may be UTF8 or may be something different depending on your platform, build, and environment, none of which you described. OTOH nodejs Buffer.toString() always uses UTF8. You may need to change it to toString(somethingelse) to match the Java.
If this 'password' is truly a password, i.e. chosen or even remembered by one or more human(s), using a simple hash of it is very weak and will probably be broken if used for anything not utterly trivial; you should use a Password-Based Key Derivation Function designed for the purpose by someone competent, like older (PKCS5) PBKDF2 or newer bcrypt, scrypt, or argon2. However, that's not a programming question and is offtopic here; it has been discussed many times and at length on https://crypto.stackexchange.com and https://security.stackexchange.com .

Related

Nodejs Encrypted string not matching java : AES-256-CBC

Hi I have written nodejs encryption and java encryption by using same algorithm on both the side. But Java and NodeJS are returning different encrypted string. Please help me here.
//Here is my Java Code
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public enum AESUtil {
;
private static final String ENCRYPTION_KEY = "RwcmlVpg";
private static final String ENCRYPTION_IV = "4e5Wa71fYoT7MFEX";
public static String encrypt(String src) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, makeKey(), makeIv());
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(cipher.doFinal(src.getBytes()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
Base64.Decoder decoder = Base64.getDecoder();
decrypted = new String(cipher.doFinal(decoder.decode(src)));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
static AlgorithmParameterSpec makeIv() {
try {
return new IvParameterSpec(ENCRYPTION_IV.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
static Key makeKey() {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] key = md.digest(ENCRYPTION_KEY.getBytes("UTF-8"));
return new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
//Below is the code to test above code
public class AESMain {
/**
* #param args
*/
public static void main(String[] args) {
String src = "Hello,CryptWorld";
String encrypted = AESUtil.encrypt(src);
String decrypted = AESUtil.decrypt(encrypted);
System.out.println("src: " + src);
System.out.println("encrypted: " + encrypted);
System.out.println("decrypted: " + decrypted);
}
}
Response from the above code is
src: Hello,CryptWorld
encrypted: rh7ro9NH1XZeLX95paLETDgYxRbnDoOIrxarO0Sy73s=
decrypted: Hello,CryptWorld
//Node JS Code
var Encrypt, crypto;
crypto = require("crypto");
Encrypt = module.exports = (function() {
var b64dec, b64enc, cipher, decrypt, encrypt, iv, key;
key = crypto.createHash("sha256").update("RwcmlVpg").digest();
iv = '4e5Wa71fYoT7MFEX';
cipher = function(mode, data) {
var encipher, encoded;
encipher = crypto[mode]("aes-256-cbc", key, iv);
encoded = encipher.update(data);
encoded += encipher.final();
return encoded;
};
encrypt = function(data) {
return b64enc(cipher("createCipheriv", data));
};
decrypt = function(data) {
return cipher("createDecipheriv", b64dec(data));
};
b64enc = function(data) {
var b;
b = new Buffer(data, "binary");
return b.toString("base64");
};
b64dec = function(data) {
var b;
b = new Buffer(data, "base64");
return b.toString("binary");
};
return {
encrypt: encrypt,
decrypt: decrypt
};
})();
var expected = Encrypt.encrypt("Hello,CryptWorld");
console.log("expected " + expected);
The Response from Node JS is
expected /R79/f1H/XZeLX95/f39TDgY/Rb9Dv39/Rb9O0T9/Xs=
The node js version is v6.10.1 and JDK version 1.8.0_77.
I really don't know what I am missing.
I'm not a javascript or node.js expert, but I think the problem is that the cipher.update() and cipher.final() are returning instances of Buffer, not string. Therefore you must use Buffer.concat(...) to concatenate them, i.e
cipher = function (mode, data) {
var encipher, encoded;
encipher = crypto[mode]("aes-256-cbc", key, iv);
cipher1 = encipher.update(data);
cipher2 = encipher.final();
return Buffer.concat([cipher1, cipher2]);
};
Additionally, you should never use this String.getBytes() method nor this String(byte[]) constructor in any code that strives to achieve portability or interoperability. Instead, always specify the charset explicitly. I would recommend UTF_8 exclusively, e.g. so use String.getBytes(StandardCharsets.UTF_8) and new String(byte[], StandardCharsets.UTF_8).
My first hint would be to make sure that you exactly the same:
character encoding
line endings
in both of your programs, for the text being encrypted and for the keys. Try printing the text buffers and keys as hex and compare those before you even do any encryption. If they differ then there's your problem. If they're the same then it may be a problem with the encryption itself.
Note that Node uses UTF-8 by default while Java uses UCS-2 internally as far as I know. I see you're making some attempts to convert the encoding but double check the results in both envirements for bot the keys and the clear text just before the encryption step.
Also make sure that you're not encrypting base64 representation of the string instead of the string itself, or that if you use base64 for the key then you do it consistently.
Also, print both keys before the encryption. You're building them programmatically so make sure you know what they are at the end.
If you make sure that you have the same keys, the same messages, the same encoding, line endings and representation (hex, base64 etc.) then use some online tool for AES encryption like:
http://aesencryption.net/
http://aes.online-domain-tools.com/
https://www.browserling.com/tools/aes-encrypt
and compare which of your program does the job correctly.

AES CBC 128 bit encryption using Java and PHP

I have recently used the AES CBC 128 algorithm in Java in order to cipher data. Now I need to rebuild that algorithm in PHP, but I have no idea how, because PHP algorithms on the internet return different results. Maybe you can help me.
This is the Java-code to encrypt:
private SecretKeySpec secretKey;
private IvParameterSpec ivSpec;
public void setKey(String myKey) {
MessageDigest sha = null;
try {
byte[] key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new String("1010101010101010").getBytes("UTF-8");
ivSpec = new IvParameterSpec(iv);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String encrypt(String strToEncrypt) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
return Base64.encode(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String strToDecrypt) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
return new String(cipher.doFinal(Base64.decode(strToDecrypt)));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
AESText aes = new AESText();
final String secretKey = "com.secure.test.projectjasdS/FjkGkGhkGjhG786Vjfg=tjGFGH";
aes.setKey(secretKey);
String originalString = "test set se ts et set s et se";
String encryptedString = aes.encrypt(originalString);
String decryptedString = aes.decrypt(encryptedString);
System.out.println("origin: " + originalString);
System.out.println("encrypted: " + encryptedString);
System.out.println("decrypted: " + decryptedString);
}
This is my php code:
protected $key;
protected $method = 'AES-128-CBC';
protected $iv = '1010101010101010';
protected $option = OPENSSL_CIPHER_AES_128_CBC;
function __construct($key)
{
$this->key = $key;
}
public function encrypt($data) {
if (is_null($data)) {
return "Error " . INVALID_PARAMS_ENCRYPTIONS . ": Data is null ";
}
$enc = openssl_encrypt($data, $this->method, $this->key, $this->option, $this->iv);
return base64_encode($enc);
}
public function decrypt($data) {
if (is_null($data)) {
return "Error " . INVALID_PARAMS_ENCRYPTIONS . ": Data is null ";
}
$data = base64_decode($data);
$dec = openssl_decrypt($data, $this->method, $this->key, $this->option, $this->iv);
return $dec;
}
When I encrypted data from java encryption, This result cannot decrypt on Php decryption.
Can you guys possibly help me with building a PHP script, that returns the same results with java encryption?
At first glance I see three issues here:
First: you are not using the same mode: in java you have AES/ECB/PKCS5Padding whereas your php uses AES-128-CBC.
Second: you probably aren't using the same IV's in the Java and PHP code (IV's are irrelevant for ECB, but once you switch your java to CBC you will need it):
You have $iv = '1010101010101010' (which is then passed to openssl) in your php but nothing like that in your java.
At the very least, you will probably need something like that in your Java part as well:
cipher.init(Cipher.DECRYPT_MODE/ENCRYPT_MODE, secretKey, new IvParameterSpec(iv))
with iv being a byte[] containing your IV bytes.
Third: once the problems above are addressed, padding may be the next breaking thing: your java cipher specification mentions PKCS5Padding. You need to make sure that both of your counterparts use the same.
edit: Fourth: One more issue is the way you derive the key bits to be used. In java you take the first 16 bytes of a sha1-hash, and in php you just pass $key to openssl. openssl might be deriving the encryption key in a different way.
When building cryptography-related tools using block ciphers, it's always nice to revisit classics like Block cipher mode of operation and Padding on Wikipedia, to get a sense of what is going on under the hood.

Encrypt on Windows Phone 8.1 in C# and decrypt in Java

The C# is on the client end while Java code is used in a service. Windows phone encrypts the data while Java decrypts the data using the same symmetric key.
Below is my C# method for encryption
public static string EncryptAesTest(string data, string password)
{
SymmetricKeyAlgorithmProvider SAP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
CryptographicKey AES;
HashAlgorithmProvider HAP = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha512);
Windows.Security.Cryptography.Core.CryptographicHash Hash_AES = HAP.CreateHash();
string encrypted;
try
{
byte[] hash = new byte[16];
Hash_AES.Append(CryptographicBuffer.CreateFromByteArray(System.Convert.FromBase64String(password)));
byte[] temp;
CryptographicBuffer.CopyToByteArray(Hash_AES.GetValueAndReset(), out temp);
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
AES = SAP.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(hash));
IBuffer Buffer = CryptographicBuffer.CreateFromByteArray(Encoding.UTF8.GetBytes(data));
encrypted = CryptographicBuffer.EncodeToBase64String(CryptographicEngine.Encrypt(AES, Buffer, null));
return encrypted;
}
catch
{
return "encryption error";
}
}
Below is my Java class for decryption
private SecretKeySpec secretKey;
public void setKey() {
skey = "mykey";
MessageDigest sha = null;
try {
key = skey.getBytes("UTF-8");
logger.debug("Key length ====> " + key.length);
sha = MessageDigest.getInstance("SHA-512");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String decrypt(String strToDecrypt) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
setDecryptedString(new String(cipher.doFinal(Base64
.decodeBase64(strToDecrypt))));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
The key generation is not complete. For some reason, your C# code uses sets the last byte of the key to the same value as the first byte with the following code:
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
(To my understanding, this could should throw some exception, because you can't copy 16 bytes into the 16 byte array hash if you begin at index 15.)
You could do the same (bad) thing in Java
public void setKey() {
skey = "mykey";
MessageDigest sha = null;
try {
key = skey.getBytes("UTF-8");
logger.debug("Key length ====> " + key.length);
sha = MessageDigest.getInstance("SHA-512");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
key[15] = key[0]; // added
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Things to consider:
ECB mode does not provide semantic security and it should never be used. Use at the very least CBC mode with a random IV for each encryption under the same key.
Passwords should be hashed multiple times. A single hash makes it easy for an attacker to brute-force the password, because this is operation is fast. You should use Password-based Encryption with a strong key derivation function like PBKDF2 (more than 100,000 iterations), scrypt or bcrypt. Don't forget to use a random salt.
Authenticate your ciphertexts. You would want to detect (malicious) manipulations of your ciphertexts in transit. This can be done either with an authenticated mode like GCM or EAX, or with an encrypt-then-MAC scheme by running a MAC algorithm over your ciphertexts. A strong MAC is HMAC-SHA256.

Common Encryption algorithm for Rails and Java

I am going to integrate two web applications written in different platforms (Java and Ruby),
I have to use common encryption algorithm for password in both application.
Is there any common encryption/decryption algorithm for both? If yes, please mention any useful link or any example.
It would be highly appreciated. Thanks in advance
In addition during my digging out it I found,
I have used Base64 with DES in both, interesting thing is that Characters and special characters give me same result in both but as i adding any number like (1,2,3), half of result is same and half encryption is something different.
*Ruby Code
require 'openssl'
require 'base64'
c = OpenSSL::Cipher::Cipher.new("des")
c.encrypt
c.key ="REPPIFY_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
e = c.update("ankit#123")
e << c.final
puts Base64.encode64(e)
Output: Cbe9GslMs8mh33jAOD9qsw==
*Java Code
I am defining only encryption method here:-
public static String encryptPassword(String pass) {
public static final String DESKEY = "REPPIFY_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
System.out.println("Here is my password = "+pass);
DESKeySpec keySpec = null;
SecretKeyFactory keyFactory = null;
SecretKey key = null;
Cipher cipher = null;
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] cleartext = null;
String encrypedPwd = null;
String pass = "ankit#123";
try {
keySpec = new DESKeySpec(DESKEY.getBytes("UTF8"));
keyFactory = SecretKeyFactory.getInstance("DES");
key = keyFactory.generateSecret(keySpec);
if(pass!=null) {
cleartext = pass.getBytes("UTF8");
cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
encrypedPwd = base64encoder.encode(cipher.doFinal(cleartext));
}
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} // cipher is not thread safe
catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
System.out.println("Here I am printing encrypted pwd = "+encrypedPwd);
return encrypedPwd;
}
Output in Java :- Cbe9GslMs8mWn9yTmZrUiw==
Well, in the Ruby world, I'd recommend BCrypt, which is also favored by popular authentication plugins like Devise. I'm not very familiar with Java but a quick search suggests that there's BCrypt implementation in Java too:
http://www.mindrot.org/projects/jBCrypt/
EDIT - BCrypt is 1-way encryption, mainly for use in hashing passwords. If you are looking for something that will encrypt and decrypt then you'll have to look at something else. Seeing as you mentioned it's for passwords I'd suggest you only want 1-way encryption though.
I got the answer...just change a following line in ruby code and then you can use base64 decoder with DES in both:
c = OpenSSL::Cipher::Cipher.new("DES-ECB")
require 'openssl'
require 'base64'
c = OpenSSL::Cipher::Cipher.new("DES-ECB")
c.encrypt
c.key ="REPPIFY_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
e = c.update("ankit#123")
e << c.final
puts Base64.encode64(e)

AES-128 Encryption not working on Java < 1.7

I've been chipping away at a school assignment for 3 days, and finally finished it today, error-free and working fine! Except, I was testing it on Java 1.7, and the school servers (where the professor will compile it) run 1.6. So, I tested my code on 1.6, wanting to cover all my bases, and I get a BadPaddingException upon decryption.
[EDIT] Warning: this code does not follow common security practices and should not be used in production code.
Originally, I had this, which works fine on 1.7 (sorry, lots of code.. all relevant..):
public static String aes128(String key, String data, final int direction) {
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
try {
if (direction == ENCRYPT) {
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
However, my BadPaddingException catch block executes upon decryption:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at CipherUtils.aes128(CipherUtils.java:112)
at CipherUtils.decryptFile(CipherUtils.java:44)
at decryptFile.main(decryptFile.java:21)
This is what I tried to fix it (basically, I added all the padding/unpadding myself, and used NoPadding):
public static String aes128(String key, String data, final int direction) {
// PADCHAR = (char)0x10 as String
while (key.length() % 16 > 0)
key = key + PADCHAR; // Added this loop
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} catch (InvalidAlgorithmParameterException e) {
return null; // Added this catch{}
}
try {
if (direction == ENCRYPT) {
while (data.length() % 16 > 0)
data = data + PADCHAR; // Added this loop
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
When using this, I just get gibberish in and out:
Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)
It is also worth noting that 1.6 and 1.7 produce different encrypted strings.
For example, on 1.7, encrypting xy (including a SHA-1 hash) with key hi produces:
XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=
On 1.6, the same thing produces:
nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=
I didn't expect the assignment to take so long, so my time has run out and it does need to be done tonight. If there is no answer by then, however, I'll just leave a note to my teacher regarding this. It appears to be some issue that was fixed in 1.7... though hopefully can be remedied through the correct addition/fix in my code.
Thanks a ton for everyone's time!
First off:
For almost all systems, encrypting the same plaintext twice should always (i.e. with very very high probability) produce different ciphertext.
The traditional example is that it allows a CPA adversary to distinguish E("attack at dawn") from E("attack at dusk") with only two queries. (There are a handful of systems where you want deterministic encryption, but the right way to do this is "synthetic IV" or cipher modes like CMC and EME.)
Ultimately, the problem is that SecureRandom() is not intended for key derivation.
If the input "key" is a passphrase, you should be using something like PBKDF2 (or scrypt() or bcrypt()).
Additionally, you should be using an explicit charset, e.g. String.getBytes("UTF-8").
If the input "key" is a key, the most common string representation is a hexdump. Java doesn't include an unhexing function, but there are several here.
If the input is a "master key" and you want to derive a subkey, then you should be hashing it with other data. There's not much point if the subkey is always the same.
Additional nitpicks:
Your code is vulnerable to a padding oracle attack; you really should be verifying a MAC before doing anything with the data (or better, using an authenticated encryption mode).
In your second listing, you explicitly reuse the IV. Bad! Assuming CBC mode, the IV used should be unpredictable; SecureRandom is useful here.
I've been looking over and over and I have to agree with NullUserException. The problem is the use of SecureRandom. This means that you never really know what your key is and therefore it is not necessarily ever the same key.
encKey comes from SecureRandom, which is seeded by the key provided. Therefore, if the key is the same, the seed is the same, so the random should be the same...
...unless of course Oracle (or another provider) changes the implementation between versions.
Okay, adding more information that I researched. I think this answer was most helpful.
Get password and cleartext from the user, and convert them to byte arrays.
Generate a secure random salt.
Append the salt to the password and compute its cryptographic hash. Repeat this many times.
Encrypt the cleartext using the resulting hash as the initialization vector and/or secret key.
Save the salt and the resulting ciphertext.
To me, it sounds like SecureRandom is used once to generate a salt but then salt must be saved with the cypher text in order to undo the cyphering process. Additional security comes from repetition and variance of steps (obscurity).
Note: I couldn't find any consensus that these steps are best practices.

Categories