Translating Ruby encryption code to Java - java

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.

Related

AES GCM key derivation swift

I'm trying to implement in swift the equivalent of my code in java. Basically is an AES implementation with GCM padding and I'm using a key derivation for that. In swift I'm using the CryptoSwift library.
My issue is that I cannot get the same encrypted text in swift.
After a very long research I couldn't find any solutions for my problem, I even saw the test code of the CryptoSwift library repository to get any ideas but with no luck
This is my java code:
GCMParameterSpec ivParameterSpec = new GCMParameterSpec(128, "ivVector".getBytes());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec("myPassword".toCharArray(), "salt".getBytes(), 1000, 256);
SecretKey tmp = secretKeyFactory.generateSecret(keySpec);
key = new SecretKeySpec(tmp.getEncoded(), "AES");
encryptCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptCipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
byte[] encryptedWord = Base64.encode(encryptCipher.doFinal("example".getBytes("UTF-8")));
And this is my swift code:
do{
keyDerivation = try PKCS5.PBKDF2(password: "myPassword".bytes, salt: "salt".bytes, iterations: 1000, keyLength: 32, variant: .sha1).calculate()
let gcm = GCM(iv: keyDerivation, mode: .combined)
let aes = try AES(key: keyDerivation, blockMode: gcm, padding: .noPadding)
let encryptedText = try aes.encrypt("example".bytes)
}catch{
print(error)
}
Any help would be appreciated.
Your IV doesn't match in both cases. In Java you use a string, and in Swift you use the derived key in keyDerivation.
Furthermore, you should make sure that you use the same character encoding. I'd not use getBytes or similar for either language. Explicitly specifying UTF-8 is probably best.
Note that the Java PBKDF2WithHmacSHA1 may handle password encoding in a rather peculiar way, so some kind of input validation on the password is probably in order.
Needless to say, the salt should be random for each call to encrypt, not static. I presume this is just test code though.

Decryption of AES-CBC-256 in Java

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()

Should I use Cipher.WRAP_MODE OR Cipher.ENCRYPT_MODE to encrypt a session key?

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

How does Java derive the the Initialization vector from SecretKeySpec for AES?

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.

BouncyCastle AES error when upgrading to 1.45

Recently upgraded from BC 1.34 to 1.45. I'm decoding some previously-encoded data with the following:
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
When using BC 1.45 I get this exception:
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:715)
at javax.crypto.Cipher.doFinal(Cipher.java:1090)
EDIT: More about this issue. I am using the following to generate raw keys from a passphrase:
KeyGenerator kgen = KeyGenerator.getInstance("AES", "BC");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
What I have found is that this results in two different values for BC 1.34 vs 1.45.
It might also not be BouncyCastle-related (I am testing on Android 2.3)
I just finished tracking this down. It's because of a bug fix on line 320 (in Gingerbread source) of SHA1PRNG_SecureRandomImpl.java in the engineNextBytes() method where
bits = seedLength << 3 + 64;
was changed to
bits = (seedLength << 3) + 64;
Clearly it was a bug that was fixed, but it means that given the same seed, SecureRandom will generate different data pre- and post-gingerbread.
I have a "fix" for it. I stole enough code from android-7 to be able to generate random bytes in the same way that SecureRandom did. I try to decrypt my information and if it fails, use my jacked up SecureRandom to decrypt it. Then I can obviously reencrypt it using the newer SecureRandom, although I'm kind of thinking of moving away from SecureRandom entirely...
Looks like the problem is SecureRandom not being portable across the Froyo-Gingerbread boundary. This post describes a similar problem:
http://groups.google.com/group/android-security-discuss/browse_thread/thread/6ec015a33784b925
I am not sure what exactly changed in SecureRandom, but the only way I found to fix it was to reencrypt the data with keys generated using a portable method.
According to the release notes, this fix was included in version 1.40:
PKCS7Padding validation would not fail if pad length was 0. This has been fixed.
This sounds like it may be pertinent.

Categories