Encryption, Part 1: Symmetric Encryption
Learn more about the basics of encryption in this series!
Join the DZone community and get the full member experience.
Join For FreeA few weeks ago, I was demonstrating how to use a generic, Talend-based-data offloading framework that I developed late last year. The framework allows a data team to download data from source data stores like Oracle, MySQL, MS SQLServer, etc, to perform various operations on the data before uploading it to various cloud platforms like Amazon Web Services (AWS) S3 buckets, AWS Redshift, and Azure data stores (in addition to uploading to HDFS and Hive). The data offloading activity is managed as a combination of atomic tasks that can be executed in a batch, one after the other. For example, to offload an Oracle table to Redshift (via an S3 bucket), we need to create a batch of the following atomic tasks — download from Oracle to file; upload the file to S3; and copy it from S3 to Redshift.
This approach allows for easy addition and/or deletion of tasks. For example, the task of archiving the data can easily be included — assuming the 'archive' functionality is available in the framework — into the batch.
A few days after demonstrating the offloading functionality, the account team came back with the question: "What about the security of the downloaded data? Can it be encrypted?"
Given the modular nature of the framework, the obvious answer was: "Yes, it is possible to encrypt the data." While the encryption functionality was not available at the time of the demonstration, it was natural for me to look at ways of incorporating this feature into the framework.
While I know the concepts of encryption, this exploration helped me understand it better and also realize how to do it properly. I plan to share my experience of this exploration using a series of articles on the topic of encryption. Please note that I will be mentioning the usage of a few techniques (along with code) without delving into any of the encryption algorithms.
The simplest and most easily understood technique is the use of symmetric encryption. So, without further ado, let's take a closer look.
Symmetric Encryption
Symmetric encryption is one of the easiest methods (not in the sense of encryption, but usage and understanding) to implement — given that Java provides a lot of the basic stuff needed to do this. In symmetric encryption, we use a key (obviously kept secret) to encrypt the data that needs to be kept secret. To get back the original text, we need to decrypt it again using the same key. As the same key is used for encryption and decryption, this method is called symmetric encryption/decryption.
Let us consider an example. Suppose Alice and Box want to communicate secretly. Before sending messages, they will have to agree on a secret key that can be used to exchange messages. Now, if Alice wants to exchange secret messages with Linda, Alice and Linda have to agree on another secret key to exchange their messages. Thus, each pair of people/machines/businesses that wish to exchange secret messages will have to create a unique secret key between themselves. This creates a problem in that the number of keys that need to be created and maintained will be very large in number.
Sample application:
package eds;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class EncDecSymmetric
{
// Symmetric encryption algorithms supported - AES, RC4, DES
protected static String DEFAULT_ENCRYPTION_ALGORITHM = "AES";
protected static int DEFAULT_ENCRYPTION_KEY_LENGTH = 256;
protected SecretKey mSecretKey;
protected String mEncryptionAlgorithm, mKeyEncryptionAlgorithm, mTransformation;
protected int mEncryptionKeyLength, mKeyEncryptionKeyLength;
protected PublicKey mPublicKey;
protected PrivateKey mPrivateKey;
EncDecSymmetric()
{
mSecretKey = null;
mEncryptionAlgorithm = EncDecSymmetric.DEFAULT_ENCRYPTION_ALGORITHM;
mEncryptionKeyLength = EncDecSymmetric.DEFAULT_ENCRYPTION_KEY_LENGTH;
}
public static BigInteger keyToNumber(byte[] byteArray)
{
return new BigInteger(1, byteArray);
}
public SecretKey getSecretKey()
{
return mSecretKey;
}
public byte[] getSecretKeyAsByteArray()
{
return mSecretKey.getEncoded();
}
public String getEncodedPublicKey()
{
String encodedKey = Base64.getEncoder().encodeToString(mPublicKey.getEncoded());
return encodedKey;
}
// get base64 encoded version of the key
public String getEncodedSecretKey()
{
String encodedKey = Base64.getEncoder().encodeToString(mSecretKey.getEncoded());
return encodedKey;
}
public void generateSymmetricKey()
{
KeyGenerator generator;
try {
generator = KeyGenerator.getInstance(mEncryptionAlgorithm);
generator.init(mEncryptionKeyLength);
mSecretKey = generator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public byte[] encryptText(String textToEncrypt)
{
byte[] byteCipherText = null;
try {
Cipher encCipher = Cipher.getInstance(mEncryptionAlgorithm);
encCipher.init(Cipher.ENCRYPT_MODE, mSecretKey);
byteCipherText = encCipher.doFinal(textToEncrypt.getBytes());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return byteCipherText;
}
public String decryptText(byte[] decryptedKey, byte[] encryptedText)
{
String decryptedPlainText = null;
try {
SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey.length, mEncryptionAlgorithm);
Cipher aesCipher2 = Cipher.getInstance(mEncryptionAlgorithm);
aesCipher2.init(Cipher.DECRYPT_MODE, originalKey);
byte[] bytePlainText = aesCipher2.doFinal(encryptedText);
decryptedPlainText = new String(bytePlainText);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return decryptedPlainText;
}
}
The way to use the above class is by:
package eds;
import javax.crypto.SecretKey;
public class Main
{
public static void main(String[] args)
{
EncDecSymmetric sed = new EncDecSymmetric();
sed.generateSymmetricKey();
byte[] secretKeyByteArray = sed.getSecretKeyAsByteArray();
System.out.println("secret key: '" + EncDecSymmetric.keyToNumber(secretKeyByteArray).toString() + "'" );
String plainText = "Hello World, Symmetric Encryption style";
System.out.println("plainText: '" + plainText + "'");
byte[] encryptedText = sed.encryptText(plainText);
System.out.println("encrypted text: '" + EncDecSymmetric.keyToNumber(encryptedText).toString() + "'" );
String decryptedText = sed.decryptText(secretKeyByteArray, encryptedText);
System.out.println("decrypted text: '" + decryptedText + "'" );
}
}
Keeping the encryption key a secret is the biggest challenge when using symmetric encryption. If the encrypted messages need to be processed by a large number of people or applications, the same key needs to be shared with each person and/or application. This increases the chances of the key being compromised. Once a key is compromised, each and every message or data block encrypted using this key is also comporimised. The only way to overcome this loss is the identify each encrypted message and/or data value and encrypt it using a different key.
In the next part, I will share how symmetric encryption can be used for encrypting large files. Stay tuned!
Opinions expressed by DZone contributors are their own.
Trending
-
What to Pay Attention to as Automation Upends the Developer Experience
-
From CPU to Memory: Techniques for Tracking Resource Consumption Over Time
-
Personalized Code Searches Using OpenGrok
-
DevOps in Legacy Systems
Comments