本文介绍 RSA 加密的原理、示例、安全性分析及 Java 语言实现。
目录
- RSA 简介
- 原理
- 示例
- 安全性
- 常见用途
- 代码实现
RSA 简介
RSA 是一种非对称加密算法,于1977年由 MIT 的 Ron Rivest,Adi Shamir 和 Leonard Adleman 三人一起提出。
原理及示例
- 选择 2 个质数
p
和q
- 计算
n = p * q
- 根据欧拉函数
φ(n) = (p - 1) * (q - 1)
计算出φ(n)
- 确定公钥(整数)
e
,要求:1 < e < φ(n)
且e
和φ(n)
互质 - 确定私钥(整数)
d
,要求:(e * d) / φ(n)
的余数为1
- 加密:原文
m
,计算m
的e
次幂除以n
,求余数c
,c
就是加密后所得的密文 - 解密:密文
c
,计算c
的d
次幂除以n
,求余数得到原文m
示例
来自 wikipedia
-
p = 61
,q = 53
n = 61 * 53 = 3233
φ(n) = (61 - 1) * (53 - 1) = 60 * 52 = 3120
e = 17
d = 2753
- 公钥
(3233,17)
,私钥(3233,2753)
- 原文
18
,公钥加密密文2100
,私钥解密得到原文18
- 原文
81
,私钥加密密文2083
,公钥解密得到原文81
安全性
- 加解需要
n
和公钥e
生成密文c
- 解密需要
n
、密钥d
和密文c
- 公开场合窃听者只能获取
n e c
,但是获取不到密钥d
,需要通过e
计算出d
- 如果想通过
e
计算出d
则必须知道φ(n)
- 想知道
φ(n)
必须求出p
和q
- 因为
n = p * q
而n
已知,所以必须进行【质因数分解】,数学证明大数质因数分解十分困难,这也是 RSA 算法安全性的根本保证。
美国国家标准与技术局和 ANSI X9 已经设定了最小密钥长度的要求,RSA 是 2048 位,这样在 2030 年以前是安全的。
常见用途
- 公钥加密、私钥解密:加密数据及信息
- 私钥加密、公钥解密:数字签名
代码实现
package tutorial.java.util;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.util.Base64;
public class RsaUtils {
/**
* 算法名称
*/
private static final String ALGORITHM_RSA = "RSA";
/**
* 签名算法
*/
public static final String SIGNATURE_ALGORITHM = "SHA512withRSA";
/**
* 生成密钥对
*
* @param keySize 密钥长度
* @return 密钥对
*/
public static KeyPair keyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_RSA);
keyPairGenerator.initialize(keySize);
return keyPairGenerator.generateKeyPair();
}
/**
* RSA 私钥加密
*
* @param privateKey 私钥
* @param content 待加密内容
* @return 加密后密文字节数组
*/
public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] content) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(content);
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No Such Algorithm");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No Such Padding");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad Padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal Block Size");
}
}
/**
* RSA 公钥加密
*
* @param publicKey 公钥
* @param content 待加密内容
* @return 加密后密文字节数组
*/
public static byte[] encryptByPublicKey(PublicKey publicKey, byte[] content) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(content);
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No Such Algorithm");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No Such Padding");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad Padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal Block Size");
}
}
/**
* RSA 私钥解密
*
* @param privateKey 私钥
* @param content 密文
* @return 解密后原文字节数组
*/
public static byte[] decryptByPrivateKey(PrivateKey privateKey, byte[] content) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(content);
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No Such Algorithm");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No Such Padding");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad Padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal Block Size");
}
}
/**
* RSA 公钥解密
*
* @param publicKey 公钥
* @param content 密文
* @return 解密后原文字节数组
*/
public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] content) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(content);
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No Such Algorithm");
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException("No Such Padding");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
} catch (BadPaddingException e) {
throw new UnsupportedOperationException("Bad Padding");
} catch (IllegalBlockSizeException e) {
throw new UnsupportedOperationException("Illegal Block Size");
}
}
/**
* 用私钥生成数字签名
*
* @param content 签名内容
* @param privateKey 私钥
* @return 数字签名
*/
public static String sign(byte[] content, PrivateKey privateKey) {
try {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(content);
return Base64.getEncoder().encodeToString(signature.sign());
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No Such Algorithm");
} catch (SignatureException e) {
throw new UnsupportedOperationException("Signature Exception");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
}
}
/**
* 用公钥验证数字签名
*
* @param content 签名内容
* @param publicKey 公钥
* @param sign 签名
* @return 验证结果
*/
public static boolean verify(byte[] content, PublicKey publicKey, String sign) {
try {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(content);
return signature.verify(Base64.getDecoder().decode(sign));
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("No Such Algorithm");
} catch (SignatureException e) {
throw new UnsupportedOperationException("Signature Exception");
} catch (InvalidKeyException e) {
throw new UnsupportedOperationException("Invalid Key");
}
}
}
单元测试:
package tutorial.java.util;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
public class RsaUtilsTest {
@Test
public void test() throws NoSuchAlgorithmException {
String content = "RSA encrypt/decrypt demo";
KeyPair keyPair = RsaUtils.keyPair(2048);
// 测试私钥加密公钥解密
byte[] encryptByPrivateKeyResult = RsaUtils.encryptByPrivateKey(keyPair.getPrivate(),
content.getBytes(StandardCharsets.UTF_8));
byte[] decryptByPublicKeyResult = RsaUtils.decryptByPublicKey(keyPair.getPublic(),
encryptByPrivateKeyResult);
Assert.assertEquals(content, new String(decryptByPublicKeyResult, StandardCharsets.UTF_8));
// 测试公钥加密私钥解密
byte[] encryptByPublicKeyResult = RsaUtils.encryptByPublicKey(keyPair.getPublic(),
content.getBytes(StandardCharsets.UTF_8));
byte[] decryptByPrivateKeyResult = RsaUtils.decryptByPrivateKey(keyPair.getPrivate(),
encryptByPublicKeyResult);
Assert.assertEquals(content, new String(decryptByPrivateKeyResult, StandardCharsets.UTF_8));
// 测试签名
String sign = RsaUtils.sign(content.getBytes(StandardCharsets.UTF_8), keyPair.getPrivate());
Assert.assertTrue(RsaUtils.verify(content.getBytes(StandardCharsets.UTF_8), keyPair.getPublic(), sign));
}
}
网友评论