Https通信的数字证书中一般采用RSA(公钥密码体制)。认知RSA之前补充点其他知识。
公钥密码体制分为三个部分,公钥、私钥、加密解密算法,它的加密解密过程如下:
公钥密码体制的公钥和算法都是公开的(这是为什么叫公钥密码体制的原因),私钥是保密的。两者都可以加解密数据,公钥加密的内容只能私钥解密,私钥加密的内容只能公钥解密
对称加密算法就是加密使用的密钥和解密使用的密钥是相同的。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道。安全系数相对较低
非对称加密算法就是加密使用的密钥和解密使用的密钥是不相同的。RSA就是一种非对称加密算法。
密钥,一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算法。加解密双方可以自行约定加解密方法,所以就出现了多种加解密方法。
加密就是是指对某个内容经过一定的加密算法及秘钥转化成base64、hex等格式。解密就是通过特定的解密算法及秘钥将加密信息转换成原始信息。数据的加解密是为了数据传输的安全,防止被他人截取。加解密算法一般就是对称加密和非对称加密。
签名就是在信息的后面再加上一段内容,可以证明信息没有被修改过。签名一般使用的方案:
1. 这个过程是不可逆的,也就是说无法通过hash值得出原来的信息内容。
2.不同的内容一定会得到不同的hash值,hash的加解密是为了防止传输过程中被更改,造成信息是否被篡改无法准确验证。
RSA采用的就是一种公钥密码体制。RSA是三位数学家Rivest、Shamir 和 Adleman 设计的一种算法,所以叫做RSA。它是计算机通信安全的基石,也是最重要的加密算法。
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全(网络通信中一般都是2048位的)。
对于RSA的算法原理推荐查看RSA算法原理
在实际应用中我们以Https通信为例来分析RSA的使用过程,现在模拟https通信中client和server的常见对话:
[str是随机字符串]
[{}中是私钥RSA加密后内容,hash为str的has,后面都如此表示]
[client 收到str原文,计算str的hash1,通过证书中RSA公钥解密XXX-hash,获得密文中的内容与hash2,比较解密内容与原内容是否一致,解密的hash和原文计算的hash是否一致,都一致说明服务器是真的,可以进行下一步]
[内容经x算法加密]
[内容经x算法加密]
**注意**
1.验证双方身份的真实性后,双方可以约定使用新的对称加密算法通信,这个比较自由,扩张性很强,可能不同的client与server的通信加密方法是不一样的。
2.在验证双方身份后,也可以client将自己生成的非对称公钥传递给server,通过非对称加密进行双方的通信。
通信前提:client已经拿到了server的数字证书。证书中包含有服务器RSA公钥。关于数字证书原理和其他信息,请阅读该篇博客数字证书。
一般我们开发应用,第一次是在程序中放了数字证书的,在快过期时,可以通过server下载新的证书进行保存并投入使用。
1. 为什么要使用RSA加解密通信内容?
答:为了数据安全,client与server直接的通信在多个层面都能被黑客攻击,通过伪装成server或者client进行数据的窃取和数据篡改。
2. 为什么不在第一次通话中获取server的证书?或者放在某个网站下载?
答:你一开始访问的网站未必是真的网站,下载或传输的证书也可以是假的证书。
3. 为什么要对通信内容同时使用加密和签名?
答:虽然“黑客”无法获知加密内容,但是可以修改加密内容,如给它首位加一段内容、信息部分内容被替换,进行信息干扰。信息通过加密和内容hash值得签名,即可判断通信内容是否被修改破坏,是否完整。
4. client验证server签名为什么是通过随机字符串的hash,而不是直接通过加解密的方式?
答:因为”黑客”可以通过发送简单有规律的字符串,如”0,1,2,3,4”等找到加密规律,破解加密方法,这样是不安全的。通过对字符串的hash值进行加密,client收到内容后,解密字符串的hash值并与传过来的字符串计算出来的hash进行比较,即可验证server的签名。
5. 如何解决”黑客”截取加密内容,多次重复发送信息?
答:给通信内容添加序号和随机号,client或者server收到同样的信息,则说明通信有被干扰,应停止通信进行其他处理。
总结:
1.信息在通信中加密可看为是防止数据泄漏被解密窃取
2.数据签名是为了判断收到的数据是否完整
保证client和server能收到完整、无关方无法解密的加密数据,才是网络通信数据安全的核心点。
附贴一个RSA工具类
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
/**
* 时间:2020/4/20 0020 星期一
* 邮件:[email protected]
* 说明:RSA非对称加密
*/
class RSAUtils {
private final String CHARSET = "UTF-8";
private final String RSA_ALGORITHM = "RSA";
private Map<String, String> createKeys(int keySize) {
//为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
}
//初始化KeyPairGenerator对象,密钥长度
kpg.initialize(keySize);
//生成密匙对
KeyPair keyPair = kpg.generateKeyPair();
//得到公钥
Key publicKey = keyPair.getPublic();
String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
//得到私钥
Key privateKey = keyPair.getPrivate();
String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
Map<String, String> keyPairMap = new HashMap<>();
keyPairMap.put("publicKey", publicKeyStr);
keyPairMap.put("privateKey", privateKeyStr);
return keyPairMap;
}
/**
* 得到公钥
*
* @param publicKey 密钥字符串(经过base64编码)
*/
private RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过X509编码的Key指令获得公钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
}
/**
* 得到私钥
* @param privateKey 密钥字符串(经过base64编码)
*/
private RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过PKCS#8编码的Key指令获得私钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
}
/**
* 公钥加密
*/
private String publicEncrypt(String data, RSAPublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength());
return Base64.getEncoder().encodeToString(bytes);
} catch (Exception e) {
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥解密
*
* @param data 待解密数据
* @param privateKey 私钥
*/
private String privateDecrypt(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes = Base64.getDecoder().decode(data);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, bytes, privateKey.getModulus().bitLength()), CHARSET);
} catch (Exception e) {
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥加密
*
*/
public String privateEncrypt(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] bytes = rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength());
return Base64.getEncoder().encodeToString(bytes);
} catch (Exception e) {
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 公钥解密
*/
public String publicDecrypt(String data, RSAPublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] bytes = Base64.getDecoder().decode(data);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, bytes, publicKey.getModulus().bitLength()), CHARSET);
} catch (Exception e) {
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
private byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
int maxBlock;
if (opmode == Cipher.DECRYPT_MODE) {
maxBlock = keySize / 8;
} else {
maxBlock = keySize / 8 - 11;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] buff;
int i = 0;
try {
while (datas.length > offSet) {
if (datas.length - offSet > maxBlock) {
buff = cipher.doFinal(datas, offSet, maxBlock);
} else {
buff = cipher.doFinal(datas, offSet, datas.length - offSet);
}
out.write(buff, 0, buff.length);
i++;
offSet = i * maxBlock;
}
} catch (Exception e) {
throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
}
byte[] resultDatas = out.toByteArray();
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return resultDatas;
}
public static void main(String[] args) throws Exception {
RSAUtils rsaUtils = new RSAUtils();
Map<String, String> keyMap = rsaUtils.createKeys(1024);
String publicKey = keyMap.get("publicKey");
String privateKey = keyMap.get("privateKey");
System.out.println("公钥: \n\r" + publicKey);
System.out.println("私钥: \n\r" + privateKey);
System.out.println("公钥加密——私钥解密");
String str = "孙子,我是你爸爸";
System.out.println("\r明文:\r\n" + str);
System.out.println("\r明文大小:\r\n" + str.getBytes().length);
String encodedData = rsaUtils.publicEncrypt(str, rsaUtils.getPublicKey(publicKey));
System.out.println("密文:\r\n" + encodedData);
String decodedData = rsaUtils.privateDecrypt(encodedData, rsaUtils.getPrivateKey(privateKey));
System.out.println("解密后文字: \r\n" + decodedData);
}
}
该篇博客纯属个人观点和见解,如有错误恳请留言指正,万分感激!
相关链接: