Ik heb een programma dat serverinformatie uit een configuratiebestand leest en wil het wachtwoord in die configuratie versleutelen dat door mijn programma kan worden gelezen en ontsleuteld.
Vereisten:
- Versleutel het wachtwoord in platte tekst dat in het bestand moet worden opgeslagen
- Ontsleutel het gecodeerde wachtwoord dat is ingelezen uit het bestand van mijn programma
Enige aanbevelingen over hoe ik dit zou doen? Ik dacht erover om mijn eigen algoritme te schrijven, maar ik denk dat het vreselijk onzeker zou zijn.
Antwoord 1, autoriteit 100%
Een eenvoudige manier om dit te doen is door wachtwoordgebaseerde versleuteling in Java te gebruiken. Hiermee kunt u een tekst coderen en decoderen met een wachtwoord.
Dit betekent in feite het initialiseren van een javax.crypto.Cipher
met algoritme "AES/CBC/PKCS5Padding"
en het verkrijgen van een sleutel van javax.crypto.SecretKeyFactory
met het "PBKDF2WithHmacSHA512"
algoritme.
Hier is een codevoorbeeld (bijgewerkt om de minder veilige op MD5 gebaseerde variant te vervangen):
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=<password>");
}
// 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);
}
}
Er blijft één probleem: waar moet u het wachtwoord opslaan dat u gebruikt om de wachtwoorden te coderen? Je kunt het opslaan in het bronbestand en het verdoezelen, maar het is niet zo moeilijk om het terug te vinden. Als alternatief kunt u het als een systeemeigenschap opgeven wanneer u het Java-proces start (-DpropertyProtectionPassword=...
).
Hetzelfde probleem blijft bestaan als u de KeyStore gebruikt, die ook is beveiligd met een wachtwoord. Kortom, je zult ergens één hoofdwachtwoord moeten hebben, en het is vrij moeilijk te beschermen.
Antwoord 2, autoriteit 12%
Bekijk jasypt, een bibliotheek die met minimale inspanning basisversleutelingsmogelijkheden biedt.
Antwoord 3, autoriteit 11%
Ja, schrijf zeker niet je eigen algoritme. Java heeft veel cryptografie-API’s.
Als het besturingssysteem waarop u installeert een sleutelarchief heeft, kunt u dat gebruiken om uw cryptosleutels op te slaan die u nodig heeft om de gevoelige gegevens in uw configuratie of andere bestanden te versleutelen en ontsleutelen.
Antwoord 4, autoriteit 9%
Ik denk dat de beste aanpak is om ervoor te zorgen dat uw configuratiebestand (met uw wachtwoord) alleen toegankelijk is voor een specifiek gebruikersaccount. U kunt bijvoorbeeld een applicatiespecifieke gebruiker appuser
hebben waarvoor alleen vertrouwde mensen het wachtwoord hebben (en waaraan zij su
toe).
Op die manier is er geen vervelende cryptografie-overhead en heb je nog steeds een veilig wachtwoord.
BEWERK:Ik ga ervan uit dat u uw applicatieconfiguratie niet exporteert buiten een vertrouwde omgeving (waarvan ik niet zeker weet of dit logisch zou zijn, gezien de vraag)
Antwoord 5, autoriteit 2%
Om de problemen met het hoofdwachtwoord op te lossen – de beste aanpak is om het wachtwoord nergens op te slaan, moet de toepassing wachtwoorden voor zichzelf versleutelen – zodat alleen zij ze kan ontsleutelen. Dus als ik een .config-bestand zou gebruiken, zou ik het volgende doen, mySettings.config:
encryptTheseKeys=secretKey,anotherSecret
secretKey=unprotectedPasswordThatIputHere
anotherSecret=anotherPass
someKey=unprotectedSettingIdontCareAbout
dus ik zou de sleutels inlezen die worden genoemd in de
encryptTheseKeys, pas het Brodwalls-voorbeeld van boven erop toe en
schrijf ze terug naar het bestand met een soort markering (laten we zeggen crypt:) om de toepassing te laten weten het niet nog een keer te doen, de uitvoer ziet er als volgt uit:
encryptTheseKeys=secretKey,anotherSecret
secretKey=crypt:ii4jfj304fjhfj934fouh938
anotherSecret=crypt:jd48jofh48h
someKey=unprotectedSettingIdontCareAbout
Zorg er wel voor dat u de originelen op uw eigen veilige plaats bewaart…
Antwoord 6
Probeer de versleutelingsmethoden van ESAPI’s te gebruiken. Het is eenvoudig te configureren en u kunt ook eenvoudig uw sleutels wijzigen.
http://owasp- esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
Jij
1)versleutelen
2) decoderen
3) teken:
4) afmelden
5) hashen
6) op tijd gebaseerde handtekeningen en nog veel meer met slechts één bibliotheek.
Antwoord 7
Bekijk wat er in Jetty beschikbaar is voor het opslaan van wachtwoorden (of hashes) in configuratiebestanden en overweeg of de OBF-codering nuttig voor u kan zijn. Kijk dan in de bron hoe het moet.
http://www.eclipse.org /jetty/documentation/current/configure-security-secure-passwords.html
Antwoord 8
Afhankelijk van hoe veilig je de configuratiebestanden nodig hebt of hoe betrouwbaar je applicatie is, http:// activemq.apache.org/encrypted-passwords.htmlkan een goede oplossing voor je zijn.
Als je niet al te bang bent dat het wachtwoord wordt ontsleuteld en het heel eenvoudig kan zijn om een bean te gebruiken om de wachtwoordsleutel op te slaan. Als u echter meer beveiliging nodig heeft, kunt u een omgevingsvariabele met het geheim instellen en deze na het starten verwijderen. Hiermee hoeft u zich geen zorgen te maken dat de applicatie / server uitvalt en niet dat de applicatie niet automatisch opnieuw wordt gestart.
Antwoord 9
Als u java 8gebruikt, kan het gebruik van de interne Base64-encoder en -decoder worden vermeden door deze te vervangen
return new BASE64Encoder().encode(bytes);
met
return Base64.getEncoder().encodeToString(bytes);
en
return new BASE64Decoder().decodeBuffer(property);
met
return Base64.getDecoder().decode(property);
Houd er rekening mee dat deze oplossing uw gegevens niet beschermt, aangezien de ontsleutelingsmethoden op dezelfde plaats worden opgeslagen. Het maakt het alleen maar moeilijker om te breken. Het vermijdt vooral om het af te drukken en het per ongeluk aan iedereen te laten zien.