zoukankan      html  css  js  c++  java
  • 敏感信息加密处理

    版权声明:本文为博主原创文章,转载请注明出处,欢迎使劲喷

    一、敏感信息加密处理我们要实现什么

    系统往往需要将用户敏感信息进行加密,不同的敏感信息加密要求不同。

    比如,密码的加密,我们往往不需要是可逆的。用户输入密码后,通过系统的加密规则,编码后直接比对加密存储的密码,获得比对结果即可证明用户登录信息合法性。

    然后,有时我们为了防止被脱库导致的数据泄漏,不得不对一些敏感信息(比如:身份证号、手机号)进行加密。这样的数据不仅要求加密,还需要在展示及其他业务场景下完全显示,或者掩码显示,这就需要我们对加密的内容进行解密。

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

    近来,项目中为了实现这个需求,做了些简单的设计:

    注:考虑到在维护生产数据时方便查询,这里使用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
    -------------------原创博文,转载请注明------------------
  • 相关阅读:
    20080531 Windows 下安装 Bugzilla
    20080823 windows + apache + mod_python 的安装
    20080519 在 Windows Server 2003 下安装 SQL Server 2000 提示“无法验证产品密钥”
    20080508 Borland CodeGear 卖了
    20080520 Javascript 随机数产生办法
    20090613 批量操作 Windows Live Mail 邮件的办法
    20080726 Castle项目创始人加入微软
    20080511 php send_mail()
    20080618 ASP.NET Ajax clientside framework failed to load
    20081105 Microsoft Word 2007 中鼠标操作失效的解决办法
  • 原文地址:https://www.cnblogs.com/shizhanming/p/6555241.html
Copyright © 2011-2022 走看看