Java Security Usage

Encode

Base64

String text = "hello 世界";
String encodeText = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
System.out.println(encodeText);
String decodeText = new String(Base64.getDecoder().decode(encodeText), StandardCharsets.UTF_8);
System.out.println(decodeText);

URL Encode

String text = "hello 世界";
String encodeText = URLEncoder.encode(text, StandardCharsets.UTF_8.name());
System.out.println(encodeText);
String decodeText = URLDecoder.decode(encodeText, StandardCharsets.UTF_8.name());
System.out.println(decodeText);

Hash Algorithms

MD5

MD5 (Message Digest 5)

String text = "123456";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(text.getBytes(StandardCharsets.UTF_8));
byte[] digest = messageDigest.digest();
String hashText = DatatypeConverter.printHexBinary(digest);
System.out.println(hashText);

Note: the MessageDigest is not thread-safe. Consequently, we should use a new instance for every thread.

SHA

SHA (Secure Hash Algorithm)

SHA-256 by MessageDigest

String text = "123456";
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.reset();
messageDigest.update(text.getBytes(StandardCharsets.UTF_8));
byte[] digest = messageDigest.digest();
String hashText = DatatypeConverter.printHexBinary(digest);
System.out.println(hashText);

SHA3-256 by MessageDigest (since Java 9)

String text = "123456";
MessageDigest messageDigest = MessageDigest.getInstance("SHA3-256");
messageDigest.reset();
messageDigest.update(text.getBytes(StandardCharsets.UTF_8));
byte[] digest = messageDigest.digest();
String hashText = DatatypeConverter.printHexBinary(digest);
System.out.println(hashText);

Conclusion

  • SHA256 is difficult to handle than MD5 because of its size.
  • SHA256 is less secure than MD5
  • MD5 result in an output of 128 bits whereas SHA256 result output of 256 bits.

Concluding all points, it will be better to use MDA5 if you want to secure your files otherwise you can use SHA256.

Symmetric Encryption Algorithms

DES

DES (data encryption standard, 1976)

DES is Not Secure.

3DES

3DES is Not Secure.

AES

AES (advanced encryption system, 2001)

The AES algorithm has six modes of operation:

  1. ECB (Electronic Code Book) Not Recommend
  2. CBC (Cipher Block Chaining)
  3. CFB (Cipher FeedBack)
  4. OFB (Output FeedBack)
  5. CTR (Counter)
  6. GCM (Galois/Counter Mode)

Don’t use AES Electronic codebook (ECB) Mode. The AES ECB mode, or AES/ECB/PKCS5Padding (in Java) is not semantically secure.

AES encryption best practice: Don’t reuse IV with the same key.

IV

The IV (initial value or initial vector), it is random bytes, typically 12 bytes or 16 bytes. In Java, we can use SecureRandom to generate the random IV.

public static IvParameterSpec generateIv() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}

secret key

The AES secret key, either AES-128 or AES-256. In Java, we can use KeyGenerator to generate the AES secret key.

public static SecretKey getAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, SecureRandom.getInstanceStrong());
return keyGen.generateKey();
}

The AES secret key that derived from a given password. In Java, we can use the SecretKeyFactory and PBKDF2WithHmacSHA256 to generate an AES key from a given password.

public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
// iterationCount = 65536
// keyLength = 256
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return secret;
}

salt

We use salt to protect rainbow attacks, and it is also a random byte, we can use the SecureRandom to generate it.

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.List;

public class CryptoUtils {

public static byte[] getRandomNonce(int numBytes) {
byte[] nonce = new byte[numBytes];
new SecureRandom().nextBytes(nonce);
return nonce;
}

// AES secret key
public static SecretKey getAESKey(int keysize) throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keysize, SecureRandom.getInstanceStrong());
return keyGen.generateKey();
}

// Password derived AES 256 bits secret key
public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
// iterationCount = 65536
// keyLength = 256
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return secret;
}

// hex representation
public static String hex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}

// print hex with block size split
public static String hexWithBlockSize(byte[] bytes, int blockSize) {
String hex = hex(bytes);
// one hex = 2 chars
blockSize = blockSize * 2;
// better idea how to print this?
List<String> result = new ArrayList<>();
int index = 0;
while (index < hex.length()) {
result.add(hex.substring(index, Math.min(index + blockSize, hex.length())));
index += blockSize;
}
return result.toString();
}
}

CBC Mode

Input

  • algorithm: AES/CBC/PKCS5Padding
  • iv bytes
  • aes key bits
  • secretKey: generate by password
  • Initialization Vector (IV): random bytes

GCM Mode


Asymmetric Encryption Algorithms

RSA

RSA (Rivest-Shamir-Adleman)

public static void getKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair();
Key pub = keyPair.getPublic();
Key pvt = keyPair.getPrivate();
System.out.println("public key");
System.out.println(Base64.getEncoder().encodeToString(pub.getEncoded()));
System.out.println("private key");
System.out.println(Base64.getEncoder().encodeToString(pvt.getEncoded()));
}

public static String decrypt(String privateKeyBase64String, String cipherText) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64String.getBytes()));
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(bytes);
}

public static String encrypt(String publicKeyBase64String, String plainText) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec pkcs8KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64String.getBytes()));
PublicKey publicKey = keyFactory.generatePublic(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = cipher.doFinal(plainText.getBytes());
return new String(Base64.getEncoder().encode(bytes));
}

ECC

ECC (Elliptic Curve Cryptography)


References

[1] Java AES encryption and decryption