PKI体系中关键的一环是消息体本身的加密传输。消息的加密可以采用非对称加密和对称加密。
1.概念:
对称密钥加密:又叫专用密钥加密,即发送和接收数据的双方必使用相同的密钥对明文进行加密和解密运算。简单的说,就是同一个密钥即可以加密也可以用来解密。常见的算法如DES,AES
非对称密钥加密:采用双钥密码系统的加密方法,在一个过程中使用两个密钥,一个用于加密,另一个用于解密,这种加密方法称为非对称加密,也称为公钥加密,因为其中一个密钥是公开的(另一个则需要保密)。常见算法如RSA
对称加密 速度快,安全性较低
非对称加密 速度较慢,安全性较高
2.为什么用对称加密消息正文
那假设我们要对QQ消息进行加密,采用非对称加密太耗费性能,速度又比较慢,而直接只用对称加密又显得没达到严格加密的要求。因此现行通常的做法是:
QQ聊天内容采用对称密钥,这样速度快,而在正式开始之前先用非对称密钥加密 对称密钥。
这样相当于先建立了一个比较安全的可信通道,然后在这个通道里用比较快的加密方法通讯。
3.JAVA实现:
因此我们这里先关注消息正文的对称加密的实现。我们这里采用AES进行对称加密。
我们设计一个实现类叫做AESImpl, 理所当然,这个类最基本的就是提供加密和解密的功能。但是现实的情况可能会复杂一些。我们可能是传输一段文字(String),也有可能是发送一个文件(File), 而对于加密,我们可能需要自己设定密码,也可能是要程序自动生成一个密钥文件用于加解密。
不过无论是什么需要,AES加解密JAVA实现的根本是一致的。都是通过javax.crypto.Cipher对象。甚至是加密解密这两种逆过程,都仅仅是cipher.init方法传入参数选Cipher.ENCRYPT_MODE还是Cipher.DECRYPT_MODE罢了。因此我们可以抽出一个"根AES加解密方法",用于持有加解密的公共逻辑,并且用于应对不同的加解密场景。
其他话就不多说了,具体看代码最清楚了:
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
/**
* AES加解密工具实现(支持对字符串或文件加解密,支持生成密钥或使用密码)
* @author nneverwei
* @e-mail: [email protected]
*
*/
public class AESImpl {
/**
* 生成AES密钥
* @return AES密钥
*/
public static SecretKey createKey() {
try {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom("nneverwei".getBytes());
keygen.init(random);
SecretKey key = keygen.generateKey();
return key;
} catch (NoSuchAlgorithmException e) {
System.out.println("AES生成密钥错误");
e.printStackTrace();
return null;
}
}
/**
* 加密Str(使用密码)
* @param content 需要加密的内容
* @param password 加密密码
* @return
*/
public static byte[] encrypt(String content, String password) {
return (byte[]) rootCrypt(Cipher.ENCRYPT_MODE, content.getBytes(), password);
}
/**
* 加密Str(使用密钥)
* @param content 需要加密的内容
* @param key 加密密钥
* @return
*/
public static byte[] encrypt(String content, Key key) {
return (byte[]) rootCrypt(Cipher.ENCRYPT_MODE, content.getBytes(), key);
}
/**
* 解密Str(使用密码)
* @param content 待解密内容
* @param password 解密密码
* @return
*/
public static byte[] decrypt(byte[] content, String password) {
return (byte[]) rootCrypt(Cipher.DECRYPT_MODE, content, password);
}
/**
* 解密Str(使用密钥)
* @param content 待解密内容
* @param key 解密密钥
* @return
*/
public static byte[] decrypt(byte[] content, Key key) {
return (byte[]) rootCrypt(Cipher.DECRYPT_MODE, content, key);
}
/**
* 加密File(使用密码)
* @param content 需要加密的内容
* @param password 加密密码
* @return
*/
public static File encrypt(File content, String password) {
return (File) rootCrypt(Cipher.ENCRYPT_MODE, content, password);
}
/**
* 加密File(使用密钥)
* @param content 需要加密的内容
* @param key 加密密钥
* @return
*/
public static File encrypt(File content, Key key) {
return (File) rootCrypt(Cipher.ENCRYPT_MODE, content, key);
}
/**
* 解密File(使用密码)
* @param content 待解密内容
* @param password 解密密码
* @return
*/
public static File decrypt(File content, String password) {
return (File) rootCrypt(Cipher.DECRYPT_MODE, content, password);
}
/**
* 解密Str(使用密钥)
* @param content 待解密内容
* @param key 解密密钥
* @return
*/
public static File decrypt(File content, Key key) {
return (File) rootCrypt(Cipher.DECRYPT_MODE, content, key);
}
/**
* 加解密根方法
* @param mode Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE
* @param msg 欲加密\解密消息体(支持String或File)
* @param key 密码或密钥(支持String或Key)
* @return byte[]或File
*/
private static Object rootCrypt(int mode, Object msg, Object key){
try{
Key passKey = null;
if(key instanceof String){
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(((String) key).getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
passKey = new SecretKeySpec(enCodeFormat, "AES");
}else{
passKey = (Key) key;
}
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
cipher.init(mode, passKey);// 初始化
if(msg instanceof byte[]){
return cipher.doFinal((byte[]) msg);
}else{
File file = (File)msg;
String fileName = file.getName();
String prefix=fileName.substring(fileName.lastIndexOf(".")+1);
File encryptFile = new File((mode == Cipher.ENCRYPT_MODE?"encrypt.":"decrypt.") + prefix);
InputStream in = new FileInputStream(file);
OutputStream out = new FileOutputStream(encryptFile);
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
boolean more = true;
while (more) {
inLength = in.read(inBytes);
if (inLength == blockSize) {
int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
out.write(outBytes, 0, outLength);
} else
more = false;
}
if (inLength > 0) {
outBytes = cipher.doFinal(inBytes, 0, inLength);
} else {
outBytes = cipher.doFinal();
}
out.write(outBytes);
in.close();
out.close();
return encryptFile;
}
}catch(Exception e){
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
byte[] afterEncrypt = AESImpl.encrypt("你好啊哈哈哈哈哈", "weichao");
System.out.println("加密后:" + new String(afterEncrypt));
byte[] afterDecrypt = AESImpl.decrypt(afterEncrypt, "weichao");
System.out.println("解密后:" + new String(afterDecrypt));
SecretKey key = AESImpl.createKey();
byte[] afterEncrypt2 = AESImpl.encrypt("sfasfasf水电费哈利开始发", key);
System.out.println("加密后:" + new String(afterEncrypt2));
byte[] afterDecrypt2 = AESImpl.decrypt(afterEncrypt2, key);
System.out.println("解密后:" + new String(afterDecrypt2));
File file = new File("hello.txt");
File outFile = AESImpl.encrypt(file, key);
File outFile2 = AESImpl.decrypt(outFile, key);
}
}