MD5、AES、Jasypt加密方式的简要介绍与对比

MD5、AES、Jasypt加密方式的简要介绍与对比

1 前言

目前做的项目中用的加密工具有:MD5、AES加密工具(旧的)及Jasypt加密工具(新增),对这三种加密工具的简要原理和应用做了整理。内容参考的文章见第6部分。如下是各工具在系统中的应用:

  • MD5: 主要用在系统用户名密码的加密上
  • AES:除数据库密码解密外、系统用于密码等所有的均使用该加密工具:redis、邮件、账户等
  • Jasypt:目前只计划实现数据库密码的加解密,配合实现数据库自动化运维

2 MD5加密算法

2.1简介

  • MD5信息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位的散列值,用于确保信息传输完整一致。
  • MD5常用于防止数据被篡改、防止直接存储明文、防止抵赖(数字签名)

2.2 原理

加密要素
  • 明文信息(即不需要秘钥之类的加密要素)
唯一性
  • 通过密码散列函数产生输入信息对应的128位的散列值,同一份信息原文计算后的散列值一定是一样的;
  • 所以通过对比两个散列值是否一样可以判断原文信息是否被篡改;
  • 但理论上来说一个MD5是可能对应无数多个原文,但是在实际现实应用中,原文长度有限,在某个范围内让两个原文对应上同一个MD5是非常困难。
不可逆性
  • 密码散列函数:散列(哈希)函数的一种,即可以解释为一种单向函数,很难从函数的输出结果推出它的输入是什么;
  • 算法计算过程中原文的部分信息是丢失的,所以无法将一个MD5密文进行解密(所以从解密角度看,MD5不算一个加密算法)。

2.3 应用范围

防止数据被篡改

比如在发送文件前,先传输文件使用MD5生成的MD5值,再传输文件,通过对收到的文件生成的MD5值与之前的MD5值对比来判断是否文件在传输过程中被篡改

避免直接存储明文

保存用户密码,这样即使数据库中的密码数据信息被泄露出去,也无法通过解密获得密码原文; 但是对于用户,系统根据用户输入的密码用MD5再次产生MD5值,和数据库的数据进行比较是否一致来确定用户输入的密码的合法性

数字签名

利用唯一性,例如A写了一个文件,认证机构用MD5产生摘要信息保存,只要再次对这个文件用MD5生成摘要信息,和原先保存的那份摘要信息一对比,只要一致,就证明是A写的那个文件,A无法抵赖

2.4 安全性上的问题

暴力破解风险

MD5虽然无法逆向解密,但是却可以“正向暴力破解”。 尽管暴力破解很费时间,但对于诸如密码的加密存储,往往若密码过短过简单则容易被暴力破解。很多在线工具提供通过密文匹配对应输入的原文。如后面示例,其中的一个简单密码就存在这个库中,很轻松就被找出来:

  • 简单密码:用MD5生成password123456的MD5值, 用这个MD5值去查询,可以查询到原文
    在这里插入图片描述
    在这里插入图片描述

  • 复杂密码:用MD5生成哈哈哈XSWL2333的MD5值, 用这个MD5值去查询,则目前这个在线工具还无法匹配到这个结果的原文

在这里插入图片描述
在这里插入图片描述

破解原理

实际上上述的“暴力破解”不是简单的一个一个去生成MD5值再去比较,也不是“字典法”预先构建一个庞大的明文->密文的库,而是在字典法上改进的构建彩虹表的方式。 但都本质都是通过输入MD5值得方式查找对应的原文。

安全规避方式

因此MD5应用在例如密码加密中,对于密码的要求应该不能过于简单,所以密码的设置不应过于简单; 常见的要求就是大小写字母+数字+字符,四选三来组成密码(公司手册对员工门户、个人电脑的密码要求就是如此)

2.5 项目中的应用

  • 用于用户密码的加密,包括对用户密码加密后存储以及登录效验用户密码是否正确
  • 封装了MD5Util工具类,提供三个加密方法直接调用:
    字节数据的加密:getMD5(byte[] bytes)
    字符串数据的加密:getMD5(String str)
    文件的加密:getFileMD5(File file)
/**
 * MD5信息-摘要算法5,是一种单向加密算法(不可逆)</br>
 */
public final class MD5Util {
	//用来将字节转换成16进制表示的字符
	private static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6',
		'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	/**
	 * 私有构造函数,防止实例化使用
	 */
	private MD5Util() {}
	
	/**
	 * 对参数进行MD5加密
	 * @param bytes 要加密的参数
	 * @return MD5值
	 */
	public static String getMD5(byte[] bytes) {
		String result = null;
		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("MD5");
			md.update(bytes);
			byte[] temp = md.digest(); //MD5的计算结果是一个128位长整数,用字节表示就是16字节
			char[] chars = new char[32]; //每个字节用16进制表示的话,需要2个字符,所以共32个字符
			
			//对MD5的每个字节转换成16进制的字符
			int k = 0;
			for (int i = 0; i < 16; i++) {
				byte aByte = temp[i];
				chars[k++] = hexDigits[aByte >>> 4 & 0xf];
				chars[k++] = hexDigits[aByte & 0xf];
			}
			
			result = new String(chars);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 对参数进行MD5加密
	 * @param str 要加密的参数
	 * @return MD5值
	 */
	public static String getMD5(String str) {
		try {
			return getMD5(str.getBytes("GBK"));
		} catch (UnsupportedEncodingException e) {
			throw new AppException("MD5加密异常", e);
		}
	}
	
	/**
	 * 计算文件的MD5加密值,注意如果文件较大、计算MD5时可能性能较差
	 * @param file 文件
	 * @return MD5值
	 */
	public static String getFileMD5(File file) {
		FileInputStream in = null;
		String result = null;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			in = new FileInputStream(file);
			byte[] buffer = new byte[1024];
			while (in.read(buffer) != -1) {
				md.update(buffer);
			}
			BigInteger bigInt = new BigInteger(1, md.digest());
			result = bigInt.toString(16);
		} catch (Exception e) {
			throw new AppException("MD5加密异常", e);
		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (Exception e) {
				throw new AppException("MD5加密时,流关闭异常", e);
			}
		}
		return result;
	}
	
	/**
	 * 计算文件的MD5加密值
	 * @param fileName 文件名
	 * @return MD5值
	 */
	public static String getFileMD5(String fileName) {
		File file = new File(fileName);
		return getFileMD5(file);
	}
}

3 AES加密算法

3.1 简介

  • 高级加密标准(AES,Advanced Encryption Standard)
  • 采用同一个秘钥进行加解密,是一种对称秘钥加密算法,加解密需要秘钥和密文/明文

3.2 原理

加密要素
  • 明文/密文
  • 秘钥
加解密过程

在这里插入图片描述

  • 是一种对称加密算法:即加解密使用的是同一个秘钥,对于同一个明文,用这个秘钥加密出来的密文是一样的;反过来,相同的密文用这个秘钥解密出的结果一定是最开始的明文

什么是非对称加密算法?加解密的秘钥是不同的,例如公钥加密,只有私钥能解密。但算法复杂,加解密速度慢。

3.3 应用

  • 因为是对称加密,所以加密速度快,适合大量数据的加密,且加密的数据可复原
  • 所以应用场景广泛,例如用户信息,实时通信的敏感信息等

3.4 安全性上的问题

安全性
  • 速度快,安全级别高:目前是最安全的加密算法之一
  • 但不是绝对安全,要保证你的秘钥不被泄露,因为AES是对称加密算法,只需要知道秘钥就能解密处所有的加密算法;
安全规避方式
  • 对于系统,秘钥往往写在代码里,所以如果说源代码和数据库数据泄露,那么数据就完全暴露给不法分子,若不法分子只有密文,没有秘钥,那么至少被加密的敏感数据不会被利用。 所以数据加密了并不代表就万无一失。
  • 因此公司有诸如不应该把源代码传到外网等规定,目的之一就是为了防止源代码泄露导致重要或敏感数据泄露

3.5 系统中的应用

  • redis、邮件、Eterm账户等密码或其他信息的加密,应用最多
  • 封装工具类:AESUtil
  1. 私有构造函数:制定加密算法,生成Key
  2. 加解密只需要密文/明文,返回结果
  3. 加密:getEncString(String str)
  4. 解密:getDesString(String str)
/**
 * AES加密算法,采用同一个密钥进行加解密,是一种对称密钥加密算法。
 */
public final class AESUtil {
    //自己定义的秘钥,128bit即16位的随机串。也支持192,25bit,长度越长安全性越高,对应的加解密时间越长
    private final static String KEY_STR = "gOZ+l59TRoBajn3G";
    private final Key key;
    private static AESUtil instance = new AESUtil();
    private static final String ALGORITHM = "AES";
    private static final String RANDOM_ALGORITHM = "SHA1PRNG";

    /**
     * 私有构造函数,防止通过实例化使用
     */
    private AESUtil() {
        try {
            KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM);//Java的秘钥生产器,使用的是AES
            SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);//随机数的算法,NativePRNG和SHA1PRNG
            random.setSeed(KEY_STR.getBytes("GBK"));//用我们设定的秘钥串作为随机数的种子,因为种子是我们固定的,产生的随机数也是固定的
            generator.init(random);
            key = generator.generateKey();//生成的秘钥,我们在加密解密需要用到相同秘钥

        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            throw new AppException("AES构造函数异常", e);
        }
    }

    public static AESUtil getInstance() {
        return instance;
    }

    /**
     * 对byte[]参数进行加密
     * @param bytes 要加密的参数
     * @return 加密后的参数
     */
    private byte[] getEncCode(byte[] bytes) {
        byte[] result = null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");//获取算法实例
            cipher.init(Cipher.ENCRYPT_MODE, key);//初始化,入参为加密模式和秘钥
            result = cipher.doFinal(bytes);//进行加密

        } catch (Exception e) {
            throw new AppException("AES加密异常", e);
        } finally {
            cipher = null;
        }
        return result;
    }

    /**
     * 对byte[]参数进行解密
     * @param bytes 要解密的参数
     * @return 解密后的参数
     */
    private byte[] getDesCode(byte[] bytes) {
        byte[] result = null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");//获取算法实例
            cipher.init(Cipher.DECRYPT_MODE, key);//初始化,入参为解密模式和秘钥
            result = cipher.doFinal(bytes);//进行解密
        } catch (Exception e) {
            throw new AppException("AES解密异常", e);
        } finally {
            cipher = null;
        }
        return result;
    }

    /**
     * 对string参数进行加密
     * @param str 要加密的参数
     * @return 加密后的参数
     */
    public String getEncString(String str) {
        BASE64Encoder base64en = new BASE64Encoder();
        byte[] input = null; //明文
        byte[] output = null; //密文
        String result = null;
        try {
            input = str.getBytes(CommonConstant.CHARSET_NAME);
            output = getEncCode(input);
            result = base64en.encode(output);

        } catch (Exception e) {
            throw new AppException("AES解密异常,参数:" + str, e);
        } finally {
            input = null;
            output = null;
        }
        return result;
    }

    /**
     * 对String参数进行解密
     * @param str 要解密的参数
     * @return 解密后的参数
     */
    public String getDesString(String str) {
        BASE64Decoder base64De = new BASE64Decoder();
        byte[] input = null; //密文
        byte[] output = null; //明文
        String result = null;
        try {
            input = base64De.decodeBuffer(str);
            output = getDesCode(input);
            result = new String(output, CommonConstant.CHARSET_NAME);

        } catch (Exception e) {
            throw new AppException("AES解密异常,参数:" + str, e);
        } finally {
            input = null;
            output = null;
        }
        return result;
    }
}

4 Jasypt加密工具

4.1 简介

Jasypt是一个开源的java库,只需要引入组件就可以使用,开发中无需关注其太多细节即可实现java项目的加密功能; 也因此很难找到相关的资料,因此原理也基本是自己使用和测试结果的总结,不一定严谨。

4.2 原理

加密要素

密文/明文:password
系统钥: sysKey
秘钥:secretKey

加密机制
  • 一套sysKey和secretKey生产的密文是“动态”的(即同一个明文每次加密后的结果不一样); 但这些密文用这一套sysKey和secretKey解密处的结果都一样;
  • 同一套sysKey、secretKey加密后的密文是“固定”的; (准确说不是由这一套加密出来的密文解密不出东西,会报错)
  • 同一系统拥同一套sysKey和secretKey(目前仅是公司DBA那边的规定)
  • 加解密需要三个加密要素,直接调用,无需其他步骤

4.3 应用范围

  • 密码、敏感数据、甚至文件、应用程序
  • 因为无需关注其细节,容易使用,很适合在java项目中实现基本的加密要求

4.4 安全性上的问题

安全规避方式
  • 和AES都是对称加密算法的一种,一样要保证secretKey和sysKey的安全
  • 和AES有点不一样的是,Jasypt加解密需要sysKey和secretKey,所以在系统中使用时,要避免将这两者放在一起,例如都写在一个配置文件

4.5 系统中的应用

  • 目前主要用于数据库连接,对数据库密码的加密,配合公司对数据库安全的要求。
  • password和sysKey由DBA管理提供,自助下载
  • secretKey 由DBA提供(配合Apollo配置中心可以实现开发人员无法接触到secretKey,在一些安全管理实践上很方便)
  • 封装工具类JasyptForDbUtil
  1. 使用到原生工具:JasyptUtil(直接调用,无需其他步骤)
    public String encryptString(String sysKey, String secretKey, String str)
    public String decryptString(String sysKey, String secretKey, String encString)
  2. 秘钥写在代码里,与sysKey和密文分开,避免都在一个地方
    private final static String SECRET_KEY = “aSecretKey”;
  3. 加密:public String getEncStr(String sysKey, String plainText)
  4. 解密:public String getDesStr(String sysKey, String cipherText)
/**
 * Jasypt 加密算法,用于数据库密码加解密 <br>
 * 加解密需要 sysKey、secretKey、密文或明文 <br>
 * 加密生成的密码是动态的,但与同一组sysKey、secretKey是对应的 <br>
 */
public final class JasyptUtil {
    /**
     * 秘钥,不同系统的秘钥不同 <br>
     * 秘钥与配置文件密文分开,避免都在一个地方 <br>
     */
    private final static String SECRET_KEY = "mySrcretKey";

    private static com.mytest.jasypt.util.JasyptUtil jasyptUtil = new com.mytest.jasypt.util.JasyptUtil();

    private static JasyptUtil instance = new JasyptUtil();

    public static JasyptUtil getInstance() {
        return instance;
    }

    /**
     * 私有构造函数,防止通过实例化使用
     */
    private JasyptUtil() {
    }

    /**
     * Jasypt 加密
     * @param sysKey    sysKey
     * @param plainText 要加密的明文
     * @return 加密后的字符串
     */
    public String getEncStr(String sysKey, String plainText) {
        try {
            return jasyptUtil.encryptString(sysKey, SECRET_KEY, plainText);
        } catch (Exception e) {
            throw new AppException("Jasypt加密异常" + e);
        }

    }

    /**
     * Jasypt 解密
     * @param sysKey     sysKey
     * @param cipherText 要解密的密文
     * @return 解密密后的字符串
     */
    public String getDesStr(String sysKey, String cipherText) {
        try {
            return jasyptUtil.decryptString(sysKey, SECRET_KEY, cipherText);
        } catch (Exception e) {
            throw new AppException("Jasypt解密异常" + e);
        }
    }
}

4.6延伸:实现对配置文件属性的解密(使用Jasypt)

系统中加密工具一个重要的应用是对一些配置文件的信息进行解密,例如数据库连接的密码使用密文保存在配置文件中,需要解密后使用

1 读取配置文件,对配置文件属性进行解密并重置操作

重写PropertyResourceConfigurer类的convertProperties方法,启动时执行该方法,读取配置文件所有属性,并解密重设需解密的属性值; 该方法由PropertyResourceConfigurer自身提供缺省实现,用于对属性值做必要的转换处理,缺省不做任何处理;
是否解密通过是否存在同名属性名+.encode,属性值为对应加密算法,根据配置的加密算法值使用对应加密工具进行解密
伪代码

    @Override
    protected void convertProperties(Properties props) {
        //获取所有配置文件属性信息
        Enumeration propertyNames = props.propertyNames();
      
        while (propertyNames.hasMoreElements()) {
            //循环这些属性
            if (name.endsWith(".encode")) {
                value = props.getProperty(name); //获取加密算法,看采用哪种加密工具进行解密
                name = name.substring(0, name.indexOf(".encode")); //找待解密参数名
                value = decode(name, value, props); //进行解密,获取解密后的参数值

                props.setProperty(name, value);//用解密结果重设属性值
            }
        }
    }
2 修改相关配置文件
<!-- Spring修改相关配置文件-->
<bean id="propertyConfigurer" class="com.mytest.common.config.DecryptPropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:public.properties</value>
            <value>classpath:jdbc.properties</value>
            <value>classpath:redis.properties</value>
        </list>
    </property>
</bean>
# public.properties配置文件
# jasypt加密使用到的sysKey,同一系统sysKey相同
jasypt.sysKey=sysKey串
# jdbc.properties配置文件
oracle.name=dbname
oracle.username=username
oracle.password=密文
oracle.password.encode=JASYPT

6 参考资料

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中使用jasypt加密可以分为以下几个步骤: 1. 引入jasypt依赖 在Java项目中使用jasypt,需要先将其引入到项目中。可以通过Maven等构建工具引入jasypt依赖,比如: ```xml <dependency> <groupId>org.jasypt</groupId> <artifactId>jasypt</artifactId> <version>1.9.3</version> </dependency> ``` 2. 配置加密算法和密钥 在Java程序中使用jasypt加密,需要指定加密算法和密钥。可以在配置文件中设置这些参数,比如: ```properties jasypt.encryptor.algorithm=PBEWithMD5AndDES jasypt.encryptor.password=my-secret-key ``` 其中,`jasypt.encryptor.algorithm`指定加密算法,`jasypt.encryptor.password`指定密钥。 3. 编写加密代码 在Java程序中使用jasypt加密,可以通过`StandardPBEStringEncryptor`类实现。可以先创建一个加密器对象,并设置加密算法和密钥,比如: ```java StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setAlgorithm("PBEWithMD5AndDES"); encryptor.setPassword("my-secret-key"); ``` 然后,可以使用加密器对象对需要加密的数据进行加密,比如: ```java String plaintext = "password"; String ciphertext = encryptor.encrypt(plaintext); ``` 4. 编写解密代码 在Java程序中使用jasypt解密,同样可以通过`StandardPBEStringEncryptor`类实现。可以先创建一个解密器对象,并设置加密算法和密钥,比如: ```java StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setAlgorithm("PBEWithMD5AndDES"); encryptor.setPassword("my-secret-key"); ``` 然后,可以使用解密器对象对需要解密的数据进行解密,比如: ```java String ciphertext = "nZ3qQ5Zm6b4="; String plaintext = encryptor.decrypt(ciphertext); ``` 以上就是使用jasypt加密的基本步骤。需要注意的是,加密算法和密钥的设置应该保密,不要将其暴露在代码或配置文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值