加密密钥和解密密钥相同时则称为对称加密。由于加密密钥和解密密钥相同,它们也被称为Shared Key。如AES等。
加密密钥(公钥)和解密密钥(私钥)不相同时则称为非对称加密,别称公钥密码。如RSA等。
非对称加密例子:
假设张三拥有的公钥Pu和私钥Pr,其公钥是公开的,谁想跟张三通信的话必须用张三的公钥Pu进行加密后传输给张三,张三用自己的私钥Pr解密后就能查看通信内容了。
RSA:建立在分解大数的困难度、公钥/私钥长度至少是1024bit。1英文字符=1字节=8bit位。
对称加密的优缺点:
1.高效
2.密钥交换问题
3.不如RSA的加密安全程度高,但是当选择256bit的AES时,仍然能胜任绝大多数的安全领域
非对称加密的优缺点:
1.安全性足够高
2.没有密钥交换的问题
3.效率低,对于大数据加密很慢
实际的保密会话应用场景:
1.基于高效的对称加密算法对会话进行加密
2.会话密钥实时产生并周期性变化
3.基于其他足够安全的方式进行会话密钥的传输和交换
针对上面的第3步,可以利用非对称加密方法来交换会话密钥:
1.产生实时随机的会话密钥
2.A使用对端B的公钥对产生的会话密钥进行加密并传递给对端B
3.对端B使用自己的私钥解密获取会话密钥
4.双方开始基于共享的会话密钥进行对称加密的保密会话通信
点对点通信使用上面的加解密方法进行通信,点对多的通信可以使用下面的方法进行广播:
5.B向任何人发消息时,发送的是使用自己私钥加密后的内容
6.任何人接收到消息后使用消息发送者B的公钥进行解密得到B广播的消息内容
非对称加密的两面性:
A使用B的公钥加密后,B可以使用自己的私钥进行解密;
B使用自己的私钥加密后,A可以使用B的公钥进行解密。
RSA加密算法实例:
public class RSA {
private static RSA mInstance;
private File mPublicKeyFile;
private File mPrivateKeyFile;
public static RSA getInstance(Context context) {
if (mInstance == null) {
mInstance = new RSA(context);
}
return mInstance;
}
public RSA(Context context) {
String path = context.getFilesDir().getAbsolutePath();
mPublicKeyFile = createFile(path + "/publickey.dat");
mPrivateKeyFile = createFile(path + "/privatekey.dat");
generateKeyPair();
}
/**
* 生成密钥对
*/
private void generateKeyPair() {
try {
// RSA算法要求有一个可信任的随机数源
SecureRandom sr = new SecureRandom();
// 为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
// 初始化密钥长度
kpg.initialize(1024, sr);
// 生成密钥对
KeyPair genKeyPair = kpg.genKeyPair();
// 获取公钥
PublicKey publicKey = genKeyPair.getPublic();
// 保存公钥
FileOutputStream fos = new FileOutputStream(mPublicKeyFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(publicKey);
oos.close();
// 获取私钥
PrivateKey privateKey = genKeyPair.getPrivate();
// 保存私钥
FileOutputStream fos1 = new FileOutputStream(mPrivateKeyFile);
ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
oos1.writeObject(privateKey);
oos1.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 公钥加解密
* isEncrypt: true是利用公钥加密,false是利用公钥解密
*/
public byte[] encrypt(byte[] source, boolean isEncrypt) {
byte[] result = null;
try {
// 获取公钥
FileInputStream fis = new FileInputStream(mPublicKeyFile);
ObjectInputStream ois = new ObjectInputStream(fis);
RSAPublicKey publicKey = (RSAPublicKey) ois.readObject();
ois.close();
Cipher cipher = Cipher.getInstance("RSA");
if (isEncrypt) {
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
} else {
cipher.init(Cipher.DECRYPT_MODE, publicKey);
}
if (source != null) {
// 加密
result = cipher.doFinal(source);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 私钥加解密
* isDecrypt: true是利用私钥解密,false是利用私钥加密
*/
public byte[] decrypt(byte[] source, boolean isDecrypt) {
byte[] result = null;
try {
// 读取私钥
FileInputStream fis = new FileInputStream(mPrivateKeyFile);
ObjectInputStream ois = new ObjectInputStream(fis);
RSAPrivateKey privateKey = (RSAPrivateKey) ois.readObject();
ois.close();
Cipher cipher = Cipher.getInstance("RSA");
if (isDecrypt) {
cipher.init(Cipher.DECRYPT_MODE, privateKey);
} else {
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
}
if (source != null) {
// 解密
result = cipher.doFinal(source);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private File createFile(String path) {
File file = new File(path);
if (!file.exists()) {
try {
file.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
}
return file;
}
}
调用:
try {
String source = "哈哈嘻嘻的假短发接欧文多拉点开机费的";
byte[] encrypt = RSA.getInstance(this).encrypt(source.getBytes("UTF-8"), true);
String result = new String(RSA.getInstance(this).decrypt(encrypt, true), "UTF-8");
System.out.println("zyf 使用公钥加密,私钥解密的结果: " + result);
byte[] decrypt = RSA.getInstance(this).decrypt(source.getBytes("UTF-8"), false);
result = new String(RSA.getInstance(this).encrypt(decrypt, false), "UTF-8");
System.out.println("zyf 使用私钥加密,公钥解密的结果: " + result);
} catch (Exception e) {
e.printStackTrace();
}
RSA的低效率特性导致即便是签名也不适合直接对原始信息进行签名,可以:
1.先计算出原始消息的MD5值
2.对MD5值进行基于非对称加密算法的签名
3.签名一般附着于原始消息尾部或头部一起发送
收到消息后,先用非对称加密算法对签名进行解密,解密后的MD5值与原始消息的MD5值进行比对,一致则认为消息没有被篡改。