I am implementing a program to encrypt string using AES encryption and would like to save my "secret key" in a file instead of hardcoding it in the source code.
But, it creates a problem for me, is how do I protect this secret key from viewed by others?
If I were to encrypt this "keyFile" again, I would have to deal with the same problem again.
How do I deal with such issues?
String keyFile = ...;
byte[] keyb = Files.readAllBytes(Paths.get(keyFile));
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
class Msc61 {
public static SecretKey generateKey() {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
return kgen.generateKey();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e.toString());
}
}
public static byte[] encrypt_cbc(SecretKey skey, String plaintext) {
/* Precond: skey is valid; otherwise IllegalStateException will be thrown. */
try {
byte[] ciphertext = null;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
byte[] initVector = new byte[blockSize];
(new SecureRandom()).nextBytes(initVector);
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
cipher.init(Cipher.ENCRYPT_MODE, skey, ivSpec);
byte[] encoded = plaintext.getBytes(java.nio.charset.StandardCharsets.UTF_8);
ciphertext = new byte[initVector.length + cipher.getOutputSize(encoded.length)];
for (int i=0; i < initVector.length; i++) {
ciphertext[i] = initVector[i];
}
// Perform encryption
cipher.doFinal(encoded, 0, encoded.length, ciphertext, initVector.length);
return ciphertext;
} catch (NoSuchPaddingException | InvalidAlgorithmParameterException | ShortBufferException |
BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchAlgorithmException e)
{
/* None of these exceptions should be possible if precond is met. */
throw new IllegalStateException(e.toString());
}
}
public static String decrypt_cbc(SecretKey skey, byte[] ciphertext)
throws BadPaddingException, IllegalBlockSizeException /* these indicate corrupt or malicious ciphertext */
{
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
byte[] initVector = Arrays.copyOfRange(ciphertext, 0, blockSize);
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
cipher.init(Cipher.DECRYPT_MODE, skey, ivSpec);
byte[] plaintext = cipher.doFinal(ciphertext, blockSize, ciphertext.length - blockSize);
return new String(plaintext);
} catch (NoSuchPaddingException | InvalidAlgorithmParameterException |
InvalidKeyException | NoSuchAlgorithmException e)
{
/* None of these exceptions should be possible if precond is met. */
throw new IllegalStateException(e.toString());
}
}
}
Ref: https://wiki.sei.cmu.edu/confluence/display/java/MSC61-J.+Do+not+use+insecure+or+weak+cryptographic+algorithms
You have several options to do this.
If you actually need to encrypt your key, you can use a JCEKS keystore to store it in an encrypted form. This keystore is well suited or, better said, can be used, for storing a single key. You can see an example of how to use it in this DZone article. It will give you an explanation about what is going on under the hoods, and will provide you some background about this kind of keystore.
You can also encrypt directly your key by using a Password Based Encryption algorithm. This stackoverflow question will provide you a good example of hoe to implement this kind of solution.
If your requirement is only encrypt and decrypt properties in a properties file as you indicated in your comments, you can use Jasypt. This library is in fact a wrapper of several cryptographic solutions.
As you can see in their documentation, this library will provide you some utilities for a general use case.
Consider the following example, obtained from the links above, and very well suited for your needs. They provide a properties file:
datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://localhost/reportsdb
datasource.username=reportsUser
datasource.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)
And they show how to read this properties:
/*
* First, create (or ask some other component for) the adequate encryptor for
* decrypting the values in our .properties file.
*/
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("jasypt"); // could be got from web, env variable...
encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256");
encryptor.setIvGenerator(new RandomIvGenerator());
/*
* Create our EncryptableProperties object and load it the usual way.
*/
Properties props = new EncryptableProperties(encryptor);
props.load(new FileInputStream("/path/to/my/configuration.properties"));
/*
* To get a non-encrypted value, we just get it with getProperty...
*/
String datasourceUsername = props.getProperty("datasource.username");
/*
* ...and to get an encrypted value, we do exactly the same. Decryption will
* be transparently performed behind the scenes.
*/
String datasourcePassword = props.getProperty("datasource.password");
// From now on, datasourcePassword equals "reports_passwd"...
If you are using Spring, it will also give you a great framework integration.
As you can see, independently of the solution adopted, you will always need a password that actually protect your key.
The best way you can provide this password to the different mechanisms described above is by defining it as an environment variable: the idea is that this information will be visible only for systems administrator and the IT people responsible of the maintenance of your server software.
On the other hand, all the major Cloud providers (AWS, GCP, Azure, ...) will give you services (KMS for the first two, and Key Vault for Azure) that allows you to safely encrypt and decrypt information even without having access to the actual key: in these scenarios the use of these services probably will be the better approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With