對配置文件內的固定內容加密解密


接到一個需求,背景是對公司的各個服務器環境下的配置文件內存有數據庫用戶名,數據庫密碼,因為在配置文件中,許多shell腳本都需要調用配置文件中的數據庫用戶名,密碼,所以一直以明文保存,需求內容就是實現對配置文件內的用戶名,密碼加密,同時加密后要解決shell腳本文件還可以調用到正確的用戶名密碼,因此還需要解密。

因為是在服務器環境下,所以可以編寫java程序,然后將程序打成jar包,通過shell指令調用jar包實現加密與解密。

因為要實現逆向解密,所以沒有采用MD5,只能用比較老的DES或者AES等,這里采用的是DES。

首先給出配置文件的內容,

不便給出全部內容,因此只有部分重要內容,

*********************

*********************

#數據庫用戶名

dbuser=cmis

#數據庫密碼

dbpass=cmis

#用戶名

douser=cmis

#密碼

dopass=cmis

*********************
*********************

假如需要對cmis加密

首先我們需要先實現對一串字符串進行加密與解密,代碼如下

import java.awt.List;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
/**
* 加密解密
*
* doencrypt方法進行加密
* dodencrypt方法進行解密
* @author liuhao
*
*/
public class EncryptAndDencrypt {

private static Cipher cipher ;
private static byte[] result ;
private static SecretKey convertsecretKey;
/**
* 生成DES加密解密key值
* @return
* @throws Exception
*/
public static SecretKey doKey() throws Exception{
//生成key
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
keyGenerator.init(56);
SecretKey secretKey = keyGenerator.generateKey();
byte[] bytesKey = secretKey.getEncoded();

//KEY轉換
DESKeySpec desKeySpec = new DESKeySpec(bytesKey);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKey convertsecretKey = factory.generateSecret(desKeySpec);
return convertsecretKey;
}
/**
* 傳入明文密碼,加密后返回密文
* @param src
* @return
* @throws Exception
*/
public static keyAndSecret doencrypt(String src) throws Exception{
keyAndSecret ks = new keyAndSecret();
convertsecretKey = doKey();
cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
//加密
cipher.init(Cipher.ENCRYPT_MODE, convertsecretKey);
result = cipher.doFinal(src.getBytes());
ks.setKey(convertsecretKey);
ks.setByt(result);
return ks;
}

/**
* 傳入密文,解密后返回明文密碼
* @param byt
* @return
* @throws Exception
*/
public static String dodecrypt(byte[] byt,SecretKey sKey) throws Exception {
try{
cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
//解密
cipher.init(Cipher.DECRYPT_MODE, sKey);
byt = cipher.doFinal(byt);
}catch(Exception e){
throw e;
}
return new String(byt);
}
}

上邊的代碼是具體實現加密解密的部分。傳入字符串后在加密前先會隨機生成一個Key,這個是java提供的,由一個factory生成,這個key十分重要,因為加密時的cipher類調用的init方法只傳入兩個值一個相當於標示作用既Cipher.DECRYPT_MODE,或Cipher.ENCRYPT_MODE,一個為加密標示,一個為解密標示,另一個傳入值就是key。我的代碼是加密一個字符串就會生成一個新的key,因此因為還有解密過程所以這個key需要保存下來,保存方式接下來會說到。字符串加密后的類型為byte[].

同時呢為了簡化代碼,將密文與密鑰封裝:

import javax.crypto.SecretKey;
/**
* 對秘鑰key與相應密文封裝
* @author liuhao
*
*/
public class keyAndSecret {
private SecretKey key;//密鑰
private byte[] byt;//密文

public SecretKey getKey(){
return key;
}
public void setKey(SecretKey key){
this.key = key;
}
public byte[] getByt(){
return byt;
}
public void setByt(byte[] byt){
this.byt = byt;
}
}

接下來是對於配置文件的IO操作了:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;


import com.ibm.misc.BASE64Encoder;

public class DoEncrypt {
private static String file = "/etldata/etl/bin/config_pass.conf";
private static String file2 = "/etldata/etl/bin/key.conf";

public static void main(String[] args) throws Exception {


//字符字節轉換編碼規范
BASE64Encoder enc = new BASE64Encoder();

BufferedReader brkey = new BufferedReader(new InputStreamReader(new FileInputStream(file2)));

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
List list = new ArrayList();
//按行讀取文件內容存入list
//讀取配置文件內容
int t = 0;//定位標記
while(true){
String str = br.readLine();
if(str==null)
break;
list.add(str);

}
br.close();
//讀取密鑰文件內容
List keylist = new ArrayList();
while(true){
String str = brkey.readLine();
if(str==null)
break;
keylist.add(str);
}
brkey.close();
keyAndSecret ks = new keyAndSecret();
//生成密文與相應的密鑰
keylist.clear();

ListIterator ite = list.listIterator();
//遍歷找到對應行
while(ite.hasNext()){
String str0= ite.next().toString();
if(str0.contains(new String("數據庫用戶名".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
String str = (String) list.get(t);
int temp = str.lastIndexOf("=")+1;
ks = EncryptAndDencrypt.doencrypt(str.substring(temp));
list.set(t, "dbuser="+enc.encode(ks.getByt()));//對應1密鑰
keylist.add(0,enc.encode(ks.getKey().getEncoded()));//1密鑰
System.out.println(t+"----"+list.get(t));

while(ite.hasNext()){
String str1 = ite.next().toString();
if(str1.contains(new String("數據庫密碼".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = (String) list.get(t);
ks = EncryptAndDencrypt.doencrypt(str.substring(temp));//對應2密鑰
list.set(t, "dbpass="+enc.encode(ks.getByt()));
keylist.add(1, enc.encode(ks.getKey().getEncoded()));//2密鑰
System.out.println(t+"----"+list.get(t));

while(ite.hasNext()){
String str2 = ite.next().toString();
if(str2.contains(new String("用戶名".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = (String) list.get(t);
ks = EncryptAndDencrypt.doencrypt(str.substring(temp));
list.set(t, "douser="+enc.encode(ks.getByt()));//對應3密鑰
keylist.add(2,enc.encode(ks.getKey().getEncoded()));//3密鑰
System.out.println(t+"----"+list.get(t));

while(ite.hasNext()){
String str3 = ite.next().toString();
if(str3.contains(new String("密碼".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = (String) list.get(t);
ks = EncryptAndDencrypt.doencrypt(str.substring(temp));
list.set(t, "dopass="+enc.encode(ks.getByt()));//對應4密鑰
keylist.add(3,enc.encode(ks.getKey().getEncoded()));//4密鑰
System.out.println(t+"----"+list.get(t));



//密鑰文件路徑
PrintWriter pw1 = new PrintWriter(file2);
for(int i=0;i<keylist.size();i++){

String str2 =keylist.get(i).toString();
pw1.println(str2);

}
pw1.close();
//密文保存
PrintWriter pw = new PrintWriter(file);

for(int i=0;i<list.size();i++){
String str1 =list.get(i).toString();
System.out.println(str1);
pw.println(str1);

}
pw.close();

}
}

其中對於如何key類型的密鑰,與byte[]類型的密文存入文件,就需要轉換成string字符了,並且在解密時能正確的轉換回原來的類型是一個需要特別注意的地方。

這里直接告訴大家byte轉換string不能使用toString()方法,因為toString()是返回表示此 Byte 的值的 String 對象。例如,如果byte[]內容是a1b2c3d4=,string內容也是a1b2c3d4=,這並不是我們需要的,因為在解密時讀取到a1b2c3d4=后轉換為byte[]時結果就不是a1b2c3d4=了。

因此需要BASE64的出場了。調用的encode方法可以將byte[]對象轉換成字符串(這里返回的對象不是byte的值,而是真正意義上的轉換值),在解密時調用BASE64Decoder的decodeBuffer方法就能將字符串轉換BASE64Encoder回我們原先的byte[]類型了。

同理,key類型用方法getEncoded()可以將key類型轉換為byte,再使用BASE64Encoder轉換為字符串存儲即可。

解密代碼:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.ibm.misc.BASE64Decoder;

public class DoDencypt {

/**
* @param args
*/
private static String file = "/etldata/etl/bin/config_pass.conf";//配置文件路徑
private static String file2 = "/etldata/etl/bin/key.conf";

public static String JM(String str) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

BufferedReader brkey = new BufferedReader(new InputStreamReader(new FileInputStream(file2)));

//讀取配置文件內容
List list = new ArrayList();

while(true){
String str1 = br.readLine();
if(str1==null)
break;
list.add(str1);
}
br.close();
//讀取密鑰文件內容
List keylist = new ArrayList();

while(true){
String str1 = brkey.readLine();
if(str1==null)
break;
keylist.add(str1);
}
brkey.close();
SecretKey key = null;
byte[] byt = null;
BASE64Decoder dec = new BASE64Decoder();//字符字節轉換編碼規范
int t = 0;//定位標記
/**
* 根據傳入值匹配對哪個類型解密
*/
if("dbuser".equals(str)){
ListIterator ite = list.listIterator();
while(ite.hasNext()){
String str1 = ite.next().toString();
if(str1.contains(new String("數據庫用戶名".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = list.get(t).toString();

byt = (dec.decodeBuffer(keylist.get(0).toString()));
key = new SecretKeySpec(byt, "DES");
try {
str = EncryptAndDencrypt.dodecrypt(dec.decodeBuffer(str.substring(7)), key);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}else if("dbpass".equals(str)){
ListIterator ite = list.listIterator();
while(ite.hasNext()){
String str1 = ite.next().toString();
if(str1.contains(new String("數據庫密碼".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = list.get(t).toString();
byt = (dec.decodeBuffer(keylist.get(1).toString()));
key = new SecretKeySpec(byt, "DES");
try {
str = EncryptAndDencrypt.dodecrypt(dec.decodeBuffer(str.substring(7)), key);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}else if("douser".equals(str)){
ListIterator ite = list.listIterator();
while(ite.hasNext()){
String str1 = ite.next().toString();
if(str1.contains(new String("#用戶名".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = list.get(t).toString();
byt = (dec.decodeBuffer( keylist.get(2).toString()));
key = new SecretKeySpec(byt, "DES");
try {
str = EncryptAndDencrypt.dodecrypt(dec.decodeBuffer(str.substring(7)), key);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}else if("dopass".equals(str)){
ListIterator ite = list.listIterator();
while(ite.hasNext()){
String str1 = ite.next().toString();
if(str1.contains(new String("#密碼".getBytes("GBK")))){
t = ite.nextIndex();
break;
}
}
str = list.get(t).toString();
byt = (dec.decodeBuffer( keylist.get(3).toString()));
key = new SecretKeySpec(byt, "DES");
try {
str = EncryptAndDencrypt.dodecrypt(dec.decodeBuffer(str.substring(7)), key);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}else{
return "error";
}

}

}

解密就不需要多說了,需要注意的就是一定要選對正確的key,因為在加密時的key必須與解密的key相同。

這也是我初次寫加密解密以及對文件特殊IO操作的代碼,內容可能有很多可以簡化或者更加優秀的寫法。

對於打包也有一些需要注意,如果需要在Linux下運行jar程序,在打包時需要定一個main函數,一個jar只能定一個main。如果想要給這個jar的main傳入參數的話參看一下代碼:

import java.io.IOException;


public class ThisMain {
public static void main(String[] args) {
try {
String str = args[0];
String result = DoDencypt.JM(str);
System.out.print(result);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

在服務器上的shell指令加密指令是:java -jar 包名

解密指令為:java -jar 包名 傳入值


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM