Encrypt and Decrypt with Aes and Base64 Encoding

Encrypt and decrypt with AES and Base64 encoding

Your Order for encrypt: getBytes, encrypt, encode, toString
Your Order for decrypt(Wrong*): getBytes, decrypt, decode, toString

Two problems:

  1. As someone already mentioned you should reverse the order of operations for decryption. You are not doing that.
  2. encrypt gives you 16 bytes, encode 24 bytes, but toString gives 106 bytes. Something to do with invalid chars taking up additional space.

Note: Also, you don't need to call generateKey() twice.

Fix problem #1 by using the reverse order for decryption.

Correct order for decrypt: getBytes, decode, decrypt, toString

Fix problem #2 by replacing xxx.toString() with new String(xxx). Do this in both the encrypt and decrypt functions.

Your decrypt should look like this:

c.init(Cipher.DECRYPT_MODE, key)
val decodedValue = new Base64().decode(encryptedValue.getBytes())
val decryptedVal = c.doFinal(decodedValue)
return new String(decryptedVal)

This should give you back "dude5"

Aes decrypt file java with base64 data encoding

As mentioned in my first comment: Encryption and decryption use different encodings for the password (Base64 in encryption, ASCII in decryption).

In addition the prefix is Base64 encoded in both encryption and decryption, so prefix plus salt are larger than one block (16 bytes), and therefore the subsequent length determination of the ciphertext during decryption fails, as it is assumed that the ciphertext starts with the 2nd block.

Both issues can be solved if the same encoding is used for the password in encryption and decryption (e.g. ASCII) and the prefix is ASCII encoded, e.g. for encryption:

...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(SALTED_MAGIC);
fos.write(salt);
...

and for decryption:

...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = fis.readNBytes(8);
byte[] salt = fis.readNBytes(8);
...

However the current encryption method does not Base64 encode (only the prefix is Base64 encoded which is rather counterproductive, s. above).

Even a Base64 encoded plaintext does not change this, as the ciphertext itself is not Base64 encoded.

Considering your statement My requirement is to encode data with base64 during encryption and decode data with base64 during decryption and the OpenSSL format used, I assume that you want to decrypt a ciphertext that has been encrypted with the -base64 option, analogous to OpenSSL, i.e. with

openssl enc -aes-256-cbc -base64 -pass pass:testpass -p -in sample.txt -out sample.crypt

Here, prefix, salt and ciphertext are concatenated on byte level and the result is then Base64 encoded. To achieve this, your implementation posted in the question could be changed as follows:

static void decrypt(String path, String password, String outPath) {

try (FileInputStream fis = new FileInputStream(path);
Base64InputStream bis = new Base64InputStream(fis, false, 64, "\r\n".getBytes(StandardCharsets.US_ASCII))) { // from Apache Commons Codec

// Read prefix and salt
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = bis.readNBytes(8);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = bis.readNBytes(8);

// Derive key and IV
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);

// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(bis, cipher);
FileOutputStream fos = new FileOutputStream(outPath)) {

int length;
byte[] buffer = new byte[1024];
while ((length = cis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");

} catch (Exception e) {
e.printStackTrace();
}
}

As I already mentioned in the comments to the linked question, the processing in chunks can easily be implemented with the class CipherInputStream. The Base64 decoding can be achieved with the class Base64InputStream of the Apache Commons Codec, as also addressed by Michael Fehr. This is a convenient way to perform Base64 decoding and decryption together. If the data does not need to be Base64 decoded (e.g. if the -base64 option was not used during encryption) the Base64InputStream class can simply be omitted.

As previously stated in the comments, small files do not need to be processed in chunks. This is only necessary when the files become large relative to the memory. However, since your encryption method processes the data in chunks, it is consistent that the decryption does the same.

Note that the encryption posted in the question is not compatible with the result of the above OpenSSL statement, i.e. the encryption would have to be adapted if necessary (analogous to the decryption posted above).

Edit: The encryptfile() method posted in the question creates a file containing the Base64 encoded prefix, the raw salt and the raw ciphertext. For key derivation the Base64 encoded password is applied. The method is used to encrypt a Base64 encoded plaintext. The following method is the counterpart of encryptfile() and allows the decryption and Base64 decoding of the plaintext:

static void decryptfile(String path, String password, String outPath) {

try (FileInputStream fis = new FileInputStream(path)) {

// Read prefix and salt
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
byte[] prefix = new byte[SALTED_MAGIC.length];
fis.readNBytes(prefix, 0, prefix.length);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = new byte[8];
fis.readNBytes(salt, 0, salt.length);

// Derive key and IV
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);

// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(fis, cipher);
Base64InputStream bis = new Base64InputStream(cis, false, -1, null); // from Apache Commons Codec
FileOutputStream fos = new FileOutputStream(outPath)) {

int length;
byte[] buffer = new byte[1024];
while ((length = bis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");

} catch (Exception e) {
e.printStackTrace();
}
}

The decryptfile()-method writes the Base64 decoded plaintext to the file in outPath. To get the Base64 encoded plaintext simply remove the Base64InputStream instance from the stream hierarchy.

As already explained in the comment, this method is incompatible to OpenSSL, i.e. it can't decrypt ciphertexts generated with the OpenSSL statement posted above (with or without -base64 option). To decrypt a ciphertext generated with OpenSSL with -base64 option use the decrypt() method.

Is it possible to decrypt a base 64 text with a key and AES standard?

I see two problems here: First, if you want to use pycryptodome, you need to decode the Base64 string for the processing.

The second one is more complex. You know that the base64 string is being encrypted with an AES. But it is also necessary to know (or to guess) the block cipher mode of operation. Depending on the mode, you might also need a nonce for the decryption. In other cases, it is necessary to know the initialization vector.

As you don't provide a nonce here, I give you an example for pycryptodome with a base64-encoded message using an OpenPGP mode. Luckily, OpenPGP stores the encrypted IV into the first 18 bytes of the encrypted message.

import base64
from Crypto.Cipher import AES

input_data = b'This is secret message'
key = b'Sixteen byte key'

## ENCRYPTION

encryption_cipher = AES.new(key, AES.MODE_OPENPGP)

# use a nonce, e.g when the mode is AES.MODE_EAX
#nonce = encryption_cipher.nonce
ciphertext = encryption_cipher.encrypt(input_data)

b64_ciphertext = base64.b64encode(ciphertext).decode()
print("Base64 of AES-encrypted message: ", b64_ciphertext)

## DECRYPTION

unb64_ciphertext = base64.b64decode(b64_ciphertext.encode())
iv = unb64_ciphertext[0:18]
unb64_ciphertext = unb64_ciphertext[18:]

decryption_cipher = AES.new(key, AES.MODE_OPENPGP, iv=iv)#, nonce=nonce)
output_data = decryption_cipher.decrypt(unb64_ciphertext)

print("Decrypted message: ", output_data)

This code was taken from the pycryptodome docs. I adapted it to your use case and included the handling of the Base64 input string.

Decrypt a String encoded using AES 256

There are multiple issues with your code and you are missing some critical information (you may ask from the system sending data)

You are missing Cipher, IV (optionally) and key

Cipher c = Cipher.getInstance("AES");

Using only AES cipher with no IV parameter means you are using AES/ECB/PKCS5Padding cipher. Are you sure you suppose to use this cipher? Shouldn't it be AES/CBC/PKCS5Padding ? Ask the system doing the encryption what the encryption should be (including mode and padding). Knowing it's AES may not be sufficient.

If the mode used needs IV (initialization vector), you need to know its value. Usually IV is 128 bits (16 bytes) prepended to the ciphertext, but you need to know that for sure.

String salt = "PRUEBA";

SecretKeySpec key = new SecretKeySpec(salt.getBytes(), "AES");

And - you need a key (without the key you won't decrypt).

As already commented, the key needs to be 128, 192 or 256 bits long (=16, 24 or 32 bytes). If it is to be generated from another string, you need to know how.

String decryptedValue = new String(decValue);

String decoded=new String(Base64.decode(decryptedValue,Base64.DEFAULT));

Are you sure that the decrypted value is base64 encoding of another String?

Just to get some examples for Java crypto, you may have a loot at my blog too.

Http reverse shell in python: AES and Base64

HTTP itself can handle binary payloads just fine.

However, HTTP GET can only be used to communicate text; instead it uses the URL to send data. There are specific rules about the values within the URL, which precludes characters / encodings such as control characters. Actually, to have error-free communication you should be using base64url encoding, a specific dialect which uses dash and underscore instead of plus, slash and equals.

If you request a webpage through a GET request you are not really transmitting large amounts of data after all. For that you would use HTTP POST, and that's what I would suggest instead of base 64.


Just a side note: CBC mode doesn't provide authenticated encryption, which means that any adversary can change the ciphertext, switch out blocks from a previous ciphertext etc. Worse, if you have an active server model you are also vulnerable against padding oracle and other plaintext oracle attacks. That means that in the worst case it doesn't provide any confidentiality either, basically rendering the encryption useless.

Transport security is pretty hard to get right, which is why most people simply gravitate or get coaxed towards TLS. Of course, if you're just learning: carry on :)

AES Decryption when writing to a file using base64 encoding

In the Encryption part, you used AES/CBC/PKCS5Padding that is CBC mode of operation with PKCS#5 - actually, it is PKCS#7 - the there is an IV is missing. The CBC mode requires and IV and that should be at least random. That can be generated with

SecureRandom randomSecureRandom = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);

In the decryption part, you used AES/ECB/PKCS5Padding. Normally both must match. Make both either ECB ( please don't) or CBC with the IV.

The IV in generally, appended to the beginning of the ciphertext. During decryption resolved it and use it.

Also, make the base64 decoding inside of the decryption. This will make your mode more robust.

Final note: if there is no specific reason, prefer AES-GCM which can provide not only confidentiality but also integrity and authentication. If you need to stick CBC you need additionally use HMAC to achieve integrity and authentication.

And for a full code example; See the Maartens answer

Obtaining same Base64 encoded output of encrypted string in Java and JavaScript

Here is answer:

However, in one of the Java source code, they have done like this:

String encryptedMessageInBase64 = Base64.getUrlEncoder().encodeToString(encryptedMessageInBytes);*

Here, basically they have done UrlEncoding instead of Base64 encoding. It is nothing but replacing + and / characters with - and _ characters. Url encoding is when we want to send encoded string over HTTP where it treats these two as special characters this is why we need to replace them with some other characters which are HTTP friendly.



Related Topics



Leave a reply



Submit