RSA
RSA 加密算法是目前最有影响力的 公钥加密算法,并且被普遍认为是目前 最优秀的公钥方案 之一。RSA 是第一个能同时用于 加密 和 数字签名 的算法,它能够 抵抗 到目前为止已知的 所有密码攻击,已被 ISO 推荐为公钥数据加密标准。
RSA 加密算法 基于一个十分简单的数论事实:将两个大 素数 相乘十分容易,但想要对其乘积进行 因式分解 却极其困难,因此可以将 乘积 公开作为 加密密钥。
RSA公钥与私钥主要用于数字签名(Digital Signature)与认证(Authentication),我们一般也称之为不对称加密/解密。
什么是认证(Authentication)?
认证是安全通信的前提,如果认证出问题,A不是和A想要聊天的人B在聊天,而是和一个C(假冒B)在聊天,则接下来所有的安全措施都是白搭。以目前常用的数字证书(Digital Certificate)认证为例。
CA(Certificate Agent) 存放 A、B 数字证书,在数字证书里包含有各自的RSA公钥与加密算法 A、B各自保管自己的RSA私钥与加密算法,RSA公钥与私钥类似锁与钥匙的关系。即RSA私钥加密,可以用RSA公钥解密,反过来亦是如此。
A与B安全通信认证过程:
- A 向CA请求B的数字证书
- CA把B的数字证书做输入参数,生成一个Hash
- CA用自己的私钥加密Hash,生成一个数字签名(Digital Signature)
- CA把数字签名附在B的数字证书之后,即 B的数字证书 + CA 数字签名,发给A
- A拥有CA的公钥(预装或者离线方式获得),可以解密CA的数字签名(CA私钥加密),得到Hash,同时对接收到的B的数字证书做Hash运算,也得到一个Hash,如果两个Hash 相等,则认为此证书安全可靠,在传输途中没有被篡改,我们称这个过程为数据完整性(Data Integrity )保护。
- A请求认证B,B用自己的私钥加密自己的身份信息,发送给A,由于A已经从CA处获得B的公钥,所以可以解密B的加密报文。既然公钥与私钥是一对一的关系,由于只有B自己知道私钥,以此逻辑推断,A通信的对象为真实的B。
- B认证A的过程类似。
import net.pocrd.annotation.NotThreadSafe;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
@NotThreadSafe
public class RsaHelper {
private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class);
private RSAPublicKey publicKey;
private RSAPrivateCrtKey privateKey;
static {
Security.addProvider(new BouncyCastleProvider()); //使用bouncycastle作为加密算法实现
}
public RsaHelper(String publicKey, String privateKey) {
this(Base64Util.decode(publicKey), Base64Util.decode(privateKey));
}
public RsaHelper(byte[] publicKey, byte[] privateKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
if (publicKey != null && publicKey.length > 0) {
this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
}
if (privateKey != null && privateKey.length > 0) {
this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public RsaHelper(String publicKey) {
this(Base64Util.decode(publicKey));
}
public RsaHelper(byte[] publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
if (publicKey != null && publicKey.length > 0) {
this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public byte[] encrypt(byte[] content) {
if (publicKey == null) {
throw new RuntimeException("public key is null.");
}
if (content == null) {
return null;
}
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int size = publicKey.getModulus().bitLength() / 8 - 11;
ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11));
int left = 0;
for (int i = 0; i < content.length; ) {
left = content.length - i;
if (left > size) {
cipher.update(content, i, size);
i += size;
} else {
cipher.update(content, i, left);
i += left;
}
baos.write(cipher.doFinal());
}
return baos.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public byte[] decrypt(byte[] secret) {
if (privateKey == null) {
throw new RuntimeException("private key is null.");
}
if (secret == null) {
return null;
}
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int size = privateKey.getModulus().bitLength() / 8;
ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size);
int left = 0;
for (int i = 0; i < secret.length; ) {
left = secret.length - i;
if (left > size) {
cipher.update(secret, i, size);
i += size;
} else {
cipher.update(secret, i, left);
i += left;
}
baos.write(cipher.doFinal());
}
return baos.toByteArray();
} catch (Exception e) {
logger.error("rsa decrypt failed.", e);
}
return null;
}
public byte[] sign(byte[] content) {
if (privateKey == null) {
throw new RuntimeException("private key is null.");
}
if (content == null) {
return null;
}
try {
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initSign(privateKey);
signature.update(content);
return signature.sign();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean verify(byte[] sign, byte[] content) {
if (publicKey == null) {
throw new RuntimeException("public key is null.");
}
if (sign == null || content == null) {
return false;
}
try {
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initVerify(publicKey);
signature.update(content);
return signature.verify(sign);
} catch (Exception e) {
logger.error("rsa verify failed.", e);
}
return false;
}
}