I need to encrypt a value in Java using AES CBC 256 but I am unable to decrypt it in NodeJS:
The cyphered string is generated with the following code:
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String result = DatatypeConverter.printHexBinary(cipher.doFinal(test));
and decrypted with this one:
var crypto = require('crypto');
var mykey = crypto.createDecipher('aes-256-cbc', key);
var mystr = mykey.update(value, 'hex', 'utf8')
but the code returns an error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
at Decipher.final (internal/crypto/cipher.js:104:26)
at evalmachine.<anonymous>:12:16
at Script.runInContext (vm.js:74:29)
at Object.runInContext (vm.js:182:6)
at evaluate (/run_dir/repl.js:133:14)
at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
at ReadStream.emit (events.js:180:13)
at addChunk (_stream_readable.js:274:12)
at readableAddChunk (_stream_readable.js:261:11)
at ReadStream.Readable.push (_stream_readable.js:218:10)
I assume it has something to do with the padding strategy but I can not find any documentation on the one applied by NodeJS.
What is the equivalent padding in Java Cypher corresponging to the one provided by the cripto package in NodeJS?
CBC mode requires an Initialization Vector (IV) to be pressent when encryptin the first block. In the Java implementation of AES you can provide this when you Initialize the Cipher, or you can let Java generate one for you. In your case Java have genereted an IV for you. As this is needed when you decrypt, you need to grab this and pass it along with the encrypted data (this ought to be random, but don't need to be kept secret). Use the Cipher.getIV() methode to get it.
In NodeJS there is a variant of the crypto.createDecipher() where you can provide this IV called crypto.createDecipheriv()
Related
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.
I'm using AES GCM authentication in my android project and it works fine. But getting some issues with authentication tag when it compare with openssl API generate tag. Please find the java code below:
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
byte[] iv = generateRandomIV();
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
int outputLength = cipher.getOutputSize(data.length); // Prepare output buffer
byte[] output = new byte[outputLength];
int outputOffset = cipher.update(data, 0, data.length, output, 0);// Produce cipher text
outputOffset += cipher.doFinal(output, outputOffset);
I am using openssl for the same in iOS and generating authentication tag using below code
NSMutableData* tag = [NSMutableData dataWithLength:tagSize];
EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, [tag length], [tag mutableBytes])
In java or bouncy castle, not able to get the exact authentication tag which openssl return and can you help me to resolve this issue. Thanks
In Java the tag is unfortunately added at the end of the ciphertext. You can configure the size (in bits, using a multiple of 8) using GCMParameterSpec - it defaults to the full size of 128 bits otherwise. You can therefore grab the tag using Arrays.copyOfRange(ciphertext, ciphertext.length - (tagSize / Byte.SIZE), ciphertext.length) if you really want to.
It is unfortunate since the tag does not have to be put at the end, and it messes up the online nature of GCM decryption - requiring internal buffering instead of being able to directly return the plaintext. On the other hand, the tag is automatically verified during decryption.
I have a legacy code in ruby that does the encryption using OpenSSL
However, I would like to translate this in Java and I am lost.
so far my biggest blocker is figuring out how to generate the IV based on this code.
Any help would be greatly appreciated
def func_enc(data, key)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.encrypt
cipher.pkcs5_keyivgen(key)
cipher.update(data)
encrypted_data << cipher.final
return encryptedData
end
EDIT
Just to clarify, I would like to use Java Crypto for this. This is the code I came up with so far:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithMD5And256AES-CBC");
KeySpec spec = new PBEKeySpec("Password".toCharArray(), null, 2048, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
but "PBKDF2WithMD5And256AES-CBC" does not have any provider and I get NoSuchAlgorithm exception.
java.security.NoSuchAlgorithmException: PBKDF2WithMD5And256AES-CBC SecretKeyFactory not available
Also the salt that pkcs5_keyivgen uses by default is null!! I am not sure if Java lets me use a null salt.
How can I generate the correct IV ?
The warning on this documentation page suggests that the deprecated pkcs5_keyivgen method does something non-standard when used together with AES. First of all, it uses PBKDF1, not PBKDF2.
It might be difficult to replicate what it does, and implementing cryptographic algorithms is generally inadvisable unless you know exactly what you're doing – even experts often get it wrong.
How should I encrypt a session key on the client side with the public key transported from server side?
Should I use Cipher.WRAP_MODE or Cipher.ENCRYPT_MODE?
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, publicKey);
byte[] wrappedSessionKey = cipher.wrap(sessionKey);
I am not really sure how to use encrypt_mode to encrypt sessionKey. Could someone help me on this?
Wrapping and encrypting are very similar, however wrapping expresses more precisely what you are planning to do. General "encryption" operates on raw data with no semantic meaning, whereas wrapping is known to relate to keys. Hence the Cipher.unwrap() method returns a Key not a byte array.
Your code will be more portable (particular with respect to hardware security modules) if you use wrap for doing key wrapping. In some circumstances, key permissions will allow a wrapping operation but not a raw encryption of the key bytes.
Of course, since the entirety of the JCE architecture is based on a provider concept, you will need to check exactly what algorithm to specify for your chosen provider to get the output format you want. This is particularly important if you are sending the wrapped key data to a third-party.
In your particular case, the same behaviour will be exhibited by both WRAP and ENCRYPT, as demonstrated below, where I interchange the results:
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "SunJSSE");
generator.initialize(2048);
KeyPair keyPair = generator.generateKeyPair();
SecretKey sessionKey = new SecretKeySpec(new byte[16], "AES");
Cipher c = Cipher.getInstance("RSA", "SunJCE");
c.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] result1 = c.doFinal(sessionKey.getEncoded());
c.init(Cipher.WRAP_MODE, keyPair.getPublic());
byte[] result2 = c.wrap(sessionKey);
c.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
SecretKey sessionKey1 = (SecretKey) c.unwrap(result1, "AES",
Cipher.SECRET_KEY);
c.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
SecretKey sessionKey2 = new SecretKeySpec(c.doFinal(result2), "AES");
System.out.println(Arrays.equals(sessionKey1.getEncoded(),
sessionKey2.getEncoded()));
This prints: true
I'm trying to encrypt some text using AES on .net and have it read on Java.
The sample code I got for the encryption looks like this:
byte[] key = ...
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
How does Java get the key and the IV from the SecretKey object? I need to provide them for .net and have found no information on it.
You may provide the IV to the cipher in Java using an instance of IvParameterSpec passed to Cipher.init. If you don't, a random IV will be generated and made available by the getIV method of Cipher.