・ ** 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 **.
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)
OpenSSL-kompatible Verschlüsselung mit Java / PHP erreichen
6 oder später. Dieser Artikel entspricht vorerst 6.
Da wir Base64 verwenden, verwenden wir ApacheCommonsCodec.
Zunächst möchte ich mit JAXB konvertieren, es handelt sich also um eine Eigenschaftendatei im XML-Format.
Benutzerdefinierte Anmerkungen für Beans.
Eine Bean, die die Werte in der Eigenschaftendatei enthält.
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".
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]
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 {}
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;
}
}
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);
}
}
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.
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