1

I need to migrate a crypto package from C++/openssl to pure java implementation. However, I am having some issues that I don't know how to solve.

Below is a C++ list that outlines a decryption scheme that I am currently trying to migrate.

#include <openssl/evp.h> #include <openssl/aes.h> #include <openssl/rand.h> #include <openssl/bio.h> #include <openssl/buffer.h> // set master key AES_KEY master_key; const int AES128_KEY_SIZE = 16; unsigned char* master_secret = "averysecretmastersecret"; AES_set_encrypt_key(master_secret, AES128_KEY_SIZE * 8 , &master_key); // Base64 decode; encryptedInput is the original input text // b64_output consists of two parts: a leading salt (16 bytes) and the following actual data char* b64_output = base64Decode(encryptedInput); // base64Decode(const char* encodedText) -> char* decodedText // prepare salt const char SALT_LEN = 16; // first byte is reserved. Actually only use 15 bytes = 120 bit unsigned char salt[SALT_LEN]; memcpy(salt, b64_output, SALT_LEN); // read salt // generate key const int AES128_KEY_SIZE = 16; unsigned char key[AES128_KEY_SIZE]; salt[0] = 1; // AES_ecb_encrypt(salt, key, &master_key, AES_ENCRYPT); // generate iv const int AES128_IV_SIZE = 16; unsigned char iv[AES128_IV_SIZE]; salt[0] = 2; // ensure that key and iv are different AES_ecb_encrypt(salt, iv, &master_key, AES_ENCRYPT); // initialize cipher context EVP_CIPHER_CTX *de; de = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(de); EVP_DecryptInit_ex(de, EVP_aes_128_cbc(), NULL, key, iv) aes_decrypt(b64_output + SALT_LEN, length - SALT_LEN); // plaintext is a buffer to contain the output int plaintext_size = DEFAULT_BUFFER_SIZE; char *plaintext = (char*)malloc(plaintext_size); int aes_decrypt(const char *ciphertext, int len) { int p_len = len, f_len = 0; // allocate an extra cipher block size of memory because padding is ON // #define AES_BLOCK_SIZE 16 if(p_len + AES_BLOCK_SIZE > plaintext_size) { ASSERT_CALL(enlarge_buffer(plaintext, plaintext_size, p_len + AES_BLOCK_SIZE), "enlarge plaintext buffer failed"); } ASSERT_OPENSSL( EVP_DecryptInit_ex(de, NULL, NULL, NULL, NULL), "sets up decode context failed"); ASSERT_OPENSSL( EVP_DecryptUpdate(de, (unsigned char*)plaintext, &p_len, (unsigned char*)ciphertext, len), "decrypt failed"); EVP_DecryptFinal_ex(de, (unsigned char*)plaintext+p_len, &f_len); return EY_SUCCESS; } EVP_CIPHER_CTX_free(de); dec_result = std::string(plaintext); 

Below is a java code list that I currently have (not working, of course) to reproduce above C++ logic:

String encrypted = "AtUKTnCF18kFTJIycg/RXKJ82IVCtaa+eKNVl8FhT0k+wvpc+cBIs5jb/QlLRMf4"; String secret = "averysecretmastersecret"; int SALT_LEN = 16; String keyAlgorithm = "AES"; String ECB_TRANSFORM = "AES/ECB/NoPadding"; String CBC_TRANSFORM = "AES/CBC/NoPadding"; byte[] bytesOfSecret = Arrays.copyOf(secret.getBytes(), 16); Key key =new SecretKeySpec(bytesOfSecret, keyAlgorithm); Cipher ecbCipher = Cipher.getInstance(ECB_TRANSFORM); Cipher cbcCipher = Cipher.getInstance(CBC_TRANSFORM); // decode byte[] decoded = Base64.getDecoder().decode(encrypted); byte[] salt = Arrays.copyOf(decoded, SALT_LEN); byte[] data = Arrays.copyOfRange(decoded, SALT_LEN, decoded.length); // get iv salt[0] = 2; ecbCipher.init(Cipher.ENCRYPT_MODE, key); byte[] iv = ecbCipher.doFinal(salt); iv = Arrays.copyOf(iv, 16); AlgorithmParameterSpec parameterSpec = new IvParameterSpec(iv); cbcCipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); byte[] bytes = cbcCipher.doFinal(data); String decrypted = new String(bytes); System.out.println(decrypted); 

There are a couple places that I don't know how to map from C++ to java right now. First, in the C++ code, it uses a salt to generate a key and an iv, which are subsequently used to initialize EVP cipher context as in EVP_DecryptInit_ex(de, EVP_aes_128_cbc(), NULL, key, iv). I don't know the equivalent operation in java.

Second, there is no direct mentioning in the C++ code whether padding is used. I tried both NoPadding and PKCS5Padding, but not sure which one is the right one.

So, how can I reproduce the C++ logic in java? Is there any example out there?

update
I also tried BouncyCastle. It is still not working. Below is my code:

int SALT_LEN = 16; String encrypted = "AtUKTnCF18kFTJIycg/RXKJ82IVCtaa+eKNVl8FhT0k+wvpc+cBIs5jb/QlLRMf4"; String password = "averysecretmastersecret"; // decode byte[] decoded = Base64.getDecoder().decode(encrypted); byte[] salt = Arrays.copyOf(decoded, SALT_LEN); byte[] data = Arrays.copyOfRange(decoded, SALT_LEN, decoded.length); BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine())); PBEParametersGenerator generator = new OpenSSLPBEParametersGenerator(); byte[] bytesOfSecret = PBEParametersGenerator.PKCS5PasswordToBytes(password.toCharArray()); generator.init(bytesOfSecret, salt, 1); ParametersWithIV parametersWithIV = (ParametersWithIV) generator.generateDerivedParameters(128, 128); // for decryption cipher.init(false, parametersWithIV); byte[] decrypted = new byte[cipher.getOutputSize(data.length)]; System.out.println("expected decrypted size = " + decrypted.length); // prints ... size = 32 int processedBytes = cipher.processBytes(data, 0, data.length, decrypted, 0); System.out.println("processed bytes = " + processedBytes); // prints ... bytes = 16 cipher.doFinal(decrypted, processedBytes); // Line 59, run into exception String output = new String(decrypted); System.out.println(output); 

Line 59, as marked above, gives this exception:

org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted at org.bouncycastle.crypto.paddings.PKCS7Padding.padCount(Unknown Source) at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(Unknown Source) ... 
2
  • I suspect EVP_DecryptInit_ex is another method you need to migrate. Commented Aug 23, 2015 at 3:43
  • You really aren't migrating anything here. You're writing equivalent code in a completely different API. Commented Aug 23, 2015 at 7:37

1 Answer 1

1

This is an example of java AES encryption i hope this helps

 String key = "HkJHBKJBvffdbv"; String IV= "qjfghftrsbdghzir"; String theMessageToCifer ="your message"; SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes()); try{ //specify your mode Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,ivSpec); encrypted = cipher.doFinal(theMessageToCifer.getBytes()); bytesEncoded = Base64.encode(encrypted); System.out.println(" base64 code " +bytesEncoded); System.out.println("encrypted string: " +encrypted); // decryption cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivSpec); byte[] original = cipher.doFinal(encrypted); String originalString = new String(original); System.out.println("Original string: " + originalString ); }catch (Exception e){ e.printStackTrace(); } 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the answer. However, I am afraid this doesn't help much. One gap between your example and the C++ example in my post is that the iv is predefined in your example, whereas the iv is derived from the salt in the C++ code. Another gap is that the salt also produces a so-called key parameter in the C++ example, which has no place in your java example.
There is a question here on stackoverflow talking about salt in AES using java i guess you should take a look it might help stackoverflow.com/questions/7303103/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.