[JAVA] Entschlüsseln Sie die gesamte Bean in einer Eigenschaftendatei mit einigen verschlüsselten Werten

Status

・ ** Wir entwickeln mit JavaVM (6 oder höher) **, was weit vom neuesten Stand entfernt ist. -In der Eigenschaftendatei ** werden einige Elementwerte wie "Passwort" mit OpenSSL (Base64) ** verschlüsselt. -Die zu entschlüsselnden Zielobjekte können sich erhöhen oder verringern **. -Ich möchte die zu entschlüsselnden Elemente nicht einzeln entschlüsseln **.

Zweck

Ich möchte mich nicht um die Entschlüsselung kümmern. Ich möchte eine Bean so betreiben, als wäre sie von Anfang an im Klartext definiert. Mit anderen Worten, wenn Sie Dawn mit Bean machen, möchten Sie es mit Pan verwenden. (Wortschatz)

Artikel, die ich als Referenz verwendet habe

OpenSSL-kompatible Verschlüsselung mit Java / PHP erreichen

Java-Version

6 oder später. Dieser Artikel entspricht vorerst 6.

Bibliothek verwendet

Da wir Base64 verwenden, verwenden wir ApacheCommonsCodec.

Charaktere (Dinge vorzubereiten)

Eigenschaftendatei

Zunächst möchte ich mit JAXB konvertieren, es handelt sich also um eine Eigenschaftendatei im XML-Format.

Verschlüsselte Anmerkung

Benutzerdefinierte Anmerkungen für Beans.

PropertyBean-Klasse

Eine Bean, die die Werte in der Eigenschaftendatei enthält.

EncDecConverter-Klasse

Wie der Titel schon sagt, ist es eine Klasse, die dich zu einer Pfanne macht, wenn du es mit Dawn machst.

Das sind alle von Java verwendeten Ressourcen. Bereiten Sie separat das Hauptkennwort vor, das für die Ver- / Entschlüsselung in Ihrem Gehirn verwendet wird. Die Verschlüsselungsmethode ist "128-Bit-Schlüssel-AES-CBC-Modus".

Trainieren

1. 1. Erstellen Sie eine Eigenschaftendatei.

Das Entschlüsselungsziel ist "xxUser" und "xxPass". Platzieren Sie es in einem Verzeichnis mit einem Klassenpfad.

property.xml


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
	<!--Entschlüsselungsziel "xxUser"-->
	<!--Es ist eine Zeichenkette, in der der Klartext "hogeyama" mit dem Passwort "yakisoba" verschlüsselt ist.-->
	<xxUser>U2FsdGVkX1/k+6dPcXrci4AbyQ0TNtytubkVFCxzcF4=</xxUser>
	<!--Entschlüsselungsziel "xxPass"-->
	<!--Es ist eine Zeichenfolge, in der der Klartext "hogeyamano password" mit dem Passwort "yakisoba" verschlüsselt ist.-->
	<xxPass>U2FsdGVkX19XLVe01kx2ahoKVKnSXLhBQ2aiRrdUlbjgtKu1IXD3EuYDSADab5vA</xxPass>
	<!--Klartext-->
	<miscItem1>hoge</miscItem1>
	<!--Klartext-->
	<miscItem2>fuga</miscItem2>
</root>

Erstellen Sie die tatsächlich verschlüsselte Zeichenfolge mit dem Befehl openssl (unten) oder der obigen Referenz-URL.

bash


$Echo [Klartext]| openssl enc -e -aes-128-cbc -base64 -k [Hauptkennwort]
2. Erstellen Sie eine verschlüsselte Anmerkung.

Verschlüsselte Anmerkung


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Encrypted {}
3. 3. Erstellen Sie eine PropertyBean.

Fügen Sie zu diesem Zeitpunkt dem Feld, das den verschlüsselten Wert enthält, eine verschlüsselte Anmerkung hinzu.

PropertyBean



public class PropertyBean {
	@Encrypted
	private String xxUser = null;
	@Encrypted
	private String xxPass= null;
	private String miscItem1 = null;
	private String miscItem2 = null;

	public String getXxUser() {
		return xxUser;
	}
	public void setXxUser(String xxUser) {
		this.xxUser = xxUser;
	}
	public String getXxPass() {
		return xxPass;
	}
	public void setXxPass(String xxPass) {
		this.xxPass = xxPass;
	}
	public String getMiscItem1() {
		return miscItem1;
	}
	public void setMiscItem1(String miscItem1) {
		this.miscItem1 = miscItem1;
	}
	public String getMiscItem2() {
		return miscItem2;
	}
	public void setMiscItem2(String miscItem2) {
		this.miscItem2 = miscItem2;
	}

}
4. Die EncDecConverter-Klasse lautet wie folgt.

EncDecConverter-Klasse


import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


public class EncDecConverter<T> {
	private T t = null;
	private String password = null;
	private Charset charset = null;

	@SuppressWarnings("unused")
	private EncDecConverter() {}

	public EncDecConverter(T t,String password){
	    this.t = t;
	    this.password = password;
	    charset = Charset.defaultCharset();
	}

	public EncDecConverter(T t,String password,String charsetName){
	    this.t = t;
	    this.password = password;
	    charset = Charset.forName(charsetName);
	}

	public boolean decrypt(){
		return convert(true);
	}

	public boolean encrypt(){
		return convert(false);
	}

	private boolean convert(boolean processDecrypt){
	    if(t == null || password == null){
	        return false;
	    }

	    Field[] fs = t.getClass().getDeclaredFields();
	    String value = "";
	    try {
		    for(Field f : fs){
		        f.setAccessible(true);
		        if(f.getAnnotation(Encrypted.class) != null){
		        	value = (processDecrypt?decrypt((String)f.get(t),password):encrypt((String)f.get(t),password));
		            f.set(t,removeLineSeparator(value));
		        }
		    }
	    }catch(Throwable e) {
	    	e.printStackTrace();
	    	return false;
	    }
	    return true;
	}

	private String removeLineSeparator(String s) {
		if(s == null) {
			return "";
		}
		return s.replaceAll("[\r]*[\n]*$", "");
	}

    private boolean getKeyAndGenerateIv(String password, byte[] salt, byte[] key_bytes, byte[] iv_bytes) {
        byte[] password_bytes = password.getBytes(charset);
        int length = password_bytes.length + salt.length;
        ByteBuffer byte_buffer = ByteBuffer.allocate(length);
        byte_buffer.put(password_bytes);
        byte_buffer.put(salt);
        byte_buffer.rewind();
        byte[] byte_array = new byte[length];
        byte_buffer.get(byte_array);
        try {
        	System.arraycopy(MessageDigest.getInstance("MD5").digest(byte_array), 0, key_bytes, 0, key_bytes.length);
        }catch (NoSuchAlgorithmException e ) {
        	e.printStackTrace();
        	return false;
        }
        length = password_bytes.length + salt.length + key_bytes.length;
        byte_buffer = ByteBuffer.allocate(length);
        byte_buffer.put(key_bytes);
        byte_buffer.put(password_bytes);
        byte_buffer.put(salt);
        byte_buffer.rewind();
        byte_array = new byte[length];
        byte_buffer.get(byte_array);
        try {
        	System.arraycopy(MessageDigest.getInstance("MD5").digest(byte_array), 0, iv_bytes, 0, iv_bytes.length);
        }catch (NoSuchAlgorithmException e ) {
        	e.printStackTrace();
        	return false;
        }

        return true;
    }

    private String encrypt(String plaintext, String password) throws Throwable{
        // Generate random salt.
        byte[] random_bytes = new byte[8];
        new SecureRandom().nextBytes(random_bytes);

        byte[] key_bytes = new byte[16];
        byte[] iv_bytes = new byte[16];
        getKeyAndGenerateIv(password, random_bytes, key_bytes, iv_bytes);

        SecretKey secret = new SecretKeySpec(key_bytes, "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv_bytes);
        Cipher cipher = null;
        try {
        	cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        }catch(NoSuchPaddingException e) {
        	throw e;
        }catch(NoSuchAlgorithmException e) {
        	throw e;
        }

        try {
        	cipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
        }catch(InvalidKeyException e) {
        	throw e;
        }catch(InvalidAlgorithmParameterException e) {
        	throw e;
        }

        byte[] encrypted_bytes;
        try {
        	encrypted_bytes = cipher.doFinal(plaintext.getBytes(charset));
	    }catch(IllegalBlockSizeException e) {
	    	throw e;
	    }catch(BadPaddingException e) {
	    	throw e;
	    }

        final String header_string = "Salted__";
        byte[] header_bytes = header_string.getBytes(charset);
        int length = header_bytes.length + random_bytes.length + encrypted_bytes.length;
        ByteBuffer byte_buffer = ByteBuffer.allocate(length);
        byte_buffer.put(header_bytes);
        byte_buffer.put(random_bytes);
        byte_buffer.put(encrypted_bytes);
        byte_buffer.rewind();
        byte[] byte_array = new byte[length];
        byte_buffer.get(byte_array);

        return new String(Base64.encodeBase64(byte_array));
        //Für Java 8 oder höher kann dies wie folgt beschrieben werden.
        //return new String(Base64.getEncoder().encodeToString(byte_array));
    }

    private String decrypt(String payload, String password) throws Throwable{
    	byte[] payload_bytes = Base64.decodeBase64(payload.getBytes(charset));
        //Für Java 8 oder höher kann dies wie folgt beschrieben werden.
        //byte[] payload_bytes = Base64.getDecoder().decode(payload.getBytes(StandardCharsets.UTF_8));
        byte[] header_bytes = new byte[8];
        byte[] salt_bytes = new byte[8];
        int length = payload_bytes.length;
        ByteBuffer byte_buffer = ByteBuffer.allocate(length);
        byte_buffer.put(payload_bytes);
        byte_buffer.rewind();
        byte_buffer.get(header_bytes);
        byte_buffer.get(salt_bytes);
        length = payload_bytes.length - header_bytes.length - salt_bytes.length;
        byte[] data_bytes = new byte[length];
        byte_buffer.get(data_bytes);

        byte[] key_byte = new byte[16];
        byte[] iv_bytes = new byte[16];
        getKeyAndGenerateIv(password, salt_bytes, key_byte, iv_bytes);

        SecretKey secret = new SecretKeySpec(key_byte, "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv_bytes);
        Cipher cipher = null;
        try {
        	cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        }catch(NoSuchPaddingException e) {
        	throw e;
        }catch(NoSuchAlgorithmException e) {
        	throw e;
        }

        try {
        	cipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
        }catch(InvalidKeyException e) {
        	throw e;
        }catch(InvalidAlgorithmParameterException e) {
        	throw e;
        }

        byte[] decrypted;
        try {
        	decrypted = cipher.doFinal(data_bytes);
        }catch(IllegalBlockSizeException e) {
        	throw e;
        }catch(BadPaddingException e) {
        	throw e;
        }

        return new String(decrypted);
    }
}
5. Erstellen Sie eine Testklasse und überprüfen Sie die Operation.

EncDecTest


import java.io.InputStream;

import javax.xml.bind.JAXB;

public class EncDecTest {
	public static void main(String[] args) {
		EncDecTest t = new EncDecTest();
		t.execute();
	}

	public void execute() {
		InputStream is = this.getClass().getClassLoader().getResourceAsStream("property.xml");
		PropertyBean prop = JAXB.unmarshal(is, PropertyBean.class);
		EncDecConverter<PropertyBean> c = new EncDecConverter<PropertyBean>(prop,"yakisoba","UTF-8");
		if(!c.decrypt()) {
			System.err.println("error.");
			System.exit(1);
		}
		System.out.println(prop.getXxUser()); //Ausgabe hogeyama
		System.out.println(prop.getXxPass()); //Hogeyamano Passwort ausgeben
		System.out.println(prop.getMiscItem1()); //Ausgabe hoge
		System.out.println(prop.getMiscItem2()); //Fuga ausgeben
		System.exit(0);
	}
}

Wenn das Ergebnis der Entschlüsselungsmethode wahr ist In der PropertyBean-Instanz als Argument übergeben ** Das zu entschlüsselnde Feld ** wurde entschlüsselt. Alles was Sie tun müssen, ist es aus der Bohne zu holen und es normal zu verwenden.

Wie oben erwähnt, als Argument für den EncDecConverter-Konstruktor Sie müssen das Hauptkennwort im Klartext übergeben. In einem ersten Schritt ** Wie soll das Master-Passwort verwaltet und wie soll I / F durchgeführt werden? ** ** ** Wird in diesem Artikel nicht erwähnt.

Zusammenfassung

Ich denke, es gibt auch ein Muster, in dem OpenSSL-verschlüsselte Zeichenfolgen teilweise anstelle von "ganzem Text" verwendet werden. (Abgesehen davon, ob es die richtige Antwort ist) Also habe ich versucht, solche Tipps zusammenzufassen. Ich denke auch nicht, dass es gut ist, Reflexion schlecht zu verwenden, aber ich benutze es, weil es praktisch ist.

Recommended Posts

Entschlüsseln Sie die gesamte Bean in einer Eigenschaftendatei mit einigen verschlüsselten Werten
Erstellen Sie mit dem Befehl eine JAR-Datei
Lesen Sie eine Zeichenfolge in einer PDF-Datei mit Java
Speichern von Dateien mit der angegebenen Erweiterung unter dem in Java angegebenen Verzeichnis in der Liste
Als ich zu IntelliJ wechselte, gab es einen großen Unterschied in der Codierung der Eigenschaftendatei.
Fügen Sie die Datei in die Eigenschaften eines Strings mit Spring XML-Konfiguration ein
Überschreiben Sie die Bean-Definition in der Spring-XML-Konfigurationsdatei mit einer anderen XML
Informationen zum Verhalten beim Erstellen einer Dateizuordnung mit Java
Finden Sie mit Kotlin die Anzahl der Tage in einem Monat
So erstellen Sie eine JAR-Datei ohne Abhängigkeiten in Maven
Lesen Sie die Datei unter dem Klassenpfad als Zeichenfolge mit spring
Wird in Spring Boot mithilfe einer Bean-Definitionsdatei mit dem Namen application.xml angepasst
Rufen Sie die öffentliche URL der privaten Datei von Flickr in Java ab