敏感信息加密處理


版權聲明:本文為博主原創文章,轉載請注明出處,歡迎使勁噴

一、敏感信息加密處理我們要實現什么

系統往往需要將用戶敏感信息進行加密,不同的敏感信息加密要求不同。

比如,密碼的加密,我們往往不需要是可逆的。用戶輸入密碼后,通過系統的加密規則,編碼后直接比對加密存儲的密碼,獲得比對結果即可證明用戶登錄信息合法性。

然后,有時我們為了防止被脫庫導致的數據泄漏,不得不對一些敏感信息(比如:身份證號、手機號)進行加密。這樣的數據不僅要求加密,還需要在展示及其他業務場景下完全顯示,或者掩碼顯示,這就需要我們對加密的內容進行解密。

二、敏感信息加密處理我做了些什么

近來,項目中為了實現這個需求,做了些簡單的設計:

注:考慮到在維護生產數據時方便查詢,這里使用aes加密方式,該加密方式同mysql的aes加密結果相同,故可在sql中直接使用hex及aes_encrypt函數進行查詢;密鹽可保存在配置文件中。

1.使用自定義注解,po的每個類中需要加密及解密的字段可添加該注解

2.聲明Base類,並實現encrypt和decrypt方法,方法實現利用java反射及自定義注解

3.所有需要用到加密及解密的實體對象,必須繼承自Base類

4.實體類加密時調用encrypt方法,解密時調用decrypt方法,如此可實現對該對象中敏感數據的加密解密

 

三、敏感信息加密實現

  1.先看效果

    

  注釋很清楚,先給對象設置身份證號,然后執行自加密方法,返回自己的引用,打印出來加密后該對象的json字符串;執行自解密方法,返回自己的引用,打印出來解密后該對象的json字符串。

  

2.設計實現結構

 1 crypt
 2       |
 3       |--annotation 4 | |--DecryptFiled 5 | |--EncryptFiled 6 |--crypt 7 | |--EncryptDecryptInterface 8 |--domain 9 | |--BaseInfo 10 | |--SimpleDomain 11 |--utils 12 | |--MySqlUtils

  2.1先看看注解的實現

/**
 * Created by bright on 2017/2/22.
 *
 * @author :
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptFiled {
    String value() default "";
}
自定義注解

兩個注解的實現一致,注解名稱不同而已,不再貼另外一個注解的代碼。

2.2定義自加密、自解密接口

Base類實現該接口中的自加密自解密方法

/**
 * Created by bright on 2017/2/22.
 *
 * @author :
 */
public interface EncryptDecryptInterface {

    public <T> T encryptSelf();

    public <T> T decryptSelf();

}
自定義接口

  2.3MysqlUtils的實現

/**
 * Created by bright on 2017/2/22.
 *
 * @author :
 */
@Component
public class MySqlUtils {

    private static final String ENCRYPTTYPE= "AES";//加密方式

    private static final String ENCODING = "UTF-8";//加密時編碼

    private static String MYSQLUTILSKEY = "aaa";//加密密鹽

    private static MySqlUtils mysqlUtils;//單例

    private static Cipher encryptCipher ;//加密cipher

    private static Cipher decryptChipher;//解密chipher

    /**
     * 該方法可用在spring項目中使用配置文件設置密鹽,默認值為123
     * @param key
     */
    @Value("${mysql.column.crypt.key:123}")
    public void setMysqlutilskey(String key){
        MySqlUtils.MYSQLUTILSKEY = key;
    }


    /**
     * encryptCipher、decryptChipher初始化
     */
    public static void init(){
        try {
            encryptCipher = Cipher.getInstance(ENCRYPTTYPE);
            decryptChipher = Cipher.getInstance(ENCRYPTTYPE);
            encryptCipher.init(Cipher.ENCRYPT_MODE, generateMySQLAESKey(MYSQLUTILSKEY, ENCODING));
            decryptChipher.init(Cipher.DECRYPT_MODE, generateMySQLAESKey(MYSQLUTILSKEY, ENCODING));
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (NoSuchPaddingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 單例獲取方法實現
     * @return
     */
    public synchronized static MySqlUtils getInstance(){
        if(mysqlUtils == null){
            mysqlUtils = new MySqlUtils();
            init();
        }
        return mysqlUtils;
    }


    /**
     * 加密算法
     * @param encryptString
     * @return
     */
    public String mysqlAESEncrypt(String encryptString) {
        try{
            return new String(Hex.encodeHex(encryptCipher.doFinal(encryptString.getBytes(ENCODING)))).toUpperCase();
        } catch (BadPaddingException e) {
            throw new RuntimeException(e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解密算法
     * @param decryptString
     * @return
     */
    public String mysqlAESDecrypt(String decryptString){
        try {
            return new String(decryptChipher.doFinal(Hex.decodeHex(decryptString.toCharArray())));
        } catch (DecoderException nspe) {
            throw new RuntimeException(nspe);
        } catch (BadPaddingException nsae) {
            throw new RuntimeException(nsae);
        } catch (IllegalBlockSizeException ike) {
            throw new RuntimeException(ike);
        }
    }

    /**
     * 產生mysql-aes_encrypt
     * @param key 加密的密鹽
     * @param encoding  編碼
     * @return
     */
    public static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {
        try {
            final byte[] finalKey = new byte[16];
            int i = 0;
            for(byte b : key.getBytes(encoding))
                finalKey[i++%16] ^= b;
            return new SecretKeySpec(finalKey, "AES");
        } catch(UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

}
MysqlUtils

  2.4BaseInfo類的實現

/**
 * Created by bright on 2017/2/22.
 *
 * @author :
 */
public class BaseInfo implements Cloneable, EncryptDecryptInterface {

    /**
     * 拷貝一個對象,並對新對象進行加密
     * 該方法主要用在日志打印上,可防止原對象被加密而影響程序執行
     * @param <T>
     * @return
     */
    public <T extends BaseInfo> T cloneAndEncrypt() {
        T cloneT = null;
        try {
            cloneT = (T) this.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
        if(cloneT !=null)
            return cloneT.encryptSelf();
        throw new RuntimeException("拷貝對象異常");
    }

    /**
     * 重寫clone方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 實現自加密
     *
     * @param <T>
     * @return
     */
    public <T> T encryptSelf() {
        Field[] declaredFields = this.getClass().getDeclaredFields();
        try {
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    if (field.isAnnotationPresent(EncryptFiled.class) && field.getType().toString().endsWith("String")) {
                        field.setAccessible(true);
                        String fieldValue = (String) field.get(this);
                        if (StringUtils.isNotEmpty(fieldValue)) {
                            field.set(this, MySqlUtils.getInstance().mysqlAESEncrypt(fieldValue));
                        }
                        field.setAccessible(false);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return (T) this;
    }

    /**
     * 實現自解密
     *
     * @param <T>
     * @return
     */
    public <T> T decryptSelf() {
        Field[] declaredFields = this.getClass().getDeclaredFields();
        try {
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    if (field.isAnnotationPresent(DecryptFiled.class) && field.getType().toString().endsWith("String")) {
                        field.setAccessible(true);
                        String fieldValue = (String)field.get(this);
                        if(StringUtils.isNotEmpty(fieldValue)) {
                            field.set(this, MySqlUtils.getInstance().mysqlAESDecrypt(fieldValue));
                        }
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return (T) this;
    }
}
BaseInfo

  2.5一個簡單的對象

/**
 * Created by bright on 2017/2/22.
 *
 * @author :
 */
public class SimpleDomain extends BaseInfo{

    @EncryptFiled
    @DecryptFiled
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}
SimpleDomain

2.6來個調用

public class Client {

    @Test
    public void test(){
        SimpleDomain sd = new SimpleDomain();//要進行加密解密的實體類
        sd.setId("6029131988005021537");//注入身份證號
        System.out.println(JSON.toJSONString(sd.encryptSelf()));//執行自加密后輸出
        System.out.println(JSON.toJSONString(sd.decryptSelf()));//執行自解密后輸出
    }
}
Client

 


免責聲明!

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



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