IVParameter Spec Problem in Java - java

I am currently devloping a Windows application using C# and looking at developing a mobile app using Java.
The windows software and the mobile app will work together, the windows app will store information and encrypt certain information before storing it in an online database.
The mobile app will pull the information from the online database and will need to decrypt the encrypted string that is retrieved from the datbase.
The encryption method I am using in C# is below
byte[] clearTextBytes = Encoding.UTF8.GetBytes(encryptionString);
SymmetricAlgorithm rijn = SymmetricAlgorithm.Create();
MemoryStream ms = new MemoryStream();
byte[] rgbIV = Encoding.ASCII.GetBytes("ryojvlzmdalyglrj");
byte[] key = Encoding.ASCII.GetBytes("hcxilkqbbhczfeultgbskdmaunivmfuo");
CryptoStream cs = new CryptoStream(ms, rijn.CreateEncryptor(key, rgbIV), CryptoStreamMode.Write);
cs.Write(clearTextBytes, 0, clearTextBytes.Length);
cs.Close();
return Convert.ToBase64String(ms.ToArray());
The Windows method works fine.
The code I am using in Java is as follows:
KeySpec ks = new DESKeySpec("hcxilkqbbhczfeultgbskdmaunivmfuo".getBytes("UTF-8"));
SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(ks);
String ivString = "ryojvlzmdalyglrj";
byte[] ivByte = ivString.getBytes("UTF-8");
IvParameterSpec iv = new IvParameterSpec(ivByte);
//RC5ParameterSpec iv = new RC5ParameterSpec(ivByte);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] encoded = cipher.doFinal(Base64.decodeBase64("iNtaFme3B/e6DppNSp9QLg=="));
Log.d("Decoded Password", encoded.toString());
As both methods need to encrypt and decrypt the same string together it has to use the same key and IV. The only problem that I am having is in the java method the IVParameterSpec is going into the catch with an error that says IV must be 8 bytes long.
How can I resolve this to ensure that I have the same IV as C#.
Thanks for the help

The problem is that you are encrypting (in C#) with AES (also known as Rjindael), but trying to decrypt in Java with DES. If you change your Java code to use AES then it should all work fine.
DES uses an 8-byte IV because it works on 64-bit blocks. AES uses a 16-byte IV because it works on 128-bit blocks.
You should also make sure you use the same character encoding. In C# you are using ASCII, but in java you're using UTF-8. In your case they will be the same, but you should really fix it now to prevent strange bugs in future. You can change the character set name in Java to "US-ASCII" and it'll work.

You have to use the same algorithm of course. The default algorithm is for .NET is AES, so that is what you should be using on the Java side as well.

Related

How to convert crypto from java to nodejs

I have following code in Java.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(INIT_LENGTH, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
I need to implement it in JavaScript/Node.js
I don't know how to implement it in JavaScript from secretString to key generated by KeyGenerator
from the comment section:
This is my code:
function encodeDesCBC( textToEncode, keyString, ivString ) {
const key = Buffer.from(keyString.substring(0, 8), 'utf8')
const iv = Buffer.from(ivString.substring(0, 8), 'utf8')
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
let c = cipher.update(textToEncode, 'utf8', 'base64')
c += cipher.final('base64')
return base64url.escape(c)
}
My problem is secureRandom and KeyGenerator. I do not know how to implement it in nodejs
I don't know Java, but your usage looks somewhat weak, there are algorithms like PBKDF2 (which is old and discouraged now) and scrypt which do a much better job at turning human passwords into keying material. I'm also not sure where your IV is coming from in your Java code. Exactly replicating the Java code would be somewhat difficult as you'd need to know how your version of Java was implemented, and hence how the bytes passed to setSeed actually get turned into a key.
Node's Crypto module, as far as I can tell, assumes you know how long the keys are supposed to be. In the case of AES 128 in CBC mode, this would be 128 bits (i.e. 16 bytes) for both the key and IV.
Assuming you wanted to use things built into the Crypto module (argon2 would be recommended if you could relax this restriction) then you'd do something like:
const crypto = require('crypto');
const password = 'passw0rd';
const scrypt_salt = crypto.randomBytes(16);
const key = crypto.scryptSync(password, scrypt_salt, 16);
which would leave you with a suitable value in key, then you'd encrypt with:
const plaintext = 'the plain text to encode';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
const ciphertext = Buffer.concat([
cipher.update(plaintext),
cipher.final(),
]);
and you could encode to Base64 with ciphertext.toString('base64').
Note that to be able to decrypt this, you'd need the scrypt_salt, iv, and ciphertext.
When you encrypt something using AES, there is always a "mode" in play. In your Java code you don't specify a mode explicitly - Cipher.getInstance("AES"). When you do not specify a mode, the default mode in Java is "ECB" which is the most insecured mode anyway.
In your NodeJs code, you're using "CBC" mode which is a altogether different mode.
Neither "ECB", nor "CBC" are considered secured enough. As of today, usually, the recommended mode is the GCM mode.
To generate a key from a password, ideally a "key derivation function" should be used. The 4 key derivation functions recommended by OWASP are: PBKDF2, Scrypt, Bcrypt and Argon2.
In your Java code, the password is used as a seed for the pseudo random number generator class SecureRandom. That's a little bit bizarre because even if you give the same password to your function, it will produce different key in different run. Yes, SecureRandom is also used to generate key. But if the requirement is to generate a key from a password, a key derivation function, as mentioned above, shoul be used. Both the approaches are shown in the following StackOverflow answer with detailed explanation. However, it uses "GCM" mode. But as long as you understand the concepts, you can use any mode of your choice.
https://stackoverflow.com/a/53015144/1235935
Similarly, you'll find the same implementation in NodeJs in the following StackOverflow answer:
https://stackoverflow.com/a/53573115/1235935
To further understand AES in general, you may want to go through the following StackOverflow answer:
https://stackoverflow.com/a/43779197/1235935

Java PBEWITHSHA1ANDDESEDE encryption equivalent in node.js?

I have inheritied Java web application and am supposed to convert that to node.js.
Part of that is encryption of data. In Java it is done like in attached code. How would I do that in node using crypto?
I am not strong in cryptography at all, sorry if this is really basic question and thanks in advance.
private final String ALGORITHM = "PBEWITHSHA1ANDDESEDE";
private final int ITERATION_COUNT = 20;
private final byte[] SALT = {
(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};
and later...
PBEKeySpec pbeKeySpec = new PBEKeySpec("password".toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(SALT, ITERATION_COUNT);
// Create PBE Cipher
Cipher pbeCipher = Cipher.getInstance(ALGORITHM);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
byte[] encrypted = pbeCipher.doFinal("text to be encrypted");
Edit:
This is my failed attempt to implement that in node.js based on various searches:
var crypto = require('crypto');
var SALT = new Buffer('c773218c7ec8ee99', 'hex');
var pass = new Buffer('password');
//digest is by default SHA-1 thats what I need
var key = crypto.pbkdf2Sync(pass, SALT, 20, 56); //[, digest]
//var cipher = crypto.createCipher('des-ede-cbc', key);
var cipher = crypto.createCipher('des-ede', key);
//var cipher = crypto.createCipheriv('des-ede-cbc', key, new Buffer('00000000', 'binary'));
cipher.update(new Buffer('This is to be encoded'));
var encrypted = cipher.final('binary');
console.log(encrypted);
fs.writeFileSync('encrypted.file', encrypted);
When I am trying to use crypto.createCipheriv I have no idea what to put there as IV.
When used without IV, it produces some 'encrypted' gibberish however when saved to file, it can't be read and decoded on Java side. Sigh.
The reason that you can't get these to interoperate is the Java side is using PBE and the node.js side is using PBKDF2, which serve similar purposes and come from the same standards document (PKCS #5), but have very different mechanisms under the hood.
Because these are different key generation algorithms, you generate different keys on each end, and thus get different results when you decrypt.
There is also something of an mismatch in the level of abstraction between java and node.js in your code. the Java API you are using is very high level, and uses OpenSSL-like constructs. Meanwhile, the node.js code is at a much lower level, gluing pieces together bit-by-bit. This can cause issues when, for example, the java code introduces a particular padding structure or cipher mode of operation.
If this is just for learning or something non-critical, I would recommend changing the java code to be at the same lower level as the node.js code, and put pieces together one by one: generate the key on both sides and make sure they are the same, ecrypt on both sides and get the same output, etc. If you can't change the java code, use something like forge to get your node.js code at the same higher level as the java code. But as the comments on this question indicate, you may not be able to do PBE anyways.
If this is for something "real" where you actually want the files saved to be secure, call out to an external program like gpg to handle the encryption, instead of "rolling your own" file encryption system.

Encrypting in NetSuite Suitescript and decrypting in java application

I need to encrypt a string using SuiteScript, send it to a web service written in Java, and decrypt it there.
Using SuiteScript I'm able to encrypt and decrypt without any issue. But when I use the same key in java, I get different errors.
var x = "string to be encrypted";
var key = 'EB7CB21AA6FB33D3B1FF14BBE7DB4962';
var encrypted = nlapiEncrypt(x,'aes',key);
var decrypted = nlapiDecrypt(encrypted ,'aes',key);
^^works fine^^
The code in Java
final String strPassPhrase = "EB7CB21AA6FB33D3B1FF14BBE7DB4962"; //min 24 chars
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(new DESedeKeySpec(strPassPhrase.getBytes()));
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, key);
String encrypted = "3764b8140ae470bda73f7ebed3c33b0895f70c3497c85f39043345128a4bc3b3";
String decrypted = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(encrypted)));
System.out.println("Text Decryted : " + decrypted);
With the above code, I get an exception javax.crypto.BadPaddingException: Given final block not properly padded
The key was generated using openssl
openssl enc -aes-128-ecb -k mypassphrase -P
it looks like you are encrypting with AES, and decrypting with DES. I think the ciphertext needs to be decrypted with the same symmetric algorithm that you used to encrypt.
Looks like currently you have to use Suitescript to decrypt messages if it was encrypted using SuiteScript.
See suiteanswers: 35099
The workaround suggested is to use an external javascript library to encrypt/decrypt. We ended up using OpenJS on the javascript, but on the java side had to make sure the defaults were adjusted according to what is setup on the javascript side. The Java APIs were more flexible in this regard than the javascript ones.

Compatible AES algorithm for Java and Javascript

I need to encrypt some values in Java application using AES algorithm, and decrypt the same in my Javascript module of my application.
I saw some examples over the internet but it seems there's some differences in compatibility.
like below issue :
AES encryption in javascript and decrypting in java
Can someone please point me some code examples to solve this issue.
Thanks.
AES is an exactly specified algorithm, so all AES implementations must be "compatible". Having said that, AES is a variable-key-length block-cipher, operating on 128-bit blocks. To actually use this in a piece of software, you have to make a bunch of other choices: how to deal with input consisting of more than 1 block (this is called the "mode"), in some modes you need an initialization vector, you need to deal with input not consisting of an exact number of blocks (padding), how to encode characters into bytes, and how to represent bytes in a context (like a source file) that doesn't support that. All those things need to be compatible.
Below is a tested example. It uses the standard Java crypto functions (and Apache Commons Codec), and JavaScript crypto library crypto-js. Choices are as follows: 128-bit key, cipher-block-chaining mode (which needs an initialization vector), PKCS5/7 padding, UTF-8 for character encoding, Base64 to represent byte arrays.
This piece of Java will output Base64-encoded ciphertext:
String plainText = "Hello, World! This is a Java/Javascript AES test.";
SecretKey key = new SecretKeySpec(
Base64.decodeBase64("u/Gu5posvwDsXUnV5Zaq4g=="), "AES");
AlgorithmParameterSpec iv = new IvParameterSpec(
Base64.decodeBase64("5D9r9ZVzEYYgha93/aUK2w=="));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
System.out.println(Base64.encodeBase64String(cipher.doFinal(
plainText.getBytes("UTF-8"))));
This piece of JavaScript correctly decrypts it:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var encrypted = CryptoJS.enc.Base64.parse('3Q7r1iqtaRuJCo6QHA9/GhkTmbl4VkitV9ZsD3K2VB7LuBNg4enkJUA1cF8cHyovUH2N/jFz3kbq0QsHfPByCg==');
var key = CryptoJS.enc.Base64.parse('u/Gu5posvwDsXUnV5Zaq4g==');
var iv = CryptoJS.enc.Base64.parse('5D9r9ZVzEYYgha93/aUK2w==');
document.write(CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(
{ ciphertext: encrypted },
key,
{ mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: iv, })));
</script>

How to generate the same AES key in Java(Android) as in .Net?

I need to generate an AES key in Java (Android) from salt and password given from .Net WebService. I need to have the same key as the key generated in .net with the same password and salt (using Rfc2898DeriveBytes and AesManaged()).
Here is my code in Android:
char[] passwordAsCharArray = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwordAsCharArray, salt, 1000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
Here is code in .net:
byte[] keyBytes = Encoding.Unicode.GetBytes(key);
Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(key, keyBytes);
AesManaged rijndaelCSP = new AesManaged();
rijndaelCSP.BlockSize = 128;
rijndaelCSP.KeySize = 256;
rijndaelCSP.Key = derivedKey.GetBytes(rijndaelCSP.KeySize / 8);
rijndaelCSP.IV = derivedKey.GetBytes(rijndaelCSP.BlockSize / 8);
ICryptoTransform decryptor = rijndaelCSP.CreateDecryptor();
When I compare both keys they are different. Any ideas how to generate on Android the same key as in .Net? (I know that the key which have been generated in .net is correct).
Number of iterations in .Net is 1000, salt and password are also the same as in Android.
Ok, it turned out that I dont need exactly the same key (as a byte array). I needed this to decrypt a file (in Java) which have been encrypted in .Net - with this key it gaves me Bad Padding Exception so I think the key was different and that causes the problem, but all I needed to do was to generate IV like a key - that solved my problem. Thanks for response!
It looks like you used the "key" (which should be a password) as a salt in your .NET code, whereas the Java part uses a specified salt. Furthermore, you specified the Unicode character set for decoding your salt, which is weird, the salt should be a random octet string (== byte array) from the beginning.
I would recommend you transform your password and random salt to byte arrays first, compare both using a hexadecimal representation (on your console, or in your debugger) and only then use those as input parameters for the PBKDF2 function in each. I would recommend an UTF-8 encoding for your password.
Always specify all parameters in cryptography, try not to use default, e.g. for the iteration count. If your input is off by a single bit, the output will be completely incorrect, and there is no way to tell which parameter was responsible.
It looks like the Java and .NET PBKDF2 "primitive" is identical on both platforms, there is working code out on the internet.

Categories