Skip to content

Cryptography

German Vekhorev edited this page May 21, 2020 · 2 revisions

It is often difficult for a beginner to encrypt some data, especially do that well and properly. Many people often fail at different points for one reason or another. kantanj provides a really simple API to work with cryptography in Java.

General encryption concepts

Cryptography-related stuff is located inside package me.darksidecode.kantanj.crypto and usually requires no additional libraries. For both encryption and decryption, interface Encryptor is used, which provides two methods: encrypt and decrypt, which both accept and return an array of bytes (byte[]). The encrypt method is used to encrypt a given array of bytes and returns the encrypted data. The decrypt method does right the opposite, accepting encrypted data and returning the original array of bytes.

AES-128

In kantanj, I decided not to focus on AES-128 too much, and only provided a small, simple, and often insecure, implementation. It should not be used in serious production projects.

In order to encrypt a given byte array (byte[] plain) with AES-128, knowing the key (exactly 16 bytes, must be secret) (byte[] key) and the init vector (exactly 16 bytes, does not have to be secret, but should not be reused — you should generate new IV each time you encrypt something) (byte[] iv), use the following code (requires no additional libraries or natives):

/* Sender Side */
byte[] key = ...
byte[] iv = ... // generate something random for encryption and transfer it with the encrypted data for proper decryption

String plaintext = "Hello, world!";
byte[] plain = plaintext.getBytes(StandardCharsets.UTF_8);
Encryptor enc = new AES128Encryptor(key, iv);
byte[] encryptedData = enc.encrypt(plain);

// send `encryptedData` and `iv` to recipient

To decrypt, use the decrypt method:

/* Recipient Side */
byte[] key = ... // secret key known both to sender and recipient
byte[] encryptedData = ... // received data
byte[] iv = ... // received init vector

Encryptor enc = new AES128Encryptor(key, iv);
byte[] plain = enc.decrypt(encryptedData);
String plainText = new String(plain, StandardCharsets.UTF_8); // "Hello, world!"

AES-256

IMPORTANT NOTE: you have to install Java Cryptography Extension (JCE) in order to take advantage of AES-256 encryption for Java 8 — you can download it here. This is not needed for newer versions of Java.

Much more attention was paid to AES-256 encryption, which is the most secure encryption standard at the moment. In order to encrypt a given byte array (byte[] plain) with AES-256, knowing the key (non-empty character array, must be secret) (char[] key) and salt (exactly 8 bytes, does not have to be secret, may be reused) (byte[] salt), use the following code (requires JCE, see the above note for details):

/* Sender Side */
char[] key = ...
byte[] salt = ... // generate 8 random bytes using a strong SecureRandom instance

String plaintext = "Hello, world!";
byte[] plain = plaintext.getBytes(StandardCharsets.UTF_8);
Encryptor enc = new AES256Encryptor(key, salt);
byte[] encryptedData = enc.encrypt(plain);

// unlike AES128Encryptor, AES256Encryptor returns both the encrypted data and the IV all merged in a single byte[] array
// send `encryptedData` to recipient

To decrypt, use the decrypt method:

/* Recipient Side */
char[] key = ... // secret key known both to sender and recipient
byte[] encryptedData = ... // received data, also contains IV
byte[] salt = ... // salt known both to sender and recipient, does not have to be secret

Encryptor enc = new AES256Encryptor(key, salt);
byte[] plain = enc.decrypt(encryptedData);
String plainText = new String(plain, StandardCharsets.UTF_8); // "Hello, world!"

Generating random IV/salt

Both in order to generate AES-128 init vector and AES-256 salt, you should only rely on SecureRandom, since its results are way more complex to predict. Never use plain Random for cryptograhpy purposes!

Here is a brief example how you can generate a random 8-bytes-long salt:

SecureRandom srnd = SecureRandom.getInstanceStrong();

byte[] salt = new byte[8];
srnd.nextBytes(salt); // done, now `salt` is filled with eight securely randomized bytes

NOTE: if the last line of the above code (nextBytes) takes too long (or infinite time) to run, this means that you are running out of entropy, or, in other words, your system/machine does not provide enough random data. This can usually be easily resolved, just Google a little bit.

Clone this wiki locally