1. DES算法简介
1.1.1 DES加密的算法
首先要生成一套加密密钥,从用户处取得一个64位(bit)长的密码口令,然后通过等分、移位、选取和迭代形成一套16个加密密钥,分别供每一轮运算中使用。
DES对64位(bit)的明文分组M进行操作,M经过一个初始置换IP,置换成m0。将m0明文分成左半部分和右半部分m0 = (L0,R0),各32位长。然后进行16轮完全相同的运算(迭代),这些运算被称为函数f,在每一轮运算过程中数据与相应的密钥结合。
在每一轮中,密钥位移位,然后再从密钥的56位中选出48位。通过一个扩展置换将数据的右半部分扩展成48位,并通过一个异或操作替代成新的48位数据,再将其压缩置换成32位。这四步运算构成了函数f。然后,通过另一个异或运算,函数f的输出与左半部分结合,其结果成为新的右半部分,原来的右半部分成为新的左半部分。将该操作重复16次。
经过16轮迭代后,左,右半部分合在一起经过一个末置换(数据整理),这样就完成了加密过程。
参考播客:http://blog.chinaunix.net/uid-29106641-id-4032988.html
![](https://box.kancloud.cn/609befdaf62275ae4043db3da008fc8d_280x280.jpg)
1.1.2 DES算法的安全性和发展
DES的安全性首先取决于密钥的长度。密钥越长,破译者利用穷举法搜索密钥的难度就越大。目前,根据当今计算机的处理速度和能力,56位长度的密钥已经能够被破解,而128位的密钥则被认为是安全的,但随着时间的推移,这个数字也迟早会被突破。
另外,对DES算法进行某种变型和改进也是提高DES算法安全性的途径。
例如后来演变出的3-DES算法使用了3个独立密钥进行三重DES加密,这就比DES大大提高了安全性。如果56位DES用穷举搜索来破译需要2∧56次运算,而3-DES 则需要2∧112次。
DES还有DESX、CRYPT、GDES、RDES等变型。这些变型和改进的目的都是为了加大破译难度以及提高密码运算的效率。
2. DES算法使用
~~~
31. package com;
32. public class DESTest {
33. public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException,
34. NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
35. UnsupportedEncodingException {
36.
37. String str = "这是原文";
38. String psw = "12345678";
39. String encrypte = encrypte(str, psw);
40. System.out.println("加密后:"+encrypte);
41. String decrypte = decrypte(encrypte, psw);
42. System.out.println("解密后:"+decrypte);
43.
44. }
45.
46. public static String encrypte(String original,String psw) throws NoSuchAlgorithmException,
47. NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
48. /**
49. * 创建加密对象,参数为加密算法
50. */
51. Cipher cipher = Cipher.getInstance("DES");
52. /**
* 调用自己的方法生成密钥
54. */
55. Key key = getKey(psw);
56. /**
57. * 初始化加密器
58. * 参数1:加密模式
59. * 参数2:密钥
60. */
61. cipher.init(Cipher.ENCRYPT_MODE, key);
62. /**
63. * 执行加密,解密后得到的字节数组
64. */
65. byte[] doFinal = cipher.doFinal(original.getBytes());
66. /**
67. * 将自己数组用Base64编码,转换为字符串
68. */
69. String encode = Base64.encode(doFinal);
70.
71. return encode;
72. }
73. /**
74. * 从指定字符串生成密钥,密钥所需的字节数组长度为8位
75. * 不足8位时后面补0,超出8位只取前8位
76. * @param psw
77. * @return
78. */
79. private static SecretKeySpec getKey(String psw) {
80. byte[] buffer = new byte[8];
81. byte[] bytes = psw.getBytes();
82. for(int i=0;i<buffer.length&&i<bytes.length;i++){
83. buffer[i]=bytes[i];
84. }
85.
86. return new SecretKeySpec(buffer, "DES");
87. }
88.
89. public static String decrypte(String original,String psw) throws NoSuchAlgorithmException,
90. NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
91. BadPaddingException,
92. UnsupportedEncodingException{
93. /**
94. * 获取加密器(同时也是解密器)
95. */
96. Cipher cipher = Cipher.getInstance("DES");
/**
98. * 获取密钥
99. */
100. Key key = getKey(psw);
101. /**
102. * 初始化密码器,设置为解密模式
103. */
104. cipher.init(Cipher.DECRYPT_MODE, key);
105. /**
106. * 解密。注意,这里需要将传入的原文使用Base64编码转换为byte,因为加密后的密文是用Base64编码的
107. */
108. byte[] doFinal = cipher.doFinal(Base64.decode(original));
109. /**
110. * 将解密后的字节数组转换为字符串
111. */
112. return new String(doFinal);
113. }
114.
115. }
116.
~~~
3. Base64原理
加密后的结果是字节数组,这些被加密后的字节在码表(例如 UTF-8 码表)上找不到对应字符,会出现
乱码,当乱码字符串再次转换为字节数组时,长度会变化,导致解密失败,所以转换后的数据是不安全的。
使用 Base64 对字节数组进行编码,任何字节都能映射成对应的 Base64 字符,之后能恢复到字节数组,
利于加密后数据的保存于传输,所以转换是安全的。同样,字节数组转换成 16 进制字符串也是安全的。
![](https://box.kancloud.cn/30b192bad317f9a167bdc04e062a0ace_720x545.jpg)
1、当字符串字符个数为3的倍数时;比如字符串“ABC”,其在计算机内存中的
十六进制表示为$41、$42、$43,十进制表示为“65”“66”“67”;二进制表示
为01000001 01000010 01000011 将这三个二进制数依次取6bit
,010000/01 0100/0010 01/000011 就转换成了: 010000
010100 001001 000011, 将这四个二进制数转换成十六制数为:
$10,$14,$9,$3,十进制数位为16,20,9,3。对照上面的码表,分别查
找出对应的字符为Q,U,J,D。也是就说字符串“ABC”经过BASE64编码后得
出“QUJD”。这是最简单的情况,即ASCII码字符数刚好可以被3整除。接
着继续讨论余数为2、为1的情况。
2、当余数为2时,比如字符串“ce”,其在内存中十六进制表示为$63,$65;十
进制表示表示99,101;二进制表示为 01100011 01100101 依次取6bit
011000/11 0110/0101 这时,第3个字符不足6位,在后面补零,也就
是0101变成010100。转换结果为 011000 110110 010100 这3个二进
制数转换成十六制数为$18,$36,$14;十进制数位为24,54,20。对照码表
得出结果“Y2U”。编码后的字符个数不足4位,用“=”填充,最后编码得出“Y2U=”。
3、当余数为1时,比如字符串“{”,其在内存中的十六进制表示为$7B,十进制为
123,二进制位表示为 01111011 依次取6bit 011110/11 补0后为
011110/110000 转换结果为011110和110000 这两个二进制数转换成
十六进制数为$1E,$30,十进制数为30,48。对照码表得出结果为“ew”,补上
“=”,最后编码得出“ew= =”。解码也很简单,是编码的逆过程,即将每
个字符对照码表换算成6bit的二进制数,然后重组起来,按8位进行截取,得出原码。