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);
}
网友评论