Encriptar palavra-passe em ficheiros de configuração em Java
Criptografia é o processo de transformar informações de texto simples em uma forma ilegível usando um algoritmo de criptografia combinado com um parâmetro denominado chave de criptografia
. O formato ilegível é geralmente conhecido como formato de ciphertext
. Somente aqueles que possuem a chave de descriptografia
podem descriptografar os dados e recuperar o texto original.
Podemos dividir o problema de criptografar senhas em arquivos de configuração em duas subtarefas a seguir.
- Criptografe a senha em texto simples que está no arquivo.
- Descriptografe a senha criptografada lida do arquivo.
Vamos primeiro criar um arquivo de configuração denominado arquivo config.properties
no caminho src/conf/
.
password = TestPassword123
Agora, para ler o arquivo de configuração, instancie a classe Properties
. Podemos criar uma instância da classe FileInputStream
usando seu construtor. Ele pega o caminho do arquivo de configuração como sua entrada. Agora, uma instância da classe de propriedades é usada para carregar as propriedades. Use o método load
para carregar o arquivo de propriedades na classe, e isso leva a instância InputStreamReader
como um parâmetro. Ele lança IllegalArgumentException
se este fluxo de entrada contém uma sequência de escape Unicode malformada e IOException
se um erro ocorreu durante a leitura do fluxo de entrada.
Depois que as propriedades forem carregadas com êxito, use o método getProperty()
para pesquisar a propriedade com a chave especificada na lista de propriedades. O método retorna null
se não for capaz de encontrar as propriedades. Faça uma verificação externa para lidar com essa situação e lance IllegalArgumentException
se uma senha for considerada nula no arquivo.
salt
é criado com qualquer string aleatória para adicionar à string da senha.
createSecretKey
é um método definido pelo usuário que retorna a chave SecretKeySpec
, e o uso da chave é para criptografar e descriptografar a senha. Os métodos encrypt
e decrypt
são métodos estáticos definidos pelo uso que foram fornecidos, na classe Encryption
.
Abaixo está o código de exemplo que demonstra o mesmo.
package fileDataEncryption;
import static fileDataEncryption.Encryption.*;
import java.io.FileInputStream;
import java.util.Properties;
import javax.crypto.spec.SecretKeySpec;
public class ConfigFileEncryption {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
FileInputStream inputStream = new FileInputStream("src/conf/config.properties");
properties.load(inputStream);
String password = properties.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("No such parameter present in config file");
}
byte[] salt = new String("12345678").getBytes();
int iterationCount = 40000;
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(), salt, iterationCount, keyLength);
String originalPassword = password;
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}
}
Uma descrição detalhada dos métodos definidos pelo usuário na classe Encryption
é apresentada a seguir.
- O
createSecretKey
é uma função que recebe parâmetros comopassword
,salt
,iterationCount
ekeyLength
.password
é a senha real no arquivo de configuração. Na criptografia, umsalt
são dados aleatórios que usamos como uma entrada adicional que faz o hash dos dados, uma senha ou uma frase secreta. O uso desalts
é para proteger as senhas no armazenamento. Usamos a variáveliterationCount
como o número de iterações que um algoritmo deve realizar. Diminuir o valor das velocidades variáveis diminui o tempo de inicialização e, portanto, são úteis durante o teste, mas também torna mais fácil para atacantes de força bruta. A variávelkeyLength
é o comprimento da chave que precisamos derivar. Lança a exceção lançada a partir dos métodos usados. - O método
getInstance
percorre a lista de Provedores de segurança registrados, começando pelo Provedor preferido. Ele pega o nome padrão do algoritmo de chave secreta solicitado e retorna o novo objetoSecretKeyFactory
. Ele lançaNullPointerException
se o algoritmo especificado for nulo eNoSuchAlgorithmException
se nenhum Provedor suportar uma implementaçãoSecretKeyFactorySpi
para o algoritmo especificado. PBEKeySpec
é um construtor de classe que pega uma senha, salt, contagem de iteração e comprimento de chave derivada para gerarPBEKey
de cifras PBE de tamanho de chave variável. Ele lançaNullPointerException
sesalt
fornull
eIllegalArgumentException
se sal estiver vazio.generateSecret
gera um objetoSecretKey
a partir da especificação-chave fornecida ou do material-chave. Leva a especificação da chave secreta. Ele lançaInvalidKeySpecException
se a especificação fornecida for inadequada para esta fábrica de chave secreta para produzir um valor de chave classificado.
Detalhes do método encrypt
na classe Encryption
.
- O método
encrypt
leva dois parâmetros, dados a serem criptografados e a chave. Este método lança exceções lançadas de métodos filhos nele. - O método
getInstance
percorre a lista de Provedores de segurança registrados, começando pelo Provedor preferido. Leva o nome da transformação, que é AES / CBC / PKCS5Padding. Ele lançaNoSuchAlgorithmException
se uma alteração for nula, vazia, em um formato inválido eNoSuchPaddingException
se a alteração contiver um esquema de preenchimento não disponível. - O método
init
inicializa oCipher
para uma das seguintes quatro operações: criptografia, descriptografia, quebra de chave ou desempacotamento de chave, dependendo do valor do modo de operação.ENCRYPT_MODE
no nosso caso. O método lançaUnsupportedOperationException
se o modo de operação for inválido eInvalidKeyException
se a chave fornecida for inadequada. - O
getParameters
retorna os parâmetros usados com esta cifra. - O
getParameterSpec
retorna uma especificação do objeto de parâmetro. O parâmetroparamSpec
identifica a classe de especificação na qual os parâmetros devem retornar. Por exemplo, poderia serDSAParameterSpec.class
para indicar que os parâmetros devem retornar em uma instância da classeDSAParameterSpec
. - O método
doFinal
criptografa ou descriptografa dados em um trabalho de parte única ou conclui uma operação de várias partes. Os dados são criptografados ou descriptografados, dependendo de como inicializamos a cifra. base64Encode
é um método privado que codifica a matriz de bytes especificada em uma string usando o esquema de codificaçãoBase64
. As funções usadas no métododescriptografar
são semelhantes ao método mencionado acima. A única diferença é que eles se comportam de maneira diferente com base nomode
especificado na função,DECRYPT_MODE
como um modo de operação.
package fileDataEncryption;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Encryption {
public static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount,
int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}
public static String encrypt(String dataToEncrypt, SecretKeySpec key)
throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(dataToEncrypt.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
public static String decrypt(String string, SecretKeySpec key)
throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}
Abaixo está a saída do código escrito para criptografar e descriptografar a senha no arquivo de configuração.
Original password: TestPassword123
Encrypted password: Hy7fbIwpyKgp0oileu+oLg==:WNRknMJz/8u8GmWlCZFPFA==
Decrypted password: TestPassword123
Rashmi is a professional Software Developer with hands on over varied tech stack. She has been working on Java, Springboot, Microservices, Typescript, MySQL, Graphql and more. She loves to spread knowledge via her writings. She is keen taking up new things and adopt in her career.
LinkedIn