HOWTO · Java
Encriptar palavra-passe em ficheiros de configuração em Java
Este artigo apresenta como criptografar senha em arquivos 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,iterationCountekeyLength.passwordé a senha real no arquivo de configuração. Na criptografia, umsaltsã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áveliterationCountcomo 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
getInstancepercorre 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çaNullPointerExceptionse o algoritmo especificado for nulo eNoSuchAlgorithmExceptionse nenhum Provedor suportar uma implementaçãoSecretKeyFactorySpipara o algoritmo especificado. PBEKeySpecé um construtor de classe que pega uma senha, salt, contagem de iteração e comprimento de chave derivada para gerarPBEKeyde cifras PBE de tamanho de chave variável. Ele lançaNullPointerExceptionsesaltfornulleIllegalArgumentExceptionse sal estiver vazio.generateSecretgera um objetoSecretKeya partir da especificação-chave fornecida ou do material-chave. Leva a especificação da chave secreta. Ele lançaInvalidKeySpecExceptionse 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
encryptleva 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
getInstancepercorre 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çaNoSuchAlgorithmExceptionse uma alteração for nula, vazia, em um formato inválido eNoSuchPaddingExceptionse a alteração contiver um esquema de preenchimento não disponível. - O método
initinicializa oCipherpara uma das seguintes quatro operações: criptografia, descriptografia, quebra de chave ou desempacotamento de chave, dependendo do valor do modo de operação.ENCRYPT_MODEno nosso caso. O método lançaUnsupportedOperationExceptionse o modo de operação for inválido eInvalidKeyExceptionse a chave fornecida for inadequada. - O
getParametersretorna os parâmetros usados com esta cifra. - O
getParameterSpecretorna uma especificação do objeto de parâmetro. O parâmetroparamSpecidentifica a classe de especificação na qual os parâmetros devem retornar. Por exemplo, poderia serDSAParameterSpec.classpara indicar que os parâmetros devem retornar em uma instância da classeDSAParameterSpec. - O método
doFinalcriptografa 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étododescriptografarsão semelhantes ao método mencionado acima. A única diferença é que eles se comportam de maneira diferente com base nomodeespecificado na função,DECRYPT_MODEcomo 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