RSA
- 目前最优秀的公钥方案,第一个能同时用于加密和数字签名的算法,能够抵抗到目前为止已知的所有密码攻击
- 基于一个十分简单的数论事实:将两个大素数相乘十分容易,但对其进行因式分解却十分困难,所以可以将其乘积公开作为加密密钥
- 密钥生成算法简述:
(1)随机选择两个不相等的质数p和q(这两个质数越大,就越难被破解)
(2)计算p和q的乘积n,n的二进制长度就是密钥的长度(实际应用中,RSA密钥1024位,重要场合为2048位)
(3)计算n的欧拉函数φ(n)【φ(n) = (p-1)(q-1)】
(4)随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质
(5)计算e对于φ(n)的模反元素d(模反元素是指有一个整数d,可以使ed被φ(n)除的余数为1)
(6)将n和e封装成公钥,n和d封装成私钥 - 使用公钥(n,e)加密
假设明文为m(m必须为整数,且m必须小于n),根据公式计算密文c: m^e ≡ c (mod n) - 使用私钥(n,d)解密
c^d ≡ m (mod n),依次代入值后就可以计算出明文m
java使用Cipher进行RSA加密
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class RSA {
public static void main(String[] args) {
try {
//随机生成密钥对
genKeyPair();
System.out.println("随机生成的公钥为:" + keyMap.get(0));
System.out.println("随机生成的私钥为:" + keyMap.get(1));
//明文
String message = "RSA是最好的非对称加密算法";
System.out.println("待加密字符串:" + message);
String encrypted = encrypt(message, keyMap.get(0));
System.out.println("密文为:" + encrypted);
//解密
String decrypted = decrypt(encrypted, keyMap.get(1));
System.out.println("解密后:" + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
private static final String ALGORITHM = "RSA";
private static Map<Integer, String> keyMap = new HashMap<>();
/**
* 随机生成密钥对
*/
public static void genKeyPair() throws Exception {
//基于RSA使用KeyPairGenerator生成密钥对
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
//初始化密钥对生成器,密钥大小为96-1024位
keyPairGen.initialize(1024, new SecureRandom());
//生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
//获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//获取公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//得到公钥字符串
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
//得到私钥字符串
String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
//将公钥和私钥保存到map
keyMap.put(0, publicKeyString);
keyMap.put(1, privateKeyString);
}
/**
* 加密
*/
public static String encrypt(String str,String publicKeyString) throws Exception{
//base64编码的公钥
byte[] decoded = Base64.getDecoder().decode(publicKeyString);
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
return outStr;
}
/**
* 解密
*/
public static String decrypt(String str, String privateKeyString) throws Exception {
byte[] inputBytes = Base64.getDecoder().decode(str.getBytes(StandardCharsets.UTF_8));
//base64编码的私钥
byte[] decoded = Base64.getDecoder().decode(privateKeyString);
RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance(ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
String outStr = new String(cipher.doFinal(inputBytes));
return outStr;
}
}
DSA
- DSA是一种更高级的验证方式,用作数字签名
- 该算法不单单只有公钥、私钥还有数字签名,私钥加密生成数字签名,公钥验证数据及签名,如果数据和签名不匹配则认为验证失败
- 数字签名的作用就是为了校验数据在传输过程中不被修改
- DSA算法简述:
(1)使用消息摘要算法将发送数据加密成数字摘要
(2)发送方将数字摘要用自己的私钥加密,生成数字签名
(3)将原文和加密的摘要同时传送给接收方
(4)接收方用发送方的公钥对摘要解密,同时对收到的数据用消息摘要算法计算生成同一摘要
(5)比对解密后的摘要和接收方自己生成的摘要,如果两者一致,则说明信息在传送过程中没有被破坏和篡改,否则,则说明信息已经失去了安全性和保密性 - 可以看到的是DSA算法并不适合加密通信(因为它把原文未加密就直接发出去了),而是适用于验证信息是否被篡改过。
java使用DSA完成数字签名验证
import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class DSA {
public static void main(String[] args) {
try {
//生成密钥对
initKey();
//公钥
byte[] publicKey = getPublicKey();
//私钥
byte[] privateKey = getPrivateKey();
//待加密密文
String originalStr = "DSA是最好的数字签名算法";
System.out.println("待加密字符串:" + originalStr);
//发送方通过私钥进行数字签名
byte[] sign = sign(originalStr.getBytes(), privateKey);
String signString = Base64.getEncoder().encodeToString(sign);
System.out.println("签名为:" + signString);
//接收方使用公钥验证签名
boolean status = verify(originalStr.getBytes(), publicKey, Base64.getDecoder().decode(signString));
System.out.println("状态:" + status);
} catch (Exception e) {
e.printStackTrace();
}
}
//密钥算法
public static final String KEY_ALGORITHM = "DSA";
//数字签名算法
public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
//DSA密钥长度(必须是64的倍数)
private static final int KEY_SIZE = 1024;
//存储密钥的map
private static Map<Integer, Object> keyMap = new HashMap<>();
/**
* 初始化密钥对
*/
public static void initKey() throws Exception {
//实例化密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥生成器
keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//公钥
DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
//私钥
DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
//将密钥放入map
keyMap.put(0, publicKey);
keyMap.put(1, privateKey);
}
/**
* 生成数字签名
*/
public static byte[] sign(byte[] data,byte [] privateKey) throws Exception{
//取得私钥
PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey priKey = keyFactory.generatePrivate(pkcs8);
//实例化签名
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化签名
signature.initSign(priKey);
//更新
signature.update(data);
return signature.sign();
}
/**
* 校验数字签名
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
//转换公钥
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
X509EncodedKeySpec x509 = new X509EncodedKeySpec(publicKey);
//产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509);
//实例化签名
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化签名
signature.initVerify(pubKey);
//更新
signature.update(data);
//验证
return signature.verify(sign);
}
/**
* 获取私钥
*/
public static byte[] getPrivateKey() {
Key key = (Key) keyMap.get(1);
return key.getEncoded();
}
/**
* 获取公钥
*/
public static byte[] getPublicKey(){
Key key = (Key) keyMap.get(0);
return key.getEncoded();
}
}
ECC
- ECC相比其他非对称加密算法的优势在于它的密钥更小,RSA虽然安全级别高,但缺点也很明显,即加解密的耗时较长
- 算法简述:
(1)发送者选定一个椭圆曲线Ep(a,b),并取曲线上一点作为基点G
(2)发送方选择一个d作为私钥,并生成公钥Q=dG
(3)发送方将曲线Ep(a,b)和点G,Q一并发送给接收方
(4)接收方收到消息后,将待传输的明文编码到曲线Ep(a,b)上的一点M,后随机选择一个整数k(k<n)
(5)接收方计算点C1=M+kQ;C2=kG
(6)接收方将C1,C2发给发送者
(7)发送方接收到消息后,计算C1-dC2=M+kQ-d(kG)=M+k(dG)-d(kG)=M,得到点M,再对点M进行解码就得到明文。
由于Cipher不支持ECC代码,所以无了。