My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

Different Encryption Techniques in Java

Dhiraj Ray's photo
Dhiraj Ray
·Mar 17, 2019

95% of HTTPS servers are vulnerable to trivial ( Man In the Middle Attack ) attacks. In this article, we will not be discussing the different ways to protect against a man-in-the-middle attack. There are already too many of them. Instead, I will try to give a brief idea of different encryption techniques that we use while transmitting any information or payload through the wire from the client-side. Here, the client can be an Android or IOs platform or a browser and we have JVM running on the server. Being a Java developer, the discussion will revolve primarily around Java.

Encryption Type

Transmitting confidential data such as plain text password or any sensitive password through a wire is always vulnerable to security. Sometimes, even the TLS does not help as BEAST attack and Lucky13 attack are evident with TLSv1.0’s successor. Hence, encryption of sensitive information is must even for a TLS enabled the system. Java provides multiple encryption algorithms for this. There are 2 basic types of encryption - Asymmetric and Symmetric encryption. Asymmetric encryption uses two different keys as public and private keys for the process of encryption and decryption. In asymmetric encryption, we encrypt sensitive information with a public key and a matching private key is used to decrypt the same. Asymmetric encryption is mostly used when there are 2 different endpoints are involved such as VPN client and server, SSH etc. Symmetric encryption uses a single key known as a private key or secret key to encrypt and decrypt sensitive information. This type of encryption is very fast as compared to asymmetric encryption and are used in systems such as a database system. Some examples of symmetric encryptions are Twofish, Blowfish, 3 DES, AES.

AES Encryption

AES stands for Advanced Encryption System and its symmetric encryption algorithm. It is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology (NIST) in 2001. The AES engine requires a plain-text and a secret key for encryption and the same secret key is required to again decrypt it. The secret key input can be of 128 bit or 192 bit or 256 bit and the corresponding bit of ciphertext is generated.

aes.PNG AES provides two block cipher mode of encryption - ECB and CBC mode. ECB(Electronic Code Book) is the simplest encryption mode and does not require IV for encryption. The input plain text will be divided into blocks and each block will be encrypted with the key provided and hence identical plain text blocks are encrypted into identical ciphertext blocks. If the same key is used to encrypt all the plain text and if an attacker finds this key then all the cipher can be decrypted in a similar way. Hence, the ECB mode is not semantically secure. CBC mode is highly recommended and it requires IV to make each message unique The use of IV ensures unique ciphertexts are created even when the same plaintext is encrypted multiple times independently with the same key. If no IV is entered then the default will be used here for CBC mode. The AES algorithm has a 128-bit block size, regardless of whether your key length is 256, 192 or 128 bits. When a symmetric cipher mode requires an IV, the length of the IV must be equal to the block size of the cipher. Hence, you must always use an IV of 128 bits (16 bytes) with AES. For a quick demonstration, you can visit this free online AES tool . Following is a sample program in java that performs AES encryption in Java . Here, we are using AES with CBC mode to encrypt a message as ECB mode is not semantically secure. We can use salt and iterations to improve the encryption process further.

private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";

public static String encrypt(String value) {
    try {
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

        byte[] encrypted = cipher.doFinal(value.getBytes());
        return Base64.encodeBase64String(encrypted);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

Following is the reverse process to decrypt the cipher. The code is self-explanatory. public static String decrypt(String encrypted) {

try {
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));

        return new String(original);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return null;
}

RSA Encryption

RSA(Rivest–Shamir–Adleman) cryptography encryption is asymmetric encryption which uses two different keys as public and private keys. Here, you can encrypt sensitive information with a public key and a matching private key is used to decrypt the same. Asymmetric encryption is mostly used when there are 2 different endpoints are involved such as VPN client and server, SSH etc.

Generating RSA Public Private Key

We can use the factory method to generate these keys using KeyPairGenerator. For demo purpose, we are using a key size of 1024. By default, the private key is generated in PKCS#8 format and the public key is generated in X.509 format. Once these keys are generated, either you can write these keys in a file and share the file containing public keys with the client. Remember, the public key is written in the text file as X.509 format. Also, we can do a base64 encode to make it readable and share the string with the client.

In the following example, we will be Base64 encoding the public and private keys to ease the sharing of these keys. RSAKeyPairGenerator.java

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.*;
import java.util.Base64;

public class RSAKeyPairGenerator {

    private PrivateKey privateKey;
    private PublicKey publicKey;

    public RSAKeyPairGenerator() throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(1024);
        KeyPair pair = keyGen.generateKeyPair();
        this.privateKey = pair.getPrivate();
        this.publicKey = pair.getPublic();
    }

    public void writeToFile(String path, byte[] key) throws IOException {
        File f = new File(path);
        f.getParentFile().mkdirs();

        FileOutputStream fos = new FileOutputStream(f);
        fos.write(key);
        fos.flush();
        fos.close();
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public PublicKey getPublicKey() {
        return publicKey;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
        RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator();
        keyPairGenerator.writeToFile("RSA/publicKey", keyPairGenerator.getPublicKey().getEncoded());
        keyPairGenerator.writeToFile("RSA/privateKey", keyPairGenerator.getPrivateKey().getEncoded());
        System.out.println(Base64.getEncoder().encodeToString(keyPairGenerator.getPublicKey().getEncoded()));
        System.out.println(Base64.getEncoder().encodeToString(keyPairGenerator.getPrivateKey().getEncoded()));
    }
}

RSA Encryption In Java

Now We have RSAUtil.java that has methods defined for RSA encryption and decryption in Java . Let us discuss about encryption first. As we discussed above the public key generated is in X.509 format and we use public key for encryption. Hence, we need X509EncodedKeySpec class to convert it again to RSA public key. Remember, that we have base64 encoded public keys. Hence, first let us first Base64 decode and generate the public key. RSAUtil.java

public static PublicKey getPublicKey(String base64PublicKey){
        PublicKey publicKey = null;
        try{
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            publicKey = keyFactory.generatePublic(keySpec);
            return publicKey;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return publicKey;
    }

Now, we have a simple method encrypt() that takes the string to be enrypted and the Base64 encoded RSA key for encryption. Here, getPublicKey() is the method that we defined above.

public static byte[] encrypt(String data, String publicKey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
    return cipher.doFinal(data.getBytes());
}

RSA Decryption In Java

For decryption, we will be using the private key and we discussed above that the private key is generated in PKCS#8 format. Hence, the following is the code to generate the private key from base64 encoded string using PKCS8EncodedKeySpec.

public staticPrivateKey getPrivateKey(String base64PrivateKey){
        PrivateKey privateKey = null;
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
        KeyFactory keyFactory = null;
        try {
            keyFactory = KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        try {
            privateKey = keyFactory.generatePrivate(keySpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return privateKey;
    }

Now following is the decrypt method that accepts RSA encrypted string and Base64 encoded RSA private key for decryption.

public static String decrypt(String data, String base64PrivateKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
        return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey));
    }

Putting RSA and AES together

With every doubling of the RSA key length, decryption is 6-7 times slower. Hence, when there are large messages for RSA encryption, the performance degrades. In such scenarios, we first do an AES encryption of the messages and the key used for AES encryption is RSA encrypted and sent to the server. This technique can be used by the Javascript or Android client for sending sensitive payloads to the server.

Bcrypt Encryption

Bcrypt is a password hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher. Bcrypt uses an adaptive hash algorithm to store password. BCrypt internally generates a random salt while encoding passwords and hence it is obvious to get different encoded results for the same string. But one common thing is that every time it generates a String of length 60. Here is a Bcypt online tool for a demonstration.