原创

Spring项目jdbc.properties中的明文密码SM4加密解密

温馨提示:
本文最后更新于 2023年01月10日,已超过 464 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

需求

在使用spring开发应用时,需要连接数据库,一般把数据库信息放在一个属性配置文件中,比如jdbc.properties,里面用明文的方式存储着数据库的敏感信息用户名username和密码password,容易产生安全问题,所以要在配置文件中对其进行加密。

解决思路

使用国密SM4加密解密,先将加密后的数据保存到jdbc.properties配置文件中,再自定义EncryptPropertyPlaceholderConfigurer并继承PropertyPlaceholderConfigurer,想后再自定义类中读取properties配置文件的加密数据,并将加密的数据进行解密。最后在spring-config.xml(一般是applicationContext.xml)中配置要解密的数据,以及jdbc数据源的配置。

解决示例

1、自定义SM4类,将需要加密的明文密码进行加密,并提供解密方法。

package com.invoice.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

/**
*国密算法SM4实现
*@author 王小东
*/
public class SM4 {
	
	private static final int DECRYPT=0;
	public static final int ROUND=32;
	private static final int BLOCK=16;
	public static final String dataKey="0400606d98aee6c2d4fb96274e5e9a5d819976f127c0fc173626c0befd018cd0e3dbe59bd967099ad9f3ca420056f12bba8221ec4638f5483fd21e30b720224a0d";
	public static  String parmKey="04811707a5806706d74e735c41805d8873d3df0add8788e37153b364752aa93397b7fc0ff8f55b525a82064a1455b42c2d3a1a1a35f6810838ea7c665644698c28";
	
	public static void main(String[] args) {
		String username = encode("root", parmKey);
		System.out.println(username);
		String password = encode("123456", parmKey);
		System.out.println(password );
	}
	private static byte[] Sbox={
			(byte) 0xd6,(byte) 0x90,(byte) 0xe9,(byte) 0xfe,(byte) 0xcc,(byte) 0xe1,0x3d,(byte) 0xb7,0x16,(byte) 0xb6,0x14,(byte) 0xc2,0x28,(byte) 0xfb,0x2c,0x05,
			0x2b,0x67,(byte) 0x9a,0x76,0x2a,(byte) 0xbe,0x04,(byte) 0xc3,(byte) 0xaa,0x44,0x13,0x26,0x49,(byte) 0x86,0x06,(byte) 0x99,
			(byte) 0x9c,0x42,0x50,(byte) 0xf4,(byte) 0x91,(byte) 0xef,(byte) 0x98,0x7a,0x33,0x54,0x0b,0x43,(byte) 0xed,(byte) 0xcf,(byte) 0xac,0x62,
			(byte) 0xe4,(byte) 0xb3,0x1c,(byte) 0xa9,(byte) 0xc9,0x08,(byte) 0xe8,(byte) 0x95,(byte) 0x80,(byte) 0xdf,(byte) 0x94,(byte) 0xfa,0x75,(byte) 0x8f,0x3f,(byte) 0xa6,
			0x47,0x07,(byte) 0xa7,(byte) 0xfc,(byte) 0xf3,0x73,0x17,(byte) 0xba,(byte) 0x83,0x59,0x3c,0x19,(byte) 0xe6,(byte) 0x85,0x4f,(byte) 0xa8,
			0x68,0x6b,(byte) 0x81,(byte) 0xb2,0x71,0x64,(byte) 0xda,(byte) 0x8b,(byte) 0xf8,(byte) 0xeb,0x0f,0x4b,0x70,0x56,(byte) 0x9d,0x35,
			0x1e,0x24,0x0e,0x5e,0x63,0x58,(byte) 0xd1,(byte) 0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,(byte) 0x87,
			(byte) 0xd4,0x00,0x46,0x57,(byte) 0x9f,(byte) 0xd3,0x27,0x52,0x4c,0x36,0x02,(byte) 0xe7,(byte) 0xa0,(byte) 0xc4,(byte) 0xc8,(byte) 0x9e,
			(byte) 0xea,(byte) 0xbf,(byte) 0x8a,(byte) 0xd2,0x40,(byte) 0xc7,0x38,(byte) 0xb5,(byte) 0xa3,(byte) 0xf7,(byte) 0xf2,(byte) 0xce,(byte) 0xf9,0x61,0x15,(byte) 0xa1,
			(byte) 0xe0,(byte) 0xae,0x5d,(byte) 0xa4,(byte) 0x9b,0x34,0x1a,0x55,(byte) 0xad,(byte) 0x93,0x32,0x30,(byte) 0xf5,(byte) 0x8c,(byte) 0xb1,(byte) 0xe3,
			0x1d,(byte) 0xf6,(byte) 0xe2,0x2e,(byte) 0x82,0x66,(byte) 0xca,0x60,(byte) 0xc0,0x29,0x23,(byte) 0xab,0x0d,0x53,0x4e,0x6f,
			(byte) 0xd5,(byte) 0xdb,0x37,0x45,(byte) 0xde,(byte) 0xfd,(byte) 0x8e,0x2f,0x03,(byte) 0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
			(byte) 0x8d,0x1b,(byte) 0xaf,(byte) 0x92,(byte) 0xbb,(byte) 0xdd,(byte) 0xbc,0x7f,0x11,(byte) 0xd9,0x5c,0x41,0x1f,0x10,0x5a,(byte) 0xd8,
			0x0a,(byte) 0xc1,0x31,(byte) 0x88,(byte) 0xa5,(byte) 0xcd,0x7b,(byte) 0xbd,0x2d,0x74,(byte) 0xd0,0x12,(byte) 0xb8,(byte) 0xe5,(byte) 0xb4,(byte) 0xb0,
			(byte) 0x89,0x69,(byte) 0x97,0x4a,0x0c,(byte) 0x96,0x77,0x7e,0x65,(byte) 0xb9,(byte) 0xf1,0x09,(byte) 0xc5,0x6e,(byte) 0xc6,(byte) 0x84,
			0x18,(byte) 0xf0,0x7d,(byte) 0xec,0x3a,(byte) 0xdc,0x4d,0x20,0x79,(byte) 0xee,0x5f,0x3e,(byte) 0xd7,(byte) 0xcb,0x39,0x48
	};
	
	private static int[] CK={
			0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
			0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
			0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
			0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
			0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
			0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
			0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
			0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
	};
	
	private static int Rotl(int x,int y)
	{
		return x<<y|x>>>(32-y);
	}
	
	private static int ByteSub(int A)
	{
		return (Sbox[A>>>24&0xFF]&0xFF)<<24|(Sbox[A>>>16&0xFF]&0xFF)<<16|(Sbox[A>>>8&0xFF]&0xFF)<<8|(Sbox[A&0xFF]&0xFF);
	}

	private static int L1(int B)
	{
		return B^Rotl(B,2)^Rotl(B,10)^Rotl(B,18)^Rotl(B,24);
	}
	
	private static int L2(int B)
	{
		return B^Rotl(B,13)^Rotl(B,23);
	}
	
	
	static void SMS4Crypt(byte[] Input,byte[] Output,int[] rk)
	{
		int r,mid;
		int[] x= new int[4];
		int[] tmp = new int[4];
		for(int i=0;i<4;i++)
		{
			tmp[0]=Input[0+4*i]&0xff;
			tmp[1]=Input[1+4*i]&0xff;
			tmp[2]=Input[2+4*i]&0xff;
			tmp[3]=Input[3+4*i]&0xff;
			x[i]=tmp[0]<<24|tmp[1]<<16|tmp[2]<<8|tmp[3];
		}
		for(r=0;r<32;r+=4)
		{
			mid=x[1]^x[2]^x[3]^rk[r+0];
			mid=ByteSub(mid);
			x[0]=x[0]^L1(mid);   //x4
			
			mid=x[2]^x[3]^x[0]^rk[r+1];
			mid=ByteSub(mid);
			x[1]=x[1]^L1(mid);	//x5
			
			mid=x[3]^x[0]^x[1]^rk[r+2];
			mid=ByteSub(mid);
			x[2]=x[2]^L1(mid);	//x6
			
			mid=x[0]^x[1]^x[2]^rk[r+3];
			mid=ByteSub(mid);
			x[3]=x[3]^L1(mid);	//x7
		}
		
		//Reverse
		for(int j=0;j<16;j+=4)
		{
			Output[j]  =(byte) (x[3-j/4]>>>24&0xFF);
			Output[j+1]=(byte) (x[3-j/4]>>>16&0xFF);
			Output[j+2]=(byte) (x[3-j/4]>>>8&0xFF);
			Output[j+3]=(byte) (x[3-j/4]&0xFF);
		}
	}
	
	private static void SMS4KeyExt(byte[] Key,int[] rk,int CryptFlag)
	{
		int r,mid;
		int[] x= new int[4];
		int[] tmp =new int[4];
		for(int i=0;i<4;i++)
		{
			tmp[0]=Key[0+4*i]&0xFF;
			tmp[1]=Key[1+4*i]&0xff;
			tmp[2]=Key[2+4*i]&0xff;
			tmp[3]=Key[3+4*i]&0xff;
			x[i]=tmp[0]<<24|tmp[1]<<16|tmp[2]<<8|tmp[3];
		}
		x[0]^=0xa3b1bac6;
		x[1]^=0x56aa3350;
		x[2]^=0x677d9197;
		x[3]^=0xb27022dc;
		for(r=0;r<32;r+=4)
		{
			mid=x[1]^x[2]^x[3]^CK[r+0];
			mid=ByteSub(mid);
			rk[r+0]=x[0]^=L2(mid);		//rk0=K4
			
			mid=x[2]^x[3]^x[0]^CK[r+1];
			mid=ByteSub(mid);
			rk[r+1]=x[1]^=L2(mid);		//rk1=K5
			
			mid=x[3]^x[0]^x[1]^CK[r+2];
			mid=ByteSub(mid);
			rk[r+2]=x[2]^=L2(mid);		//rk2=K6
			
			mid=x[0]^x[1]^x[2]^CK[r+3];
			mid=ByteSub(mid);
			rk[r+3]=x[3]^=L2(mid);		//rk3=K7
		}
		
		if(CryptFlag==DECRYPT)
		{
			for(r=0;r<16;r++)
			{
				mid=rk[r];
				rk[r]=rk[31-r];
				rk[31-r]=mid;
			}
		}
	}
	
	public static byte[] sms4(byte[] in,int inLen,byte[] key,int CryptFlag)
	{
		int point=0;
		int[] round_key=new int[ROUND]; 
		//int[] round_key={0};
		SMS4KeyExt(key,round_key,CryptFlag);
		byte[] input = new byte[16];
		byte[] output = new byte[16];
		byte[] out = new byte[16];
		while(inLen>=BLOCK)
		{
			input=Arrays.copyOfRange(in, point, point+16);
			SMS4Crypt(input,output,round_key);
			System.arraycopy(output, 0, out, point, BLOCK);
			inLen-=BLOCK;
			point+=BLOCK;
		}
		
		return out;
	}
	
	public static String encode(String input,String key){
		
		return encodeSm4(input, key);
	}
	
	
	public static String encodeSm4(String input,String key) {
		byte[] b_input=null;
		byte[] bbs = null;
		try {
			b_input = input.getBytes("utf-8");
			bbs=key.getBytes("utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		byte[] b_key = new byte[16];
		
		if(bbs.length < 16){
			for(int i=0;i<bbs.length;i++){
				b_key[i] = bbs[i];
			}
		}else{
			b_key = bbs;
		}
		
		byte[] o = new byte[0];
		for(int i=0;i<b_input.length;i+=16){
			byte[] t = ArrayUtils.subarray(b_input, i, i+16);
			o = ArrayUtils.addAll(o, sms4(t,16,b_key,1));
		}
		return new Base64().encodeAsString(o);
	}
	
	public static String md5(String str) {
		str=MD5Util.MD5(str);
		if(str.length()>20) {
			str=str.substring(0, 20);
    	}
		return str;
	}
	
	public static String decode(String input,String key) {
		if(input==null || "".equals(input.trim())) {
			return input;
		}
		input=input.replace(".2b", "+").replace(".#", "=");
		if(!checkSM4(input, key)) {
			return input;
		}
		try {
			return URLDecoder.decode(decodeSM4(input, key).replace("%2C", ","), "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static String decodeSM4(String input,String key) {
		byte[] b_input = Base64.decodeBase64(input);
		byte[] b_key = new byte[16];
		byte[] bbs=null;
		try {
			bbs = key.getBytes("utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		if(bbs.length < 16){
			for(int i=0;i<bbs.length;i++){
				b_key[i] = bbs[i];
			}
		}else{
			b_key = bbs;
		}
		
		byte[] o = new byte[0];
		for(int i=0;i<b_input.length;i+=16){
			byte[] t = ArrayUtils.subarray(b_input, i, i+16);
			byte[] t1 = sms4(t,16,b_key,0);
			o = ArrayUtils.addAll(o,t1);
		}
		
		try {
			return new String(o,"utf-8").trim();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}
	
    public static String getPublicKey(){
        SM2 sm2 = SM2.Instance();
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
        //ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
        //BigInteger privateKey = ecpriv.getD();
        ECPoint publicKeyTo = ecpub.getQ();
        String publicKey=StringUtil.byteToHex(publicKeyTo.getEncoded());
        Session.setAttribute("publickey", publicKey);
        return publicKey;
    }
    
	public static boolean checkSM4(String str,String key)  {		
		String strTow = decodeSM4(str,key).replace("+", ".2b");
		if(!isMessyCode(strTow)) {
			String strOne=encodeSm4(strTow.replace(".2b","+"), key);
			if(str.equals(strOne)) {
				return true;
			}
		}
		return false;
	}
	private static boolean isChinese(char c) {
	    Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
	    if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
	        return true;
	    }
	    return false;
	}
	/** * 判断是否是数字或者是英文字母 * @param c * @return */
	public static boolean judge(char c) {
	    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
	        return true;
	    }
	    return false;
	}
	public static boolean isMessyCode(String strName) {
	    Pattern p = Pattern.compile("\\s*|\t*|\r*|\n*");
	    Matcher m = p.matcher(strName);
	    String after = m.replaceAll("");
	    String temp = after.replaceAll("\\p{P}", "");
	    char[] ch = temp.trim().toCharArray();
	    for (int i = 0; i < ch.length; i++) {
	        char c = ch[i];
	        if (!judge(c)) {
	            if (!isChinese(c)) {
	                return true;
	            }
	        }
	    }
	    return false;
	}	
}

2、jdbc.properties配置

将加密后的序列码替换jdbc.properties配置文件中对应的明文密码。

jdbc.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.url=jdbc:sqlserver://127.0.0.1:8443;databaseName=ceshi
jdbc.username=OVGuWu51ON+UjZfle7BUIA==
jdbc.password=G7cELCnx7NJCUhWNCBh51A==
hibernate.dialect=org.hibernate.dialect.SQLServerDialect

3、自定义EncryptPropertyPlaceholderConfigurer类。

继承PropertyPlaceholderConfigurer,重写convertProperties(Properties properties)方法,在此方法里对所有的加密密码进行解密,并赋给properties文件。

package com.spring.util;
 
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

import com.invoice.util.SM4;
 
/**
 * 继承PropertyPlaceholderConfigurer定义支持密文版属性的属性配置器
 * 
 * @author 王小东
 * 
 */
public class EncryptPropertyPlaceholderConfigurer extends
        PropertyPlaceholderConfigurer {
    private String[] encryptPropNames = { "jdbc.username", "jdbc.password" };
 
    @Override
    protected String convertProperty(String propertyName, String propertyValue) {
        if (isEncryptProp(propertyName)) {
        	String decryptVal = SM4.decode(propertyValue, SM4.parmKey);
            return decryptVal;
        }
        return propertyValue;
    }
 
    /**
     * 判断是否是加密的属性
     * 
     * @param propertyName
     * @return
     */
    private boolean isEncryptProp(String propertyName) {
        for (String encryptpropertyName : encryptPropNames) {
            if (encryptpropertyName.equals(propertyName))
                return true;
        }
        return false;
    }
}

4、 spring-config.xml配置

在spring-config.xml(一般是applicationContext.xml)中进行jdbc数据源配置与读取解密工具配置

    <bean class="com.spring.util.EncryptPropertyPlaceholderConfigurer">
		<property name="locations">
			<value>classpath:jdbc.properties</value>
		</property>
	</bean>
	
	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
正文到此结束
该篇文章的评论功能已被站长关闭
本文目录