Criptografar senha em arquivos de configuração?

Eu tenho um programa que lê informações do servidor de um arquivo de configuração e gostaria de criptografar a senha nessa configuração que pode ser lida pelo meu programa e descriptografada.

Requisitos:

  • Criptografar senha em texto simples para ser armazenada no arquivo
  • Descriptografar a senha criptografada do arquivo do meu programa

Quaisquer recomendações sobre como eu iria fazer isso? Eu estava pensando em escrever meu próprio algoritmo, mas acho que seria terrivelmente inseguro.

Uma maneira simples de fazer isso é usar Criptografia Baseada em Senha em Java. Isso permite criptografar e descriptografar um texto usando uma senha.

Isso basicamente significa inicializar um javax.crypto.Cipher com o algoritmo "AES/CBC/PKCS5Padding" e obter uma chave de javax.crypto.SecretKeyFactory com o algoritmo "PBKDF2WithHmacSHA512" .

Aqui está um exemplo de código (atualizado para replace a variante baseada em MD5 menos segura):

 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 ProtectedConfigFile { public static void main(String[] args) throws Exception { String password = System.getProperty("password"); if (password == null) { throw new IllegalArgumentException("Run with -Dpassword="); } // The salt (probably) can be stored along with the encrypted data byte[] salt = new String("12345678").getBytes(); // Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers int iterationCount = 40000; // Other values give me java.security.InvalidKeyException: Illegal key size or default parameters int keyLength = 128; SecretKeySpec key = createSecretKey(password.toCharArray(), salt, iterationCount, keyLength); String originalPassword = "secret"; 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); } private 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"); } private static String encrypt(String property, 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(property.getBytes("UTF-8")); byte[] iv = ivParameterSpec.getIV(); return base64Encode(iv) + ":" + base64Encode(cryptoText); } private static String base64Encode(byte[] bytes) { return Base64.getEncoder().encodeToString(bytes); } private 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); } } 

Um problema permanece: onde você deve armazenar a senha que você usa para criptografar as senhas? Você pode armazená-lo no arquivo de origem e ofuscá-lo, mas não é muito difícil encontrá-lo novamente. Como alternativa, você pode -DpropertyProtectionPassword=... lo como uma propriedade do sistema ao iniciar o processo Java ( -DpropertyProtectionPassword=... ).

O mesmo problema permanece se você usar o KeyStore, que também é protegido por uma senha. Basicamente, você precisará ter uma senha mestra em algum lugar, e é muito difícil de proteger.

Sim, definitivamente não escreva seu próprio algoritmo. Java tem muitas APIs de criptografia.

Se o SO em que você está instalando tiver um keystore, você poderá usá-lo para armazenar as chaves de criptografia necessárias para criptografar e descriptografar os dados confidenciais em sua configuração ou outros arquivos.

Confira o jasypt , que é uma biblioteca que oferece resources básicos de criptografia com o mínimo de esforço.

Eu acho que a melhor abordagem é garantir que seu arquivo de configuração (contendo sua senha) seja acessível apenas para uma conta de usuário específica . Por exemplo, você pode ter um usuário do aplicativo específico do aplicativo para o qual apenas pessoas confiáveis ​​tenham a senha (e para a qual elas su ).

Dessa forma, não há sobrecarga de criptografia irritante e você ainda tem uma senha segura.

EDIT: Estou assumindo que você não está exportando sua configuração de aplicativo fora de um ambiente confiável (que não tenho certeza faria qualquer sentido, dada a pergunta)

Bem, para resolver os problemas de senha mestra – a melhor abordagem não é armazenar a senha em qualquer lugar, o aplicativo deve criptografar senhas para si mesmo – de modo que apenas possa descriptografá-las. Então, se eu estivesse usando um arquivo .config, faria o seguinte, mySettings.config :

encryptTheseKeys = secretKey, anotherSecret

secretKey = unprotectedPasswordThatIputHere

anotherSecret = anotherPass

someKey = unprotectedSettingIdontCareAbout

então eu iria ler as chaves que são mencionadas no encryptTheseKeys, aplicar o exemplo Brodwalls de cima nelas e gravá-las de volta no arquivo com um marcador de algum tipo (digamos crypt 🙂 para permitir que o aplicativo não o faça novamente, a saída ficaria assim:

encryptTheseKeys = secretKey, anotherSecret

secretKey = cripta: ii4jfj304fjhfj934fouh938

anotherSecret = cripta: jd48jofh48h

someKey = unprotectedSettingIdontCareAbout

Apenas certifique-se de manter os originais em seu próprio lugar seguro …

O ponto principal, e o elefante na sala e tudo mais, é que, se o seu aplicativo conseguir se apossar da senha, um hacker com access à checkbox também poderá se apossar dela!

A única maneira de contornar isso é que o aplicativo solicita a “senha mestra” no console usando a input padrão e, em seguida, usa isso para descriptografar as senhas armazenadas no arquivo. É claro que isso faz com que seja impossível fazer com que o aplicativo seja iniciado sem supervisão junto com o sistema operacional quando ele é inicializado.

No entanto, mesmo com este nível de aborrecimento, se um hacker consegue obter access root (ou até mesmo acessar como o usuário que está executando o aplicativo), ele poderia despejar a memory e encontrar a senha lá.

O importante é garantir que a empresa inteira não tenha access ao servidor de produção (e, portanto, às senhas), e certifique-se de que é impossível quebrar essa checkbox!

Tente usar os methods de criptografia ESAPIs. É fácil de configurar e você também pode facilmente mudar suas chaves.

http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html

Você

1) criptografar 2) descriptografar 3) assinar 4) não assinar 5) hashing 6) assinaturas baseadas no tempo e muito mais com apenas uma biblioteca.

Veja o que está disponível no Jetty para armazenar senhas (ou hashes) em arquivos de configuração e considere se a codificação OBF pode ser útil para você. Então veja na fonte como isso é feito.

http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html

Dependendo de quão seguro você precisa dos arquivos de configuração ou da confiabilidade do seu aplicativo, http://activemq.apache.org/encrypted-passwords.html pode ser uma boa solução para você.

Se você não tem medo de que a senha seja descriptografada e pode ser muito simples configurar usando um bean para armazenar a chave de senha. No entanto, se você precisar de mais segurança, poderá definir uma variável de ambiente com o segredo e removê-la após o lançamento. Com isso, você tem que se preocupar com o aplicativo / servidor indo para baixo e não o aplicativo não é automaticamente o relançamento.

Se você estiver usando o java 8, o uso do codificador e decodificador interno Base64 pode ser evitado substituindo

return new BASE64Encoder().encode(bytes);

com

return Base64.getEncoder().encodeToString(bytes);

e

return new BASE64Decoder().decodeBuffer(property);

com

return Base64.getDecoder().decode(property);

Observe que essa solução não protege seus dados, pois os methods de descriptografia são armazenados no mesmo local. Isso só torna mais difícil de quebrar. Principalmente evita imprimi-lo e mostrá-lo a todos por engano.