Skip to content

Asymmetric Encryption#

What Is The Asymmetric Encryption?#

  • Asymmetric encryption, also known as Public-Private-Key Cryptography, encrypts and decrypts the data using two separate cryptographic asymmetric keys. These two keys are known as a public key and a private key. As their names suggest. the private key must be kept secret, whereas the public can be know to everyone. When applying encryption, the public key is used, whereas descrypting requires the private key. Anyone should be able to send us encrypted data, but only we should be able to descrypt and read it! We have some common asymmetric encryption methods as RSA (named after computer scientists Ron Rivest, Adi Shamir, and Leonard Adleman), PKI (Public key infrastructure). More information

     #zoom - Asymmetric encryption is used in TLS, VPN, SSH

Generate Asymmetric Encryption Key Pair In Java 17#

  • In Java 17, we don't need to add any more dependency to generate the asymmetric encryption key pair.

Model#

  • Let's create a model for the response body which will contain the key pair as below.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.springboot.project.model;  

import lombok.Getter;  
import lombok.Setter;  

@Getter  
@Setter  
public class KeyPairResponse {  

    private String publicKey;  
    private String privateKey;  

}

Configuration#

  • Then let's create a configuration property file for loading the length of the RSA key pair because the key pair can have value as 1024 or 2048.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.springboot.project.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "encryption.asymmetric")
public class ApplicationProperty {

    private int keySize;

}
  • Then we configure the application.yaml as below.
1
2
3
encryption:
  asymmetric:
    key-size: 2048

Service#

  • So to generate the asymmetric key pair let's create a service as below.
KeyPairGeneratorService.java
 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
package com.springboot.project.service;

import com.springboot.project.config.ApplicationProperty;
import com.springboot.project.model.KeyPairResponse;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

@Service
public class KeyPairGeneratorService {

    private static final String RSA_ALGORITHM = "RSA";

    private final ApplicationProperty applicationProperty;

    @Autowired
    public KeyPairGeneratorService(ApplicationProperty applicationProperty) {
        this.applicationProperty = applicationProperty;
    }

    public KeyPairResponse generateKeyPair() {
        SecureRandom secureRandom = new SecureRandom();
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            keyPairGenerator.initialize(this.applicationProperty.getKeySize(), secureRandom);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            KeyPairResponse keyPairResponse = new KeyPairResponse();

            keyPairResponse.setPrivateKey(Base64.encodeBase64String(keyPair.getPrivate().getEncoded()));
            keyPairResponse.setPublicKey(Base64.encodeBase64String((keyPair.getPublic().getEncoded())));

            return keyPairResponse;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

}
  • We will use the RSA algorithm for generating the key pair. Then to generate the key pair we also need to provide the key length. RSA keys tend to be 1024 or 2048 bits in length, making them extremely difficult to factorize, though 1024 bit keys are believed to breakable soon.

Controller#

  • Now, we just simply create a controller to below.
 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
package com.springboot.project.controller;  

import com.springboot.project.model.KeyPairResponse;  
import com.springboot.project.service.KeyPairGeneratorService;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.http.HttpStatus;  
import org.springframework.http.MediaType;  
import org.springframework.http.ResponseEntity;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RequestMethod;  
import org.springframework.web.bind.annotation.RestController;  

@RestController  
public class KeyPairController {  


    private final KeyPairGeneratorService keyPairGeneratorService;  

    @Autowired  
    public KeyPairController(KeyPairGeneratorService keyPairGeneratorService) {  
        this.keyPairGeneratorService = keyPairGeneratorService;  
    }  

    @RequestMapping(method = RequestMethod.POST, value = "/v1/asymmetric-encryption/actions/generate-keypair", produces = MediaType.APPLICATION_JSON_VALUE)  
    public ResponseEntity<KeyPairResponse> getKeyPair() {  
        return new ResponseEntity<>(this.keyPairGeneratorService.generateKeyPair(), HttpStatus.CREATED);  
    }  

}

Testing#

  • Now, let's open postman and call the api then we will see the RSA key pair as below.

 #zoom

Using Asymmetric Encryption Key Pair In Java 17#

  • In Java 17, we don't need to add any more dependency to generate the asymmetric encryption key pair.

Model#

  • Let's create a model for the encryption and decryption request body which will contain the key pair as below.
EncryptionDataRequest.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package com.springboot.project.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class EncryptionDataRequest {

    private String data;

}
DecryptionDataRequest.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package com.springboot.project.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class DecryptionDataRequest {

    private String encryptionData;

}

Configuration#

  • Then let's create a configuration property file for loading the private key and public key of the RSA key pair.
ApplicationProperty.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.springboot.project.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "encryption.asymmetric")
public class ApplicationProperty {

    private int keySize;
    private String privateKey;
    private String publicKey;

}
  • Then we configure the application.yaml as below.
application.yaml
1
2
3
4
5
encryption:
  asymmetric:
    key-size: 2048
    private-key: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCSJGYIsw485Op8CG6To0uWyv4Dj8rqUbwknFjJL6g+ue7YiPwVDscB02zVoLYK6+/VBOr1kNxP3USu2lyp/4i4J8VkQl9+Lz7lF3gsO2wfaIRXWiiX6zYUm0544vNbDHmqYtHl0w9hlsa0yRxzYDpmrQbZnwOjAQpJLHl5wGYjtSD8+BYq4HDk5+xSGeIAgtcPMsyeQqJ8kJdFnPV4xmA3sy2n7DNIsettEZmGeW0rHtqt5Vsa5Ffw6V6pbxatt9WIDTvGG1/O5/sHZJeIJvXFJ29Pu6MXHvwoeLIFXdTHzfNnxfHZuPJqJKr3DjWTELIiBiQ+SjGhXYQmAt6lRKYtAgMBAAECggEAAbhSncZDDqMVwhafUTbaIZDguHitcgs65FDAGBjAKYIqtiW2lyYwtWwgRctkbnB5xs9jVzc4leq2xOVmXcyCIW4REVhZTgo5pUdHb3iA50W6+QQ4ohZvRoLQGPV4Lm7RJ8ON+4nRDob7GQ7yOhwR0+TexcTsrV3KbzNfd/gJu7XWJchXdSuX0QabbPPuepDrSWTo3ewNWOw+eUakS3tLMeO9CrMB9wg01slJUfYcmUZe5q81ndN8C3JTJB0aNcMsJKgwXznZMXmr7ERMdvtAjAPoeKWsQ9pK0/LdcZK//JHPXXx+z0eBkp3Kp/NQBr0PcujSjM07M4AOp5wrJNn7gQKBgQC93a/Ok27wdjr8NhyOt9hfN/idC++fcjaYewgEk1PAQj5RRS0N4YRa2P80Ei3yyAitX1VNEj72ebHooQ2jhk9S3nSTwo9UPGiSVtc/RRz+IR1+Bt1IHIoXkIpK1j66mb7QvNt/JZBKqQL2ShZM4ATYYLIFgLkBO3DitrYczIax8QKBgQDFC97ExDtoVKW1La6+pubr5jU7PmHbjbIJWZbaggQ2PtOxwhgYHTg6ZjqhcdQIMsBE2l0QkQU64NtxPZ+8/Voo4wOgdsBYWnPhvYh0md4XvuG7jhNgclIRNVmyC+Bo9CfwO3I0YfdAmxB3sWduxGL3MWsW5loYCa7Y3FR47yh7/QKBgGjPggeVV76iZblo3abjUixuJ236ctMgJ4dRE4IDGk6dwuz2NQLepUPJWkgSaQ8G4dNjNyGOmvdRAqcakP7eW9exOcV/t8OWfVubWMNykaiv+yng/DKcnpaXkd/yGTrFOJLeToUH5Xlxh1MSXvEdEU+pQVzMTFSjzZM5rZqERS7hAoGAPeBwYBHPJgAO2UozKaN3uHReGvCIAk4Irm73tD5H3FD2YUe2ETx7v7sChlgcYUauofCAcQdZQRgDURgs5UgZ2+UN6oHf/N7KrQS7qQH4gDRs5lzDMPI1WzoJpKOeINcrA4FnZ5Or9Rm8nhmThXEeMsnN+y7y88F2qprl2+QeLnECgYEAi7kiKU6l1iheMGw1NkopyEcymJGsoTF9wIDhP9izX8Fz46V29dccRVSVN3DmaXWnTHcCCRlDqLYU/k2XrRvQ4Y0ZTXezmbC2VDoBm49pFBq0FTUSDagZsOoTfVQNWj92JrgHaQ58bO+P39r6iRtNX2fICiDxBaRtHOy9RYQUwLg="
    public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkiRmCLMOPOTqfAhuk6NLlsr+A4/K6lG8JJxYyS+oPrnu2Ij8FQ7HAdNs1aC2Cuvv1QTq9ZDcT91Ertpcqf+IuCfFZEJffi8+5Rd4LDtsH2iEV1ool+s2FJtOeOLzWwx5qmLR5dMPYZbGtMkcc2A6Zq0G2Z8DowEKSSx5ecBmI7Ug/PgWKuBw5OfsUhniAILXDzLMnkKifJCXRZz1eMZgN7Mtp+wzSLHrbRGZhnltKx7areVbGuRX8OleqW8WrbfViA07xhtfzuf7B2SXiCb1xSdvT7ujFx78KHiyBV3Ux83zZ8Xx2bjyaiSq9w41kxCyIgYkPkoxoV2EJgLepUSmLQIDAQAB"

Service#

  • So to use the asymmetric key pair let's create a service as below.
AsymmetricEncryptionService.java
 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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.springboot.project.service;

import com.springboot.project.config.ApplicationProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

@Service
public class AsymmetricEncryptionService {

    private static final String RSA_ALGORITHM = "RSA";

    private final ApplicationProperty applicationProperty;

    @Autowired
    public AsymmetricEncryptionService(ApplicationProperty applicationProperty) {
        this.applicationProperty = applicationProperty;
    }

    public String encryptDataWithPublicKey(String data) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            byte[] x509EncodedBytes = Base64
                    .getDecoder()
                    .decode(this.applicationProperty.getPublicKey().getBytes(StandardCharsets.UTF_8));
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509EncodedBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);

            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] cipherBytes = cipher.doFinal(data.getBytes());

            return Base64
                    .getEncoder()
                    .encodeToString(cipherBytes);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public String decryptDataWithPrivateKey(String encryptedData) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            byte[] pkcs8EncodedBytes = Base64
                    .getDecoder()
                    .decode(this.applicationProperty.getPrivateKey());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] cipherBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData.getBytes()));

            return new String(cipherBytes);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

}
  • As we can see that we will create 2 methods, one is used for encrypt the data by using the public key and the other one is used for decrypt data using the private key. Also, we can use private key for encrypt data and the public key for decrypt data but this way is not common and recommended.

  • So for the method encryptDataWithPublicKey method, we will do some steps as below.

    • Firstly, we will create a Cipher instance for the RSA algorithm, which will be used to perform the encryption.
    • Then the public key, stored as a base64 encoded string, is decoded into its raw byte array representation. This assumes that the public key is stored in X.509 format.
    • Then an X509EncodedKeySpec is created from the decoded public key bytes. This specification is used to reconstruct the public key.
    • Then a KeyFactory for the RSA algorithm is used to generate a PublicKey object from the key specification.
    • Then the cipher is initialized in encryption mode with the public key.
    • Then the input data is converted to a byte array and encrypted using the cipher. The result is a byte array of encrypted data.
    • Then the encrypted byte array is encoded to a base64 string, which is returned as the result.
  • For the method decryptDataWithPrivateKey method, we will have some steps.

    • Firstly, we will create a Cipher instance for the RSA algorithm, which will be used to perform the encryption.
    • Then we will decode the Base64 encoded private key to a byte array.
    • Then thePKCS8EncodedKeySpec is used to specify the key material in the PKCS8 format.
    • KeyFactory.getInstance(RSA_ALGORITHM) gets a KeyFactory object for the RSA algorithm.
    • keyFactory.generatePrivate(keySpec) generates a PrivateKey object from the key specification.
    • cipher.init(Cipher.DECRYPT_MODE, privateKey) initializes the Cipher object in decryption mode with the private key.
    • Base64.getDecoder().decode(encryptedData) decodes the Base64 encoded encrypted data to a byte array.
    • cipher.doFinal(encryptedBytes) decrypts the byte array and returns the decrypted byte array.
    • new String(decryptedBytes) converts the decrypted byte array to a string and returns it.

Controller#

  • Now, we just simply create a controller to below.
AsymmetricEncryptionController.java
 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
package com.springboot.project.controller;

import com.springboot.project.model.DecryptionDataRequest;
import com.springboot.project.model.EncryptionDataRequest;
import com.springboot.project.service.AsymmetricEncryptionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class AsymmetricEncryptionController {

    private final AsymmetricEncryptionService asymmetricEncryptionService;

    @Autowired
    public AsymmetricEncryptionController(AsymmetricEncryptionService asymmetricEncryptionService) {
        this.asymmetricEncryptionService = asymmetricEncryptionService;
    }

    @RequestMapping(method = RequestMethod.POST, path = "/v1/asymmetric-encryption/actions/encrypt")
    public ResponseEntity<String> encryptDataWithPrivateKey(@RequestBody EncryptionDataRequest encryptionDataRequest) {
        return new ResponseEntity<>(this.asymmetricEncryptionService
                .encryptDataWithPublicKey(encryptionDataRequest.getData()), HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.POST, path = "/v1/asymmetric-encryption/actions/decrypt")
    public ResponseEntity<String> encryptDataWithPrivateKey(@RequestBody DecryptionDataRequest decryptionDataRequest) {
        return new ResponseEntity<>(this.asymmetricEncryptionService
                .decryptDataWithPrivateKey(decryptionDataRequest.getEncryptionData()), HttpStatus.OK);
    }

}

Testing#

  • Now, let's open postman and call the api for encrypting the data with public key as below.

     #zoom

  • Then now, let use the response encrypted data and call the api for decrypting the data by private key as below.

 #zoom

  • So the message has been encrypted and decrypted successfully.

See Also#

References#