{{announcement.body}}
{{announcement.title}}

Implementation of Hybrid Encryption Using Java 1.8 and OpenSSL

DZone 's Guide to

Implementation of Hybrid Encryption Using Java 1.8 and OpenSSL

Here we are going to understand why to use hybrid encryption and see an implementation of hybrid encryption out of AES symmetric encryption algorithm and RSA asymmetric algorithm using Java 1.8 and OpenSSL.

· Security Zone ·
Free Resource

Symmetric encryption is a type of encryption in which a single key is used to both encrypt and decrypt the data, whereas in asymmetric encryption approach public/private key pair is used to do the same. This rules out the risk of mishandling of the key as the public key is only shared with the clients and the private key is kept secret. Client can encrypt the data with the key and send the data securely over any standard data sharing protocols. At the receiver end, the private key is used to decrypt the data.

But the time to encrypt the data with asymmetric encryption grows significantly proportionately with the size of data. Here symmetric encryption does the job quite efficiently.

What Is Hybrid Encryption?

Hybrid encryption as the name suggests combines the best of both the worlds of symmetric and asymmetric encryption and allows us to encrypt the data efficiently while ensuring secrecy is not compromised .

At the client end, we encrypt the data with symmetric encryption using secret key. The secret key is then encrypted with the asymmetric public key. Then, both the encrypted key and encrypted data are sent to the receiver. 

At the receiver end the secret key is retrieved by decrypting the encrypted secret key with the private key. The secret key is then used to decrypt the encrypted data.

Now we are going to take a journey from RSA public/private key generation via OpenSSL to key importing in Java, then data encryption, data exporting till data decryption in step by step manner. The steps are shown below...

  1. How to create RSA public/private key with certificate
    1. How to extract modulus, public exponent and private exponent from the private key and certificate
    2. How to extract modulus and public exponent from certificate
    3. How to validate the key with certificate
  2. How to construct RSA key pair in java using modulus and public/private exponent
  3. Example of data encryption using hybrid encryption technology.
  4. Example of data decryption using hybrid decryption technology.

1. Generation of RSA Key-Pair Using OpenSSL With Self-Signing Certificate

Shell
 




xxxxxxxxxx
1


 
1
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt



  • -x509 is a certificate signing utility
  • -newkey rsa:2048 is for creating RSA key of size 2048bytes (privateKey.key)
  • -days 365 is the validity time frame for the certificate (certificate.crt)

Certificate

The certificate contains the details of the organization (who is generating the key) along with the public key. 

The private key holds the details of both private and public key. Therefore the private key is kept secured within the key generating organisation, whereas the certificate is shared with the clients who wants to send encrypted data to the organisation.

1.1 How to Extract Modulus, Public Exponent and Private Exponent From the Private Key

Shell
 




xxxxxxxxxx
1


 
1
openssl rsa -in privateKey.key -text -noout



Extraction from private key

1.2 How to Extract Modulus and Public Exponent from The Certificate

Shell
 




xxxxxxxxxx
1


 
1
openssl x509 -in certificate.crt -text -noout



Extraction from certificate

1.3 How to Validate the Certificate with The Private Key?

The modulus and the public exponent of the certificate will be the same as that of the private key.

2. Construction of RSA Key-Pair in Java Using Modulus and Public/Private Exponent

Class: RsaKeyExtractor.class

Java
 




xxxxxxxxxx
1
53


 
1
package utilities;
2
 
          
3
import java.math.BigInteger;
4
import java.security.KeyFactory;
5
import java.security.NoSuchAlgorithmException;
6
import java.security.PrivateKey;
7
import java.security.PublicKey;
8
import java.security.spec.InvalidKeySpecException;
9
import java.security.spec.RSAPrivateKeySpec;
10
import java.security.spec.RSAPublicKeySpec;
11
 
          
12
public class RsaKeyExtractor {
13
    
14
    private BigInteger mod;
15
    private BigInteger pubExp;
16
    private BigInteger pvtExp;
17
    
18
    public RsaKeyExtractor(BigInteger mod, BigInteger pubExp) {
19
        this.mod = mod;
20
        this.pubExp = pubExp;
21
        this.pvtExp = null;
22
    }
23
 
          
24
    public RsaKeyExtractor(BigInteger mod, BigInteger pubExp, BigInteger pvtExp) {
25
        this.mod = mod;
26
        this.pubExp = pubExp;
27
        this.pvtExp = pvtExp;
28
    }
29
    
30
    public PrivateKey getPrivateKey()
31
            throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidRsaKey {
32
        
33
        if(pvtExp==null || mod==null)
34
            throw new InvalidRsaKey("PrivateExponent or Modulus is Null");
35
        
36
        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(mod, pvtExp);
37
        KeyFactory fact = KeyFactory.getInstance("RSA");
38
        PrivateKey privKey = fact.generatePrivate(keySpec);
39
        return privKey;
40
    }
41
    
42
    public PublicKey getPublicKey()
43
            throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidRsaKey {
44
        
45
        if(pvtExp==null || mod==null)
46
            throw new InvalidRsaKey("PublicExponent or Modulus is Null");
47
        
48
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(mod, pubExp);
49
        KeyFactory fact = KeyFactory.getInstance("RSA");
50
        PublicKey pubKey = fact.generatePublic(keySpec);
51
        return pubKey;
52
    }
53
}



This class shows how to instantiate PublicKey and PrivateKey objects in java 1.8. For instantiating the PublicKey object we need modulus and public exponent. Similarly to instantiate PrivateKey object we need modulus and the private exponent.

3. Example of the Implementation of Hybrid Encryption Technology for Data Encryption

Class: Encrypter.java

Java
 




xxxxxxxxxx
1
101


 
1
import java.io.BufferedReader;
2
import java.io.BufferedWriter;
3
import java.io.File;
4
import java.io.FileNotFoundException;
5
import java.io.FileReader;
6
import java.io.FileWriter;
7
import java.io.IOException;
8
import java.nio.charset.StandardCharsets;
9
import java.security.NoSuchAlgorithmException;
10
import java.security.PublicKey;
11
import java.util.Arrays;
12
import java.util.Base64;
13
 
          
14
import javax.crypto.KeyGenerator;
15
import javax.crypto.SecretKey;
16
 
          
17
import utilities.AsymmetricCipher;
18
import utilities.SymmetricCipher;
19
 
          
20
public class Encrypter {
21
 
          
22
    public static File encryptionFlow(String path, PublicKey publicKey, int secretKeySize, String secretKeyAlgo, String symmetricAlgo, String asymmetricAlgo) {
23
 
          
24
        // Secret Key Generation
25
        KeyGenerator keyGenerator;
26
        File encryptedFile = null;
27
        try {           
28
            File file = new File(path);
29
            
30
            // AES 128
31
            keyGenerator = KeyGenerator.getInstance(secretKeyAlgo);
32
            keyGenerator.init(secretKeySize);
33
            SecretKey secretKey = keyGenerator.generateKey();
34
            byte[] secretkeyByte = secretKey.getEncoded();
35
            System.out.println("\n@EncFlow: Original Secret Key");
36
            System.out.println(Arrays.toString(secretkeyByte));
37
            
38
            // Reading content
39
            FileReader reader = new FileReader(file);
40
            BufferedReader buffReader = new BufferedReader(reader);
41
            
42
            char[] buffer = new char[100];
43
            int nChar = buffReader.read(buffer);
44
            StringBuilder sb = new StringBuilder();
45
            while (nChar>0) {
46
                for(int i=0; i<nChar; i++) {
47
                    sb.append(buffer[i]);
48
                }
49
                
50
                nChar = buffReader.read(buffer);
51
            }
52
            String content = sb.toString();
53
            byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
54
            System.out.println("\n@Content Bytes");
55
            System.out.println(Arrays.toString(contentBytes));
56
            byte[] encDataBytes = SymmetricCipher.encrypt(contentBytes, symmetricAlgo, secretKey);
57
            System.out.println("\n@Encrypted Content Bytes");
58
            System.out.println(Arrays.toString(encDataBytes));
59
            byte[] encSecretkey = AsymmetricCipher.encrypt(secretkeyByte, asymmetricAlgo, publicKey);
60
            System.out.println("\n@Encrypted Secret Key : ");
61
            System.out.println(Arrays.toString(encSecretkey));
62
            
63
            Base64.Encoder encoder = Base64.getEncoder();
64
            
65
            byte[] base64EncSecKey = encoder.encode(encSecretkey);
66
            byte[] base64EncData = encoder.encode(encDataBytes);
67
 
          
68
            String encSecretkeyString = new String(base64EncSecKey, StandardCharsets.UTF_8);
69
            String encDataString = new String(base64EncData, StandardCharsets.UTF_8);
70
            System.out.println("\n@Encrypted Secret Key String: ");
71
            System.out.println(encSecretkeyString);
72
            System.out.println("\n@Encrypted Data String: ");
73
            System.out.println(encDataString);
74
            // FileContent
75
            sb = new StringBuilder();
76
            sb.append(encSecretkeyString).append(System.lineSeparator()).append(encDataString);
77
 
          
78
            // Creation of encrypted file
79
            String encFilePath = file.getPath().concat(".enc");
80
 
          
81
            encryptedFile = new File(encFilePath);
82
            FileWriter writer;
83
            writer = new FileWriter(encryptedFile);
84
            BufferedWriter bufWriter = new BufferedWriter(writer);
85
            bufWriter.write(sb.toString());
86
            bufWriter.close();
87
 
          
88
        } catch (NoSuchAlgorithmException e1) {
89
            // TODO Auto-generated catch block
90
            e1.printStackTrace();
91
        } catch (FileNotFoundException e1) {
92
            // TODO Auto-generated catch block
93
            e1.printStackTrace();
94
        } catch (IOException e1) {
95
            // TODO Auto-generated catch block
96
            e1.printStackTrace();
97
        }
98
 
          
99
        return encryptedFile;
100
    }
101
}



Here first the AES symmetric secret key is generated. Then the data which is read from the file is encrypted using the secret key and suitable AES symmetric key algorithm. The secret key byte array is then encrypted using the public key of RSA asymmetric encryption technique. 

Now both encrypted secret key and encrypted data are encoded with base64 encoder in order to preserve the encrypted data from any kind of data corruption/misinterpretation during or after data transfer process. 

Finally the encoded and encrypted secret key and the data is written to a file sequentially, each in separate line. Now the new file is secured and safe to transfer via any standard file transfer protocol to the certificate owning organisation.

4. Example of Implementation of Hybrid Encryption Technology for Data Decryption

Class: Decrypter.java

Java
 




x
155


 
1
import java.io.BufferedReader;
2
import java.io.File;
3
import java.io.FileNotFoundException;
4
import java.io.FileOutputStream;
5
import java.io.FileReader;
6
import java.io.IOException;
7
import java.io.OutputStream;
8
import java.nio.charset.StandardCharsets;
9
import java.security.PrivateKey;
10
import java.util.ArrayList;
11
import java.util.Arrays;
12
import java.util.Base64;
13
 
          
14
import javax.crypto.SecretKey;
15
import javax.crypto.spec.SecretKeySpec;
16
 
          
17
import utilities.AsymmetricCipher;
18
import utilities.SymmetricCipher;
19
 
          
20
public class Decrypter {
21
 
          
22
    public static File decryptionFlow(String path, PrivateKey pvtKey, String secretKeyAlgo, String symmetricAlgo, String asymmetricAlgo) throws IOException {
23
 
          
24
        File inFile = new File(path);
25
        String[] contents = extractDataElements(inFile);
26
 
          
27
        System.out.println("Content Count: "+contents.length);
28
        
29
        for (int i = 0; i < contents.length; i++) {
30
            contents[i] = filterNewlineChars(contents[i]);
31
        }
32
        
33
        System.out.println("\n@Base64 encoded Encrypted Secret Key String: ");
34
        System.out.println(contents[0]);
35
        System.out.println("\n@Base64 encoded Encrypted Data String: ");
36
        System.out.println(contents[1]);
37
        
38
        Base64.Decoder decoder = Base64.getDecoder();       
39
        
40
        byte[] encSecretKeyByte = decoder.decode(contents[0]);
41
        System.out.println("\n@Enc SecretKey");
42
        System.out.println(Arrays.toString(encSecretKeyByte));
43
        
44
        byte[] secretKeyBytes = AsymmetricCipher.decrypt(encSecretKeyByte, asymmetricAlgo, pvtKey);     
45
        System.out.println("\n@Secret Key Bytes: ");
46
        System.out.println(Arrays.toString(secretKeyBytes));
47
        
48
        SecretKey secretKey = new SecretKeySpec(secretKeyBytes, 0, secretKeyBytes.length, secretKeyAlgo);
49
 
          
50
        ArrayList<byte[]> dataList = new ArrayList<byte[]>();
51
        for (int i = 1; i < contents.length; i++) {
52
            byte[] encStrByte = decoder.decode(contents[i]);
53
            System.out.println("\n@Encrypted Data Bytes: ");
54
            System.out.println(Arrays.toString(encStrByte));
55
            
56
            byte[] messageByte = SymmetricCipher.decrypt(encStrByte, symmetricAlgo, secretKey);
57
            System.out.println("\n@Data Bytes: ");
58
            System.out.println(Arrays.toString(messageByte));
59
            
60
            dataList.add(messageByte);
61
            contents[i] = new String(messageByte, StandardCharsets.UTF_8);
62
        }
63
        
64
        int lastIndx = inFile.getPath().lastIndexOf(".");
65
        String outpath = inFile.getPath().substring(0, lastIndx);
66
 
          
67
        lastIndx = outpath.lastIndexOf(".");
68
        outpath = outpath.substring(0, lastIndx).concat(".dat");
69
        outpath = outpath.substring(0, lastIndx).concat(".dat");
70
        
71
        File nwFile = new File(outpath);
72
        OutputStream os = new FileOutputStream(nwFile);
73
        for (byte[] data : dataList) {
74
            os.write(data);
75
        }
76
        os.close();
77
 
          
78
        return nwFile;
79
    }
80
 
          
81
    private static String[] extractDataElements(File file) {
82
 
          
83
        String[] dataArr = null;
84
 
          
85
        FileReader reader;
86
        BufferedReader bufReader;
87
        try {
88
 
          
89
            char nwLine = '\n';
90
            char cr = '\r';
91
 
          
92
            reader = new FileReader(file);
93
            bufReader = new BufferedReader(reader);
94
            
95
            
96
            //Extracting the Encrypted Secret Key
97
            String encSecretKey = bufReader.readLine();
98
            
99
            StringBuilder sb = new StringBuilder();
100
            char[] buffer = new char[10]; 
101
            int nChars = bufReader.read(buffer);
102
            while (nChars>0) {
103
                
104
                for(int i=0; i<nChars; i++) {
105
                    sb.append(buffer[i]);   
106
                }
107
                
108
                nChars = bufReader.read(buffer);                
109
            }
110
            
111
            dataArr = new String[2]; 
112
            dataArr[0] = encSecretKey;
113
            dataArr[1] = sb.toString();
114
 
          
115
        } catch (FileNotFoundException e) {
116
            // TODO Auto-generated catch block
117
            e.printStackTrace();
118
        } catch (IOException e) {
119
            // TODO Auto-generated catch block
120
            e.printStackTrace();
121
        }
122
 
          
123
        return dataArr;
124
    }
125
 
          
126
    private static String filterNewlineChars(String data) {
127
 
          
128
        if (data == null) {
129
            return null;
130
        }
131
 
          
132
        char carriageReturn = '\r';
133
        char newLine = '\n';
134
        int length = data.length();
135
        int indx = 0;
136
 
          
137
        // Checking nextLine chars at the start of the line
138
        while (indx < length && (data.charAt(indx) == carriageReturn || data.charAt(indx) == newLine)) {
139
            indx++;
140
        }
141
 
          
142
        int startIndx = indx;       
143
        indx = length-1;
144
        // Checking nextLine Chars at the end of the line
145
        while (indx >= startIndx && (data.charAt(indx) == carriageReturn || data.charAt(indx) == newLine)) {
146
            indx--;
147
        }
148
        int endIndx = indx+1;
149
 
          
150
        String cleanData = data.substring(startIndx, endIndx);
151
        System.out.println("AcLength:" + data.length() + ", ModLength:" + cleanData.length());
152
 
          
153
        return cleanData;
154
    }   
155
}



Similar to the data encryption process, now during decryption we have to follow the reverse process of that of encryption flow. 

The contents of the encrypted file is read line by line and filtered off any newline chars. The first line is the base64 encoded encrypted secret key while the second line is the base64 encoded encrypted data. Both are decoded using the Base64 decoder. Next, the secret key byte array is decrypted using the private key and RSA asymmetric algorithm, as used during encryption. Then the SecretKey is instantiated in java using the secret key byte array and the AES symmetric algorithm, same as used to create the secret key during the encryption flow. The data is then decrypted using the secret key. Finally, the decrypted data is extracted.

GitHub repo for complete code reference.

Topics:
aes 256, asymmetric encryption, hybrid encryption, java, openssl, rsa encryption, secuirty, symmetric encryption, tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}