The Java Cryptography Extension (JCE) is a framework that provides cryptographic functionality within the Java platform. It offers a set of APIs, algorithms, and services for various cryptographic operations, including encryption, decryption, digital signatures, key management, and more. I will discuss in a 3 part series about the details of

  1. Encryption and Decryption using Java
  2. Digital Signatures
  3. KeyManagement and why we need it.

So in the first part let us dive into the Encryption using AES algorithm by using some already prebuilt java apis.

Encryption and Decryption:

JCE allows you to perform encryption and decryption using symmetric and asymmetric encryption algorithms. So we should understand brefily about symmetric and asymmetirc encryption.

Symmetric Encryption

Symmetric Encryption is a type of encryption where only one key (a secret key) is used to both encrypt and decrypt electronic data. The entities communicating via symmetric encryption must exchange the key so that it can be used in the decryption process. This encryption method differs from asymmetric encryption where a pair of keys - one public and one private - is used to encrypt and decrypt messages. By using symmetric encryption algorithms, data is “scrambled” so that it can’t be understood by anyone who does not possess the secret key to decrypt it. Once the intended recipient who possesses the key has the message, the algorithm reverses its action so that the message is returned to its original readable form. The secret key that the sender and recipient both use could be a specific password/code or it can be random string of letters or numbers that have been generated by a secure random number generator (RNG).

Code Example - Symmetric Encryption (AES):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import javax.crypto.*;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

public class Day73 {
    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        // Generate a secret key
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);
        SecretKey secretKey = keyGenerator.generateKey();

        // Initialize the cipher for encryption
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        String plaintext = "Sensitive information";
        byte[] encrypted = cipher.doFinal(plaintext.getBytes());

        System.out.println("Encrypted: " + new String(encrypted));

        // Initialize the cipher for decryption
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] decrypted = cipher.doFinal(encrypted);

        System.out.println("Decrypted: " + new String(decrypted));
    }
}

Now in this example we can see some java api’s. Specifically KeyGenerator, SecretKey and Cipher.

Java has a KeyGenerator class :

This class is designed to generate secret (symmetric) keys, offering the capabilities of a key generator. You can create KeyGenerator instances using any of the getInstance methods provided by this class.

KeyGenerator instances can be used multiple times; that is, once a key has been created, the same KeyGenerator can be used to create more keys.

There are two methods to create a key: one that is algorithm-independent, and another that is specific to an algorithm. The only distinction between these two methods is how the object is initialized. For the algorithm specific ones the java platform provides some algorithms we can choose from and they are

  • AES (128)
  • DES (56)
  • DESede (168)
  • HmacSHA1
  • HmacSHA256

Cipher class gives us the option to select the algorithms that we want to use to encrypt or decrypt. In our case we have chosen AES as algorithm to encrypt by initializing once as ENCRYPT_MODE and when decrypting as DECRYPT_MODE.

To learn more about AES and how it works watch this AES Explained (Advanced Encryption Standard) - Computerphile

Asymmetric Encryption

Asymmetric encryption, also known as public-key cryptography, is a cryptographic system that uses a pair of keys - a public key and a private key - to encrypt and decrypt data. These keys are mathematically related but cannot be derived from one another. The public key is used to encrypt data, while the private key is used to decrypt the data. This system allows for secure communication without the need for the sender and receiver to share the same key beforehand.

Here’s how asymmetric encryption works: Asymmetric encryption, also known as public-key cryptography, is a method of encrypting and decrypting information using a pair of keys: a public key and a private key. It provides a secure way to transmit sensitive data over insecure channels, such as the internet. Here’s a simple example for software developers:

Imagine two people, Alice and Bob, who want to communicate securely over an insecure channel. Both Alice and Bob have a pair of keys, one public and one private.

Key Generation:

Alice generates a public-private key pair. She keeps her private key secret and shares her public key with Bob. Similarly, Bob does the same and shares his public key with Alice.

Encryption:

If Alice wants to send a confidential message to Bob, she uses Bob’s public key to encrypt the message. This encrypted message can only be decrypted using Bob’s private key.

Decryption:

When Bob receives the encrypted message, he uses his private key to decrypt it. Only Bob possesses the private key necessary to unlock the message encrypted with his public key.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;

public class Day73 {

    public static void main(String[] args) throws Exception {

        // Generate Key Pair
        KeyPair keyPair = generateKeyPair();
        System.out.println(keyPair.getPrivate());
        System.out.println(keyPair.getPublic());

        // Message to be encrypted
        String originalMessage = "Hello, this is a secret message!";

        // Encryption
        String encryptedMessage = encrypt(originalMessage, keyPair.getPublic());

        System.out.println("Original Message: " + originalMessage);
        System.out.println("Encrypted Message: " + encryptedMessage);

        // Decryption
        String decryptedMessage = decrypt(encryptedMessage, keyPair.getPrivate());

        System.out.println("Decrypted Message: " + decryptedMessage);
    }

    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(4096);
        return keyPairGenerator.generateKeyPair();
    }

    public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherText = encryptCipher.doFinal(plaintext.getBytes());
        return Base64.getEncoder().encodeToString(cipherText);
    }

    public static String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
        Cipher decriptCipher = Cipher.getInstance("RSA");
        decriptCipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedText = decriptCipher.doFinal(Base64.getDecoder().decode(encryptedText));
        return new String(decryptedText);
    }
}

now lets see how this code does that

Key Pair Generation:

The generateKeyPair() method uses the KeyPairGenerator class to create a new key pair for encryption and decryption. It initializes the key pair generator with the RSA algorithm and a key size of 2048 bits.

Encryption:

The encrypt() method takes a plaintext message and a public key as input. It initializes a Cipher object for encryption using the RSA algorithm. The method then encrypts the plaintext message using the provided public key and returns the encrypted message as a Base64-encoded string.

Decryption:

The decrypt() method takes the encrypted text and the corresponding private key as input. It initializes a Cipher object for decryption using the RSA algorithm. The method then decrypts the Base64-encoded encrypted text using the provided private key and returns the decrypted message as a string.

In the main method, a new key pair is generated. The original plaintext message is encrypted using the public key from the generated key pair. The encrypted message is then printed to the console. Next, the encrypted message is decrypted using the private key from the same key pair, and the decrypted message is printed to the console.

The example demonstrates how to securely encrypt and decrypt messages using asymmetric encryption with the RSA algorithm in Java. The Base64 class is used to encode and decode byte arrays as strings to ensure that the encrypted and decrypted messages can be properly handled as strings.