引入:
现在我们来研究密码学的加密解密过程,这个十分重要,因为它是明文到密文的桥梁,从类型上分,我们又分为加密解密字符串和加密解密文件,我们这文章就讲解这些细节,主要的核心类是Cipher类。
实践:
加密字符串的一般过程为:
(1)获取一个加密用的密钥
(2)实例化Cipher对象
(3)初始化Cipher对象,指定其现在处于加密模式(ENCRYPT_MODE),并指定加密使用的密钥
(4)调用doFinal方法,传入被加密的字符串对应的字节数组,返回加密后的字节数组
解密字符串的一般过程为:
(1)获取一个解密用的密钥
(2)实例化Cipher对象
(3)初始化Cipher对象,指定其现在处于解密模式(DECRYPT_MODE),并指定解密用的密钥
(4)调用doFinal方法,传入密文字节数组,返回解密后的字节数组
加密文件的一般过程为:
(1)获取一个加密用的密钥
(2)实例化Cipher对象
(3)初始化Cipher对象,指定其现在处于加密模式(ENCRYPT_MODE),并指定加密使用的密钥
(4)打开文件输入流,指向要被加密的文件
(5)打开文件输出流,指向加密后存放的文件
(6)打开加密输入流,包装指向被加密文件的输入流
(7)读取,并且加密,加密结果写入目标文件
解密文件的一般过程为:
(1)获取一个解密用的密钥
(2)实例化Cipher对象
(3)初始化Cipher对象,指定其现在处于解密模式(DECRYPT_MODE),并指定解密用的密钥
(4)打开文件输入流,指向要被解密的文件
(5)打开文件输出流,指向解密后存放的文件
(6)打开解密输出流,包装指向解密后文件的输出流
(7)读取,并且解密,解密结果写入目标文件
为了演示上面过程,我们写了一个工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
package com.charles.encryptstudy;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
/** *
* Description: 这个类提供了加密解密的一般方法
*
* @author charles.wang
* @created Oct 29, 2013 9:17:48 AM
*
*/
public class EncryptUtil {
//加密解密字符串的操作方法
/**
* 用指定的密钥加密,吧字符串加密成字节数组
*
* @param data 被加密的字符串
* @param key 加密使用的密钥
* @return 加密后的字节流
* @throws InvalidKeyException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
*
*/
public static byte [] encryptString(String data, Key key)
throws InvalidKeyException,NoSuchPaddingException,NoSuchAlgorithmException,BadPaddingException,IllegalBlockSizeException {
// 实例化Cipher对象用于加密操作
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
// 初始化Cipher对象,表明这个Cipher现在处于加密模式,并且指定加密使用的密钥
cipher.init(Cipher.ENCRYPT_MODE, key);
// 加密过程,返回加密后的内容
return cipher.doFinal(data.getBytes());
}
/**
* 用指定的密钥解密,还原字符串
*
* @param data 要被解密的字节流
* @param key 解密使用的密钥
* @return 解密后还原的字符串
* @throws InvalidKeyException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
*/
public static String decryptString( byte [] data, Key key) throws InvalidKeyException,NoSuchPaddingException,NoSuchAlgorithmException,BadPaddingException,IllegalBlockSizeException {
// 实例化Cipher对象用于解密操作
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
// 初始化Cipher对象,表明这个Cipher现在处于解密模式,并且指定解密使用的密钥
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(data));
}
//加密解密文件的操作方法
/**
* 用指定的密钥加密文件(sourceFile),结果保存在destFile中
*
* @param sourceFile 要加密的文件
* @param destFile 加密后的文件
* @param key 加密使用的密钥
* @throws Exception
*/
public static void encryptFile(String sourceFile,String destFile,Key key) throws Exception{
//实例化Cipher对象用于加密操作
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
//初始化Cipher对象,表明这个Cipher现在处于加密模式,并且指定加密使用的密钥
cipher.init(Cipher.ENCRYPT_MODE, key);
//打开文件输入流,指向要被加密的文件
InputStream is = new FileInputStream(sourceFile);
//打开文件输出流,指向加密后存放的文件
OutputStream os = new FileOutputStream(destFile);
//打开加密输入流,包装指向被加密文件的输入流
CipherInputStream cis = new CipherInputStream(is,cipher);
//缓冲区
byte [] buffer = new byte [ 1024 ];
int result;
//读取,并且加密,加密结果写入目标文件
while ((result = cis.read(buffer)) > 0 ) {
os.write(buffer, 0 , result);
}
cis.close();
is.close();
os.close();
}
/**
* 用指定的密钥解密文件(encryptedFile),结果保存在originalFile中
*
* @param encryptedFile 要解密的文件
* @param originalFile 解密后的文件
* @param key 解密使用的密钥
* @throws Exception
*/
public static void decryptFile(String encryptedFile,String originalFile,Key key) throws Exception{
//实例化Cipher对象用于解密操作
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
//初始化Cipher对象,表明这个Cipher现在处于解密模式,并且指定解密使用的密钥
cipher.init(Cipher.DECRYPT_MODE, key);
//打开文件输入流,指向要被解密的文件
InputStream is = new FileInputStream(encryptedFile);
//打开文件输出流,指向解密后存放的文件
OutputStream os = new FileOutputStream(originalFile);
//打开解密输出流,包装指向解密后文件的输出流
CipherOutputStream cos = new CipherOutputStream(os,cipher);
//缓冲区
byte [] buffer = new byte [ 1024 ];
int result;
//读取,并且解密,解密结果写入目标文件
while ((result = is.read(buffer)) > 0 ) {
cos.write(buffer, 0 , result);
}
cos.close();
is.close();
os.close();
}
} |
对于加密解密字符串,我们写了一个演示Demo类,来演示这2个操作结果,我们指定任意字符串,先用我们的加密方法对其加密,然后用我们的解密方法解密还原原始字符串.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.charles.encryptstudy;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import sun.misc.BASE64Encoder;
import com.charles.keystudy.SymmetricKeyUtil;
/** *
* Description: 这个类用来演示如何加密和解密字符串
*
* @author charles.wang
* @created Oct 28, 2013 4:37:41 PM
*
*/
public class EncryptStringDemo {
public static void main(String[] args) throws Exception {
//我们演示加密解密字符串
System.out.println( "加密解密字符串:" );
String stringNeedEncrypt= "加密这段文本" ;
System.out.println( "被加密的文本为:" +stringNeedEncrypt);
//产生一个密钥
KeyGenerator keyGen = KeyGenerator.getInstance( "DES" );
Key key=keyGen.generateKey();
System.out.println( "\n加密使用的密钥为:" +( new BASE64Encoder()).encode(key.getEncoded()));
System.out.println( "加密使用的算法为:" +key.getAlgorithm());
//加密字符串过程
System.out.println( "\n开始加密字符串..." );
byte [] encryptedValue = EncryptUtil.encryptString(stringNeedEncrypt, key);
System.out.println( "加密后的密文为:" +( new BASE64Encoder()).encode(encryptedValue));
//解密字符串过程
System.out.println( "\n开始解密字符串..." );
String decryptedString = EncryptUtil.decryptString(encryptedValue, key);
System.out.println( "解密后还原的字符串为:" +decryptedString);
}
} |
运行这个类的main()方法,我们可以很清楚的看到,代码正确的被执行了:
对于加密解密文件,我们写了一个演示Demo类,来演示这2个操作结果,我们指定原始文件,先用我们的加密方法对其加密,产生密文文件,然后用我们的解密方法解密还原原始文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package com.charles.encryptstudy;
import java.io.File;
import java.io.FileInputStream;
import java.security.Key;
import javax.crypto.KeyGenerator;
import sun.misc.BASE64Encoder;
/** *
* Description: 这个类是用来演示如何加密和解密文件的
*
* @author charles.wang
* @created Oct 29, 2013 10:06:01 AM
*
*/
public class EncryptFileDemo {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
//我们演示加密解密文件
System.out.println( "加密解密文件:" );
//给定被加密的文件,你可以吧相应任意文件放在任意目录并且更改这里路径
String filePathNeedEncrypt= "travel.jpg" ;
//获取被加密的文件信息
File fileNeedEncrypt= new File(filePathNeedEncrypt);
System.out.println( "被加密的文件为:" +fileNeedEncrypt.getName());
System.out.println( "被加密文件的大小为:" +fileNeedEncrypt.length()+ " bytes" );
//产生一个密钥
KeyGenerator keyGen = KeyGenerator.getInstance( "DES" );
Key key=keyGen.generateKey();
System.out.println( "\n加密使用的密钥为:" +( new BASE64Encoder()).encode(key.getEncoded()));
System.out.println( "加密使用的算法为:" +key.getAlgorithm());
//加密文件过程
System.out.println( "\n开始加密文件..." );
String filePathEncrypted= "travel-encrypt.encrypt" ;
EncryptUtil.encryptFile(filePathNeedEncrypt,filePathEncrypted,key);
//获取加密后的密文文件信息
File fileEncrypted = new File(filePathEncrypted);
System.out.println( "加密后的密文文件为:" +fileEncrypted.getName());
System.out.println( "加密后的密文文件大小为:" +fileEncrypted.length()+ " bytes" );
//解密文件过程
System.out.println( "\n开始解密文件..." );
String filePathRestored= "travel-restored.jpg" ;
EncryptUtil.decryptFile(filePathEncrypted,filePathRestored,key);
//获取解密后的明文文件信息
File fileRestored = new File(filePathRestored);
System.out.println( "解密后的明文文件为:" +fileRestored.getName());
System.out.println( "解密后的明文文件大小为:" +fileRestored.length()+ " bytes" );
}
} |
运行这个类的main()方法,我们可以很清楚的看到,代码正确的被执行了,尤其比较下文件大小:
如果不放心的话,我们去文件系统看:
对于原始文件travel.jpg:
对于加密后的密文文件travel-encrypt.encrypt:
再比较解密后还原的文件travel-restored.jpg:
可以发现它和原始文件是完全一样的。