Java中密码加密之PBE算法
2015-12-01 09:14
357 查看
一、简述
PBE算法(Password Base Encryption,基于口令加密)算法是一种基于口令的加密算法。特点在于口令由用户自己掌握,采用随机数(我们这里叫做 盐)杂凑多重加密等方法保证数据的安全性。
PBE算法没有密钥的概念,把口令当做密钥了。因为密钥长短影响算法安全性,还不方便记忆,这里我们直接换成我们自己常用的口令就大大不同了,便于我们的记忆。但是单纯的口令很容易被字典法给穷举出来,所以我们这里给口令加了点“盐”,这个盐和口令组合,想破解就难了。
同时我们将盐和口令合并后用消息摘要算法进行迭代很多次来构建密钥初始化向量的基本材料,使破译更加难了。
PBE算法没构建新的加密算法,就是用了我们常用的对称加密算法,例如AES,DES等算法。
二、模型分析
这里我们还是假设甲乙双发要传递消息,需要口令和盐还有算法
传统的对称加密算法就是,构建密钥,指定算法,然后发送数据前用密钥加密数据,密钥和加密后的数据一起发送给对方,对方拿着密钥对数据进行解密。
现在是密钥没有了,我们就用“口令+盐”构造出一个密钥,然后对数据进行加密
这里“口令”我们可以随便设置,都可以设置成我们开自己电脑的密码
这里“盐”的设置可以采用随机数,可以是甲乙双方约定的数据
最终我们的口令和盐都要公布给双方
1、消息传递双方约定口令,这里甲方构建口令
2、甲方构建口令后,公布给乙方
3、由口令构建方(甲方)构建本次消息传递使用的盐,其实也可以双方约定一个数据,例如硬盘号,今天的日期等等,不一定非要写个安全算法计算出来,只要双方一致就行
4、甲方使用口令、盐对数据加密
5、甲方将盐、加密数据发送给消息接收者(乙方)
6、乙方用收到的口令、盐(可以是约定的数据)对数据进行解密
总体看来口令和盐两边都需要知道。消息传递过程还是需要指定双方的统一算法进行。而这些算法其实还是用的那些常见的对称加密算法
三、java6和bouncycastle支持的算法列表
四、程序展示
[java] view
plaincopy
package com.ca.test;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.commons.codec.binary.Base64;
/**
* 对称加密算法:基于口令加密-PBE算法实现
* 使用java6提供的PBEWITHMD5andDES算法进行展示
* @author kongqz
* */
public class PBECoder {
/**
* J***A6支持以下任意一种算法
* PBEWITHMD5ANDDES
* PBEWITHMD5ANDTRIPLEDES
* PBEWITHSHAANDDESEDE
* PBEWITHSHA1ANDRC2_40
* PBKDF2WITHHMACSHA1
* */
public static final String ALGORITHM="PBEWITHMD5andDES";
/**
* 迭代次数
* */
public static final int ITERATION_COUNT=100;
/**
* 盐初始化
* 盐长度必须为8字节
* @return byte[] 盐
* */
public static byte[] initSalt() throws Exception{
//实例化安全随机数
SecureRandom random=new SecureRandom();
//产出盐
return random.generateSeed(8);
}
/**
* 转换密钥
* @param password 密码
* @return Key 密钥
* */
private static Key toKey(String password) throws Exception{
//密钥彩礼转换
PBEKeySpec keySpec=new PBEKeySpec(password.toCharArray());
//实例化
SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(ALGORITHM);
//生成密钥
SecretKey secretKey=keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
* 加密
* @param data 待加密数据
* @param password 密码
* @param salt 盐
* @return byte[] 加密数据
*
* */
public static byte[] encrypt(byte[] data,String password,byte[] salt) throws Exception{
//转换密钥
Key key=toKey(password);
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt,ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.ENCRYPT_MODE, key,paramSpec);
//执行操作
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param password 密码
* @param salt 盐
* @return byte[] 解密数据
*
* */
public static byte[] decrypt(byte[] data,String password,byte[] salt) throws Exception{
//转换密钥
Key key=toKey(password);
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt,ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.DECRYPT_MODE, key,paramSpec);
//执行操作
return cipher.doFinal(data);
}
/**
* 使用PBE算法对数据进行加解密
* @throws Exception
*
*/
public static void main(String[] args) throws Exception {
//待加密数据
String str="PBE";
//设定的口令密码
String password="azsxdc";
System.out.println("原文:/t"+str);
System.out.println("密码:/t"+password);
//初始化盐
byte[] salt=PBECoder.initSalt();
System.out.println("盐:/t"+Base64.encodeBase64String(salt));
//加密数据
byte[] data=PBECoder.encrypt(str.getBytes(), password, salt);
System.out.println("加密后:/t"+Base64.encodeBase64String(data));
//解密数据
data=PBECoder.decrypt(data, password, salt);
System.out.println("解密后:"+new String(data));
}
}
控制台结果输出:
原文: PBE
密码: azsxdc
盐: VeEQqRzOw2Y=
加密后: 7bQTon5WD04=
解密后:PBE
五、总结
PBE不是一个新构建的算法,就是将密钥的概念转成 “口令+盐” 的方式,将不便于记忆的密钥转成便于记忆的口令
算法还是用的基本的对称加密算法,从上边的支持表中就能看到。
这个是不是和我们到银行开的U盾有些像呢?
就拿民生银行的U盾来说,我们插上U盾,还需要我们去输入口令。然后才能到网上银行去登陆操作,付款的时候还是需要我们去输入口令。
1、U盾的盐从何来?:应该是那个闪盘的序列编号经过一定的处理得出的一个值,因为我们想要U盾生效,在民生的大厅中需要找一个机器激活的。那个激活的动作应该是将我们U盾这个闪盘中的数据写入到我们个人的账户中,相当于做了一次盐的公布
2、口令何来?:就是我们设定的登陆密码呀。这个在窗口办理的时候需要我们设定U盾的密码。并且我们在激活的时候可以改我们的密码
有了上边两个,我们再进行操作,就可以进行我们的身份发送数据的安全性了。网银的数据交换真的安全了...
----例如:项目中真正使用的Util
PBE算法(Password Base Encryption,基于口令加密)算法是一种基于口令的加密算法。特点在于口令由用户自己掌握,采用随机数(我们这里叫做 盐)杂凑多重加密等方法保证数据的安全性。
PBE算法没有密钥的概念,把口令当做密钥了。因为密钥长短影响算法安全性,还不方便记忆,这里我们直接换成我们自己常用的口令就大大不同了,便于我们的记忆。但是单纯的口令很容易被字典法给穷举出来,所以我们这里给口令加了点“盐”,这个盐和口令组合,想破解就难了。
同时我们将盐和口令合并后用消息摘要算法进行迭代很多次来构建密钥初始化向量的基本材料,使破译更加难了。
PBE算法没构建新的加密算法,就是用了我们常用的对称加密算法,例如AES,DES等算法。
二、模型分析
这里我们还是假设甲乙双发要传递消息,需要口令和盐还有算法
传统的对称加密算法就是,构建密钥,指定算法,然后发送数据前用密钥加密数据,密钥和加密后的数据一起发送给对方,对方拿着密钥对数据进行解密。
现在是密钥没有了,我们就用“口令+盐”构造出一个密钥,然后对数据进行加密
这里“口令”我们可以随便设置,都可以设置成我们开自己电脑的密码
这里“盐”的设置可以采用随机数,可以是甲乙双方约定的数据
最终我们的口令和盐都要公布给双方
1、消息传递双方约定口令,这里甲方构建口令
2、甲方构建口令后,公布给乙方
3、由口令构建方(甲方)构建本次消息传递使用的盐,其实也可以双方约定一个数据,例如硬盘号,今天的日期等等,不一定非要写个安全算法计算出来,只要双方一致就行
4、甲方使用口令、盐对数据加密
5、甲方将盐、加密数据发送给消息接收者(乙方)
6、乙方用收到的口令、盐(可以是约定的数据)对数据进行解密
总体看来口令和盐两边都需要知道。消息传递过程还是需要指定双方的统一算法进行。而这些算法其实还是用的那些常见的对称加密算法
三、java6和bouncycastle支持的算法列表
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
PBEWithMD5AndDES | 56 | 56 | CBC | PKCS5Padding | java6实现 |
PBEWithMD5AndTripeDES | 112、168 | 168 | CBC | PKCS6Padding | java7实现 |
PBEWithSHA1AndDESede | 112、168 | 168 | CBC | PKCS7Padding | java8实现 |
PBEWithSHA1AndRC2_40 | 40至1024 | 128 | CBC | PKCS8Padding | java9实现 |
PBEWithMD5AndDES | 64 | 64 | CBC | PKCS5Padding/PKCS7Padding/ISO10126Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithMD5AndRC2 | 128 | 128 | CBC | PKCS5Padding/PKCS7Padding/ISO10127Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHA1AndDES | 64 | 64 | CBC | PKCS5Padding/PKCS7Padding/ISO10128Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHA1AndRC2 | 128 | 128 | CBC | PKCS5Padding/PKCS7Padding/ISO10129Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAndIDEA-CBC | 128 | 128 | CBC | PKCS5Padding/PKCS7Padding/ISO10130Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAnd2-KeyTripleDES-CBC | 128 | 128 | CBC | PKCS5Padding/PKCS7Padding/ISO10131Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAnd3-KeyTripleDES-CBC | 192 | 192 | CBC | PKCS5Padding/PKCS7Padding/ISO10132Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAnd128BitRC2-CBC | 128 | 128 | CBC | PKCS5Padding/PKCS7Padding/ISO10133Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAnd40BitRC2-CBC | 40 | 40 | CBC | PKCS5Padding/PKCS7Padding/ISO10134Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAnd128BitRC4 | 128 | 128 | CBC | PKCS5Padding/PKCS7Padding/ISO10135Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAnd40BitRC4 | 40 | 40 | CBC | PKCS5Padding/PKCS7Padding/ISO10136Padding/ZeroBytePadding | BouncyCastle实现 |
PBEWithSHAAndTwofish-CBC | 256 | 256 | CBC | PKCS5Padding/PKCS7Padding/ISO10137Padding/ZeroBytePadding | BouncyCastle实现 |
[java] view
plaincopy
package com.ca.test;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.commons.codec.binary.Base64;
/**
* 对称加密算法:基于口令加密-PBE算法实现
* 使用java6提供的PBEWITHMD5andDES算法进行展示
* @author kongqz
* */
public class PBECoder {
/**
* J***A6支持以下任意一种算法
* PBEWITHMD5ANDDES
* PBEWITHMD5ANDTRIPLEDES
* PBEWITHSHAANDDESEDE
* PBEWITHSHA1ANDRC2_40
* PBKDF2WITHHMACSHA1
* */
public static final String ALGORITHM="PBEWITHMD5andDES";
/**
* 迭代次数
* */
public static final int ITERATION_COUNT=100;
/**
* 盐初始化
* 盐长度必须为8字节
* @return byte[] 盐
* */
public static byte[] initSalt() throws Exception{
//实例化安全随机数
SecureRandom random=new SecureRandom();
//产出盐
return random.generateSeed(8);
}
/**
* 转换密钥
* @param password 密码
* @return Key 密钥
* */
private static Key toKey(String password) throws Exception{
//密钥彩礼转换
PBEKeySpec keySpec=new PBEKeySpec(password.toCharArray());
//实例化
SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(ALGORITHM);
//生成密钥
SecretKey secretKey=keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
* 加密
* @param data 待加密数据
* @param password 密码
* @param salt 盐
* @return byte[] 加密数据
*
* */
public static byte[] encrypt(byte[] data,String password,byte[] salt) throws Exception{
//转换密钥
Key key=toKey(password);
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt,ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.ENCRYPT_MODE, key,paramSpec);
//执行操作
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param password 密码
* @param salt 盐
* @return byte[] 解密数据
*
* */
public static byte[] decrypt(byte[] data,String password,byte[] salt) throws Exception{
//转换密钥
Key key=toKey(password);
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt,ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.DECRYPT_MODE, key,paramSpec);
//执行操作
return cipher.doFinal(data);
}
/**
* 使用PBE算法对数据进行加解密
* @throws Exception
*
*/
public static void main(String[] args) throws Exception {
//待加密数据
String str="PBE";
//设定的口令密码
String password="azsxdc";
System.out.println("原文:/t"+str);
System.out.println("密码:/t"+password);
//初始化盐
byte[] salt=PBECoder.initSalt();
System.out.println("盐:/t"+Base64.encodeBase64String(salt));
//加密数据
byte[] data=PBECoder.encrypt(str.getBytes(), password, salt);
System.out.println("加密后:/t"+Base64.encodeBase64String(data));
//解密数据
data=PBECoder.decrypt(data, password, salt);
System.out.println("解密后:"+new String(data));
}
}
控制台结果输出:
原文: PBE
密码: azsxdc
盐: VeEQqRzOw2Y=
加密后: 7bQTon5WD04=
解密后:PBE
五、总结
PBE不是一个新构建的算法,就是将密钥的概念转成 “口令+盐” 的方式,将不便于记忆的密钥转成便于记忆的口令
算法还是用的基本的对称加密算法,从上边的支持表中就能看到。
这个是不是和我们到银行开的U盾有些像呢?
就拿民生银行的U盾来说,我们插上U盾,还需要我们去输入口令。然后才能到网上银行去登陆操作,付款的时候还是需要我们去输入口令。
1、U盾的盐从何来?:应该是那个闪盘的序列编号经过一定的处理得出的一个值,因为我们想要U盾生效,在民生的大厅中需要找一个机器激活的。那个激活的动作应该是将我们U盾这个闪盘中的数据写入到我们个人的账户中,相当于做了一次盐的公布
2、口令何来?:就是我们设定的登陆密码呀。这个在窗口办理的时候需要我们设定U盾的密码。并且我们在激活的时候可以改我们的密码
有了上边两个,我们再进行操作,就可以进行我们的身份发送数据的安全性了。网银的数据交换真的安全了...
----例如:项目中真正使用的Util
package org.jeecgframework.core.util; import java.security.Key; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; public class PasswordUtil { /** * J***A6支持以下任意一种算法 PBEWITHMD5ANDDES PBEWITHMD5ANDTRIPLEDES * PBEWITHSHAANDDESEDE PBEWITHSHA1ANDRC2_40 PBKDF2WITHHMACSHA1 * */ /** * 定义使用的算法为:PBEWITHMD5andDES算法 */ public static final String ALGORITHM = "PBEWithMD5AndDES";//加密算法 public static final String Salt = "63293188";//密钥 /** * 定义迭代次数为1000次 */ private static final int ITERATIONCOUNT = 1000; /** * 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. 盐长度必须为8字节 * * @return byte[] 盐值 * */ public static byte[] getSalt() throws Exception { // 实例化安全随机数 SecureRandom random = new SecureRandom(); // 产出盐 return random.generateSeed(8); } public static byte[] getStaticSalt() { // 产出盐 return Salt.getBytes(); } /** * 根据PBE密码生成一把密钥 * * @param password * 生成密钥时所使用的密码 * @return Key PBE算法密钥 * */ private static Key getPBEKey(String password) { // 实例化使用的算法 SecretKeyFactory keyFactory; SecretKey secretKey = null; try { keyFactory = SecretKeyFactory.getInstance(ALGORITHM); // 设置PBE密钥参数 PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); // 生成密钥 secretKey = keyFactory.generateSecret(keySpec); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return secretKey; } /** * 加密明文字符串 * * @param plaintext * 待加密的明文字符串 * @param password * 生成密钥时所使用的密码 * @param salt * 盐值 * @return 加密后的密文字符串 * @throws Exception */ public static String encrypt(String plaintext, String password, byte[] salt) { Key key = getPBEKey(password); byte[] encipheredData = null; PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONCOUNT); try { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); encipheredData = cipher.doFinal(plaintext.getBytes()); } catch (Exception e) { } return bytesToHexString(encipheredData); } /** * 解密密文字符串 * * @param ciphertext * 待解密的密文字符串 * @param password * 生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致) * @param salt * 盐值(如需解密,该参数需要与加密时使用的一致) * @return 解密后的明文字符串 * @throws Exception */ public static String decrypt(String ciphertext, String password, byte[] salt) { Key key = getPBEKey(password); byte[] passDec = null; PBEParameterSpec parameterSpec = new PBEParameterSpec(getStaticSalt(), ITERATIONCOUNT); try { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); passDec = cipher.doFinal(hexStringToBytes(ciphertext)); } catch (Exception e) { // TODO: handle exception } return new String(passDec); } /** * 将字节数组转换为十六进制字符串 * * @param src * 字节数组 * @return */ public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } /** * 将十六进制字符串转换为字节数组 * * @param hexString * 十六进制字符串 * @return */ public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } private static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } public static void main(String[] args) { int i=10; for (int j = 0; j < i; j++) { if((j)%3==0){ System.out.print("<br>"); }else { System.out.print(j); } } System.out.print(-1%2==0); String str = "admin"; String password = "123456"; org.jeecgframework.core.util.LogUtil.info("明文:" + str); org.jeecgframework.core.util.LogUtil.info("密码:" + password); try { byte[] salt = PasswordUtil.getStaticSalt(); //加密 String ciphertext = PasswordUtil.encrypt(str, password, salt); org.jeecgframework.core.util.LogUtil.info("密文:" + ciphertext); //解密 String plaintext = PasswordUtil.decrypt(ciphertext, password, salt); org.jeecgframework.core.util.LogUtil.info("明文:" + plaintext); } catch (Exception e) { e.printStackTrace(); } } }
相关文章推荐
- java字符串函数及理解
- java常用日期函数总结
- java数组
- java的控制流程
- java的表达式
- java中数据类型转换
- JAVA常见的异常类型
- 详解Java的Struts框架中注释的用法
- Struts2源码深究:StrutsPrepareAndExecuteFilter
- 最流行的java后台框架spring quartz定时任务
- Java--UTF-8
- LeetCode 36 : Valid Sudoku (Java)
- Java--ASCII码
- java的数据类型
- java 1.7
- 从小白慢慢往上爬的历程--Java运算符
- java线程相关
- LeetCode 111 : Minimum Depth of Binary Tree (Java)
- java字符串api
- 第6章 访问权限控制