加密之对称Base64,DES,PBE
1 Base64算法
BASE64 严格地说,属于编码格式,而非加密算法
MD5、SHA、HMAC这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。
1.1 简介
BASE64按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的。
标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的/和+字符变为形如%XX的形式,而这些%号在存入数据库时还需要再进行转换,因为ANSI SQL中已将%号用作通配符。
为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充=号,并将标准Base64中的+和/分别改成了-和_,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。
另有一种用于正则表达式的改进Base64变种,它将+和/改成了“!”和“-”,因为+,*以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。
此外还有一些变种,它们将+/改为“-”或“.”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。)
BASE64的加密解密是双向的,可以求反解。
MD5、SHA以及HMAC是单向加密,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传输过程中是否被修改。其中HMAC 算法有一个密钥,增强了数据传输过程中的安全性,强化了算法外的不可控因素。
1.2 原理
1.2.1 转换步骤
使用base64转换utf-8编码时,utf-8的编码是变长编码,字节数为1、2、3:
转换步骤:
3字节的utf-8编码转换,由3*8转换为4*6,然后每6位增加两个高位0,变为四个字节编码输出;2字节的转换时,将其拆分为6 6 4长度格式,前两个(长度为6的新字符)同上述操作,加两个高位的0,最后一个(长度为4的新字符)高位增加两个0,低位用0补充至一个字节长度,即00XXXX00,最后输出为三个字节的新字符外加一个=,即XXX=;一字节长度的utf-8,方法相同,拆分为两个新字符6 2,操作如上,形成新字符,00XXXXXX,00XX0000,输出为XX==;
也就是说utf-8的字符编码通过base64算法转换,使其输出四个字节的字符,等号的数量对应这utf-8原始字节数,方便解码进行格式识别和编码还原
1.2.2 转换对应表
| 数字 | 码值 | 数字 | 码值 | 数字 | 码值 | 数字 | 码值 |
|---|---|---|---|---|---|---|---|
| 0 | A | 17 | R | 34 | i | 51 | z |
| 1 | B | 18 | S | 35 | j | 52 | 0 |
| 2 | C | 19 | T | 36 | k | 53 | 1 |
| 3 | D | 20 | U | 37 | l | 54 | 2 |
| 4 | E | 21 | V | 38 | m | 55 | 3 |
| 5 | F | 22 | W | 39 | n | 56 | 4 |
| 6 | G | 23 | X | 40 | o | 57 | 5 |
| 7 | H | 24 | Y | 41 | p | 58 | 6 |
| 8 | I | 25 | Z | 42 | q | 59 | 7 |
| 9 | J | 26 | a | 43 | r | 60 | 8 |
| 10 | K | 27 | b | 44 | s | 61 | 9 |
| 11 | L | 28 | c | 45 | t | 62 | + |
| 12 | M | 29 | d | 46 | u | 63 | / |
| 13 | N | 30 | e | 47 | v | ||
| 14 | O | 31 | f | 48 | w | ||
| 15 | P | 32 | g | 49 | x | ||
| 16 | Q | 33 | h | 50 | y |
1.2.3 转换实例分析
1.2.3.1 三字节转换
Man如何转成Base64编码
第一步,M、a、n的ASCII值分别是77、97、110,对应的二进制值是01001101、01100001、01101110,将它们连成一个24位的二进制字符串010011010110000101101110
第二步,将这个24位的二进制字符串分成4组,每组6个二进制位:010011、010110、000101、101110
第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节:00010011、00010110、00000101、00101110。它们的十进制值分别是19、22、5、46。
第四步,根据上表,得到每个值对应Base64编码,即T、W、F、u。
因此,Man的Base64编码就是TWFu
1.2.3.2 不足三字节转换
如果字节数不足三,则这样处理:
二个字节的情况:将这二个字节的一共16个二进制位,按照上面的规则,转成三组,最后一组除了前面加两个0以外,后面也要加两个0。这样得到一个三位的Base64编码,再在末尾补上一个=号。
比如,Ma这个字符串是两个字节对应二进制是01001101、01100001,按照6 6 4可以转化成三组00010011、00010110、00000100以后,对应Base64值分别为T、W、E,再补上一个=号,因此Ma的Base64编码就是TWE=一个字节的情况:将这一个字节的8个二进制位,按照上面的规则转成6 2二组,最后一组除了前面加二个0以外,后面再加4个0。这样得到一个二位的Base64编码,再在末尾补上两个=号。
比如,M这个字母是一个字节对应二进制是01001101,可以转化为二组00010011、00010000,对应的Base64值分别为T、Q,再补上二个=号,因此M的Base64编码就是TQ==
1.3 实际操作
通过java代码实现如下: ·
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encode(key);
}
主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是8的倍数,如果不够位数以=符号填充。
2 DES算法
2.1 定义
DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密
DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位
2.2 具体操作
public static final String ALGORITHM = "DES";
static BASE64Decoder de = new BASE64Decoder();
static BASE64Encoder en = new BASE64Encoder();
//根据密钥 生成key
private static Key toKey(byte[] key) throws Exception {
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey secretKey = keyFactory.generateSecret(dks);
// 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码
//SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
return secretKey;
}
//解密 操作
public static String decrypt(byte[] data, String key) throws Exception {
Key k = toKey(de.decodeBuffer(key));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return new String(cipher.doFinal(data));
}
//加密 操作
public static String encrypt(byte[] data, String key) throws Exception {
Key k = toKey(de.decodeBuffer(key));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return en.encodeBuffer(cipher.doFinal(data));
}
//初始化密钥
public static String initKey(String seed) throws Exception {
SecureRandom secureRandom = null;
if (seed != null) {
secureRandom = new SecureRandom(de.decodeBuffer(seed));
} else {
secureRandom = new SecureRandom();
}
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
kg.init(secureRandom);
SecretKey secretKey = kg.generateKey();
return en.encodeBuffer(secretKey.getEncoded());
}
public static void main(String[] args)throws Exception {
String key = initKey("123"); //hceKtjfTbcg=
System.out.print("钥匙:"+key);
String enStr = encrypt("test".getBytes(),key);
System.out.println("加密后:"+enStr);
String deStr = decrypt(de.decodeBuffer(enStr),key);
System.out.println("解密后:"+deStr);
}
其实DES有很多同胞兄弟,如DESede(TripleDES)、AES、Blowfish、RC2、RC4(ARCFOUR)。这里就不过多阐述了,大同小异,只要换掉ALGORITHM换成对应的值,同时做一个代码替换SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);就可以了,此外就是密钥长度不同了
3 PBE算法
3.1 定义
PBE——Password-based encryption(基于密码加密)。其特点在于口令由用户自己掌管,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式
3.2 实际操作
在使用PBE时可以使用,这些算法:PBEWithMD5AndDES,PBEWithMD5AndTripleDES,PBEWithSHA1AndDESede,PBEWithSHA1AndRC2_40
/**
* 支持以下任意一种算法
*
* <pre>
* PBEWithMD5AndDES
* PBEWithMD5AndTripleDES
* PBEWithSHA1AndDESede
* PBEWithSHA1AndRC2_40
* </pre>
*/
public static final String ALGORITHM = "PBEWITHMD5andDES";
static BASE64Decoder de = new BASE64Decoder();
static BASE64Encoder en = new BASE64Encoder();
/**
* 盐初始化
* @return
* @throws Exception
*/
public static byte[] initSalt() throws Exception {
byte[] salt = new byte[8];
Random random = new Random();
random.nextBytes(salt);
return salt;
}
/**
* 转换密钥<br>
*
* @param password
* @return
* @throws Exception
*/
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
* @throws Exception
*/
public static String encrypt(byte[] data, String password, byte[] salt) throws Exception {
Key key = toKey(password);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
return en.encodeBuffer(cipher.doFinal(data));
}
/**
* 解密
* @param data 数据
* @param password 密码
* @param salt 盐
* @return
* @throws Exception
*/
public static String decrypt(byte[] data, String password, byte[] salt) throws Exception {
Key key = toKey(password);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
return new String(cipher.doFinal(data));
}
public static void main(String[] args) throws Exception {
String inputStr = "abc";
System.err.println("原文: " + inputStr);
byte[] input = inputStr.getBytes();
String pwd = "efg";
System.err.println("密码: " + pwd);
byte[] salt = initSalt();
System.out.println("盐值:" +JSON.toJSON(salt));
String data = encrypt(input, pwd, salt);
System.err.println("加密后: " + data);
String output = decrypt(de.decodeBuffer(data), pwd, salt);
System.err.println("解密后: " + output);
}

浙公网安备 33010602011771号