非对称密码的概念:
1.对称密码中的密钥配送问题
一旦黑客截取到你的密钥,你的数据也就泄露了。
2.非对称密码通信模型
说明:接收方已生成自己的公钥和私钥,私钥自己保存,而私钥则广播出去,发送方甲收到乙方的公钥,对需要传输的数据用公钥进行加密,乙方收到数据后用私钥进行解密恢复到原始数据。经过公钥加密过的数据只有私钥才能破解,所以即使加密过后的数据被截取到没有私钥也不能破解。
3.非对称密码的特征
(1)需要两个密钥来进行加密和解密,分别为公钥和私钥
(2)公钥和私钥相互配对,称为 KeyPair
4.非对称密码的优缺点
(1)优点:相比于对称密码,安全性更高
(2)缺点:加解密花费时间长、速度慢
常用非对称密码
(1)DH 密钥交换算法
(2)RSA 算法
(3)EIGamal 算法
非对称密码的作用
1.密钥交换(DH)
2.加密/解密(RSA)
3.数字签名(RSA)
DH 算法介绍
1.DH算法
一种适基于密钥一致协议的加密算法。
2.流程分析
1)甲方构建密钥对儿,将公钥公布给乙方,将私钥保留;双方约定数据加密算法;乙方通过甲方公钥构建密钥对儿,将公钥公布给甲方,将私钥保留。
2)甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给乙方加密后的数据;乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。
3)乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给甲方加密后的数据;甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密
3.DH算法的数学原理
4.JDK实现
密钥长度:512-1024(64的整数倍)
默认密钥长度:1024
工作模式:无
填充方式:无
5.代码实现
package key.base64;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
public class DHUtil {
public static final String PUBLIC_KEY = "DHPublicKey";
public static final String PRIVATE_KEY = "DHPrivateKey";
/**
* 甲方初始化返回秘钥对
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化秘钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");
// 初始化秘钥生成器,默认是1024 512-1024& 64的倍数
keyPairGenerator.initialize(1024);
// 生成秘钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 得到甲方的公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 得到甲方的私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 将公约和私钥封装在Map中,方便以后使用
Map<String, Object> keyMap = new HashMap<String, Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 乙方根据甲方公约初始化并返回秘钥对
*/
public static Map<String, Object> initKey(byte[] key) throws Exception {
// 将甲方公约从字节数组中转换成PublicKey
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
// 实例化秘钥工厂
KeyFactory keyFactory = KeyFactory.getInstance("DH");
// 产生甲方的公钥
DHPublicKey dhPublicKey = (DHPublicKey) keyFactory.generatePublic(keySpec);
// 剖析甲方的公钥,得到其参数
DHParameterSpec dhParameterSpec = dhPublicKey.getParams();
// 实例化秘钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");
// 用甲方秘钥生成秘钥生成器
keyPairGenerator.initialize(dhParameterSpec);
// 产生密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 得到乙方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 得到乙方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 将公约和私钥封装在Map中,方便以后使用
Map<String, Object> keyMap = new HashMap<String, Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 根据对方的公钥和自己私钥生成对称密码本地秘钥
*/
public static byte[] getSecretKey(byte[] publicKey,byte[] privateKey) throws Exception {
//实例化秘钥工厂
KeyFactory keyFactory=KeyFactory.getInstance("DH");
//将公约从字节数组转化为PublicKey
X509EncodedKeySpec pubKeySpec=new X509EncodedKeySpec(publicKey);
PublicKey pubKey=keyFactory.generatePublic(pubKeySpec);
//将私钥从字节数组转化为PrivateKey
PKCS8EncodedKeySpec priKeySpec=new PKCS8EncodedKeySpec(privateKey);
PrivateKey priKey= keyFactory.generatePrivate(priKeySpec);
//准备以上公钥和私钥生成本地秘钥
//先实例化KeyAgreement
KeyAgreement keyAgreement=KeyAgreement.getInstance("DH");
//用自己的私钥初始化KeyAgreement
keyAgreement.init(priKey);
//结合对方公钥进行运算
keyAgreement.doPhase(pubKey, true);
//生成本地秘钥SecretKey秘钥算法为对称密码算法
SecretKey secretKey=keyAgreement.generateSecret("DES");//DES AES 3DES
return secretKey.getEncoded();
}
/**
* 从Map中取得公钥
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) {
DHPublicKey key=(DHPublicKey) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 从Map中取得私钥
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) {
DHPrivateKey key=(DHPrivateKey) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
*Test
*/
public static void main(String[] args){
//Test DH
// 甲方公钥
byte[] publicKey1;
// 甲方私钥
byte[] privateKey1;
// 甲方本地秘钥
byte[] secretKey1;
// 乙方公钥
byte[] publicKey2;
// 乙方私钥
byte[] privateKey2;
// 乙方本地秘钥
byte[] secretKey2;
//初始化并产生甲方秘钥对
Map<String, Object>keyMap1= DHUtil.initKey();
publicKey1=DHUtil.getPublicKey(keyMap1);
privateKey1=DHUtil.getPrivateKey(keyMap1);
System.out.println("DH 甲方公钥:"+BytesToHex.fromBytesToHex(publicKey1));
System.out.println("DH 甲方私钥:"+BytesToHex.fromBytesToHex(privateKey1));
//乙方根据甲方公钥产生乙方秘钥对
Map<String, Object> keyMap2=DHUtil.initKey(publicKey1);
publicKey2=DHUtil.getPublicKey(keyMap2);
privateKey2=DHUtil.getPrivateKey(keyMap2);
System.out.println("DH 乙方公钥:"+BytesToHex.fromBytesToHex(publicKey2));
System.out.println("DH 乙方私钥:"+BytesToHex.fromBytesToHex(privateKey2));
}
}
RSA 算法介绍
1. RSA:MIT 的 Ron Rivest、 Adi Shamir 和 Leonard Adleman三位学者提出的 非对称加密算法
2. 作用:数据加密和数字签名
3.RSA 数学原理
(1)加解密公式
RSA加密:密文 = 明文E mod N 公钥(E,N)
RSA解密:明文 = 密文D mod N 私钥(D,N)
(2)模拟生成密钥对
1)求N:p=17 q=19 N=p*q = 323
2)求L:L=lcm(p-1,q-1)=lcm(16,18) = 144
3)求E:gcd(E,L)=1 E=5
4)求D:E*D mod L = 1 D=29
公钥(5,323) 私钥(29,323)
公钥(5,323) 私钥(29,323)
(3)加密
密文 = 明文E mod N = 123^5 mod 323 = 225
(4)解密
明文 = 密文D mod N = 225^29 mod 323 = 123
4. JDK实现
密钥长度:512-65536(64的倍数)
默认密钥长度:1024
工作模式:ECB
填充方式:NoPadding、PKCS1Padding、
OAEPWITHMD5AndMGF1Padding、
OAEPWITHSHA1AndMGF1Padding、
OAEPWITHSHA256AndMGF1Padding、
OAEPWITHSHA384AndMGF1Padding、
OAEPWITHSHA512AndMGF1Padding
下面是代码实现:
package key.base64;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
public class RSAUtil {
private static final String PUBLIC_KEY="publicKey";
private static final String PRIVATE_KEY="privateKey";
/**
* 生成RSA的公钥和私钥
*/
public static Map<String , Object> initKey() throws Exception{
KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair=keyPairGenerator.generateKeyPair();
RSAPublicKey publicKey=(RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey=(RSAPrivateKey) keyPair.getPrivate();
Map<String , Object> keyMap=new HashMap<String, Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
public static RSAPublicKey getPublicKey(Map<String, Object> keyMap) {
RSAPublicKey publicKey=(RSAPublicKey) keyMap.get(PUBLIC_KEY);
return publicKey;
}
public static RSAPrivateKey getPrivateKey(Map<String, Object> keyMap) {
RSAPrivateKey privateKey=(RSAPrivateKey) keyMap.get(PRIVATE_KEY);
return privateKey;
}
/**
* 公钥加密
*/
public static byte[] encrypt(byte[] data,RSAPublicKey publicKey) throws Exception{
Cipher cipher=Cipher.getInstance("RSA") ;
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherBytes=cipher.doFinal(data);
return cipherBytes;
}
/**
* 私钥解密
*/
public static byte[] decrypt(byte[] data,RSAPrivateKey privateKey) throws Exception {
Cipher cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainBytes=cipher.doFinal(data);
return plainBytes;
}
//Test
public static void main(String[] args){
//Test RSA
Map<String , Object> keyMap=RSAUtil.initKey();
RSAPublicKey rsaPublicKey=RSAUtil.getPublicKey(keyMap);
RSAPrivateKey rsaPrivateKey=RSAUtil.getPrivateKey(keyMap);
System.out.println("RSA PublicKey:"+rsaPublicKey);
System.out.println("RSA PrivateKey:"+rsaPrivateKey);
byte[] rsaResult=RSAUtil.encrypt(DATA.getBytes(), rsaPublicKey);
System.out.println(DATA+">>>RSA加密>>>"+BytesToHex.fromBytesToHex(rsaResult));
byte[] plainResult=RSAUtil.decrypt(rsaResult, rsaPrivateKey);
System.out.println(DATA+">>>RSA 解密>>>"+new String(plainResult));
}
}
BytesToHex 类:
package key.base64;
public class BytesToHex {
public static String fromBytesToHex(byte[] resultBytes) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < resultBytes.length; i++) {
if (Integer.toHexString(0xFF & resultBytes[i]).length() == 1) {
builder.append("0").append(
Integer.toHexString(0xFF & resultBytes[i]));
} else {
builder.append(Integer.toHexString(0xFF & resultBytes[i]));
}
}
return builder.toString();
}
}
“`