Aes Encrypt and Decrypt

How to symmetrically encrypt & decrypt some data in C# using AES?

I would take a look at the example in the documentation and compare to your code.

Notably when decrypting you are using aes.CreateEncryptor(key, iv). It should probably be aes.CreateDecryptor(key, iv).

The example from the docs also inputs the key and IV when calling CreateEncryptor, but I'm not sure if that is required or not.

You should probably not use sha256 to generate the key from a password. The correct way would be a key derivation algorithm. For example Rfc2898DeriveBytes

AES Encrypt and Decrypt

I found the solution, it is a good library.

Cross platform 256bit AES encryption / decryption.

This project contains the implementation of 256 bit AES encryption which works on all the platforms (C#, iOS, Android). One of the key objective is to make AES work on all the platforms with simple implementation.

Platforms Supported:
iOS ,
Android ,
Windows (C#).

https://github.com/Pakhee/Cross-platform-AES-encryption

AES 256 encrypt and Decrypt in Java

TLDR: it's mostly the IV

For encryption modes like CBC that use an IV (initialization vector, or sometimes nonce) decryption must use the same IV (and key) as encryption did. In addition, a block mode like CBC generally requires padding and unpadding the data, and OpenSSL as used by PHP does so using the 'standard' PKCS5/7 padding which Java also supports but must select.

$ cat 71865785.php
$data = "some test data";
$cipher = "aes-256-cbc";
$key = "reallyrealsekritreallyrealsekrit"; // for test only, real key should not be human-chosen
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
echo base64_encode($iv)."\n".openssl_encrypt($data,$cipher,$key,0,$iv)."\n";
?>
$ php 71865785.php
LGQXy7XF8DHBilN4Kb8Xrg==
DWI/F97O5e/NGD2czp2OUA==
$ cat SO71865785.java
//nopackage
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;

public class SO71865785 /* decrypt from PHP */ {
public static void main (String[] args) throws Exception {
byte[] key = "reallyrealsekritreallyrealsekrit".getBytes("ASCII"); // for test only
byte[] iv = Base64.getDecoder().decode("LGQXy7XF8DHBilN4Kb8Xrg==");
byte[] ctext = Base64.getDecoder().decode("DWI/F97O5e/NGD2czp2OUA==");

Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init (Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] plain = ciph.doFinal(ctext);
System.out.println ("len="+plain.length+":"+new String(plain,"ASCII"));
// if non-ASCII chars are present must use encoding to match encrypting system which is not known
}
}
$ javac SO71865785.java
$ java -cp . SO71865785
len=14:some test data

For a real application of course you wouldn't use hardcoded data (and usually shouldn't hardcode key either) and the method of passing the data produced by the PHP app to the Java app varies (a lot!). One not-uncommon way is to concatenate the IV and ciphertext (and preferably a MAC or auth tag as well) for transmission and/or storage and then separate them with code like the two Arrays.copyOfRange you have in comments, but for this demo I simply base64-ed each piece (openssl_encrypt already does base64 by default).

Note for AES-256 the key must be exactly 32 bytes (and should be arbitrary bytes, not characters recognizable to a human). In PHP/OpenSSL if you provide less than required it silently pads with zero bytes (aka NUL), but Java does not; if you have a too-short byte[] in Java you can conveniently zero-pad it with e.g. Arrays.copyOf(poorkey,32).

How to use OpenSSL to encrypt/decrypt files?

Security Warning: AES-256-CBC does not provide authenticated encryption and is vulnerable to padding oracle attacks. You should use something like age instead.

Encrypt:

openssl aes-256-cbc -a -salt -pbkdf2 -in secrets.txt -out secrets.txt.enc

Decrypt:

openssl aes-256-cbc -d -a -pbkdf2 -in secrets.txt.enc -out secrets.txt.new

More details on the various flags

CryptoJS javascript AES-128, ECB encrypt / decrypt

There are several bugs in your code:

  • Block cipher modes like ECB always require padding if the size of the plaintext does not correspond to an integer multiple of the blocksize, as is the case in the above example. Actually, an exception should be thrown. CryptoJS, however, implicitly pads with 0x00 values, performs the encryption, and truncates the ciphertext to the length of the original plaintext. This processing is haphazard and is (im my opionion) even a bug.

    As fix, padding must be applied. Since you are talking about 0-padding, you may mean zero padding, i.e. CryptoJS.pad.ZeroPadding. Note that PKCS#7 padding (the default) is more reliable.
  • You are using the hex encoder for the key import, so the 16 hex digits of your key are converted to an 8 bytes key, which is not a valid AES key (valid AES key lengths are 16, 24 and 32 bytes). However, due to a bug, CryptoJS processes this key size but produces a non-AES compliant result.

    As fix, for instance, a 32 hex digit key must be used for AES-128 in combination with the Hex encoder. Another alternative that works with the posted key is the Utf8 encoder.
  • The plaintext is obviously not hex encoded, so no hex encoder may be applied.

    As fix the Utf8 encoder is to be used.
  • ECB mode does not use an IV, i.e. the IV is ignored and can therefore be omitted. Note that the ECB mode is insecure precisely because it does not use an IV. Therefore, a mode with an IV should be applied.
  • During decryption CryptoJS.AES.decrypt() expects a CipherParams object, but in the current code the hex encoded ciphertext is passed.

    The fix is to either create a CipherParams object from the hex encoded ciphertext and pass it, or to pass the Base64 encoded ciphertext, which CryptoJS implicitly converts to a CipherParams object.
  • The Utf8 decoding of the decrypted data is missing.

Fixed code:

var text = 'US0378331005-USD-US-en';
var key = '11A1764225B11AA1';

console.log('text:', text);
console.log('key:', key);
console.log('key length:', key.length );

// Fix: Use the Utf8 encoder
text = CryptoJS.enc.Utf8.parse(text);
// Fix: Use the Utf8 encoder (or apply in combination with the hex encoder a 32 hex digit key for AES-128)
key = CryptoJS.enc.Utf8.parse(key);

// Fix: Apply padding (e.g. Zero padding). Note that PKCS#7 padding is more reliable and that ECB is insecure
var encrypted = CryptoJS.AES.encrypt(text, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Hex);
console.log('encrypted', encrypted);

// Fix: Pass a CipherParams object (or the Base64 encoded ciphertext)
var decrypted = CryptoJS.AES.decrypt({ciphertext: CryptoJS.enc.Hex.parse(encrypted)}, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });

// Fix: Utf8 decode the decrypted data
console.log('decrypted', decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

Decrypt AES in JavaScript

CryptoJS uses WordArrays, so that key, IV and ciphertext have to be converted accordingly. For this purpose the appropriate encoders have to be applied. Furthermore decrypt() expects the ciphertext as CipherParams object.

This results in the following possible CryptoJS implementation: