RSA 加解密(Java 实现)
Posted 长安明月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RSA 加解密(Java 实现)相关的知识,希望对你有一定的参考价值。
RSA 算法是一种非对称加解密算法。服务方生成一对 RSA 密钥,即公钥 + 私钥,将公钥提供给调用方,调用方使用公钥对数据进行加密后,服务方根据私钥进行解密。
一、基础工具类
下方工具类涵盖了生成 RSA 密钥对、加密、解密的方法,并附上了测试过程。
package com.test.utils;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
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;
@Slf4j
public class RSAUtil
public static final String KEY_ALGORITHM = "RSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";
// 1024 bits 的 RSA 密钥对,最大加密明文大小
private static final int MAX_ENCRYPT_BLOCK = 117;
// 1024 bits 的 RSA 密钥对,最大解密密文大小
private static final int MAX_DECRYPT_BLOCK = 128;
// 生成密钥对
public static Map<String, Object> initKey(int keysize) throws Exception
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 设置密钥对的 bit 数,越大越安全
keyPairGen.initialize(keysize);
KeyPair keyPair = keyPairGen.generateKeyPair();
// 获取公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
// 获取公钥字符串
public static String getPublicKeyStr(Map<String, Object> keyMap)
// 获得 map 中的公钥对象,转为 key 对象
Key key = (Key) keyMap.get(PUBLIC_KEY);
// 编码返回字符串
return encryptBASE64(key.getEncoded());
// 获取私钥字符串
public static String getPrivateKeyStr(Map<String, Object> keyMap)
// 获得 map 中的私钥对象,转为 key 对象
Key key = (Key) keyMap.get(PRIVATE_KEY);
// 编码返回字符串
return encryptBASE64(key.getEncoded());
// 获取公钥
public static PublicKey getPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException
byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyString);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generatePublic(keySpec);
// 获取私钥
public static PrivateKey getPrivateKey(String privateKeyString) throws Exception
byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyString);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
/**
* BASE64 编码返回加密字符串
*
* @param key 需要编码的字节数组
* @return 编码后的字符串
*/
public static String encryptBASE64(byte[] key)
return new String(Base64.getEncoder().encode(key));
/**
* BASE64 解码,返回字节数组
*
* @param key 待解码的字符串
* @return 解码后的字节数组
*/
public static byte[] decryptBASE64(String key)
return Base64.getDecoder().decode(key);
/**
* 公钥加密
*
* @param text 待加密的明文字符串
* @param publicKeyStr 公钥
* @return 加密后的密文
*/
public static String encrypt1(String text, String publicKeyStr)
try
log.info("明文字符串为:[]", text);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr));
byte[] tempBytes = cipher.doFinal(text.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(tempBytes);
catch (Exception e)
throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e);
/**
* 私钥解密
*
* @param secretText 待解密的密文字符串
* @param privateKeyStr 私钥
* @return 解密后的明文
*/
public static String decrypt1(String secretText, String privateKeyStr)
try
// 生成私钥
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr));
// 密文解码
byte[] secretTextDecoded = Base64.getDecoder().decode(secretText.getBytes("UTF-8"));
byte[] tempBytes = cipher.doFinal(secretTextDecoded);
return new String(tempBytes);
catch (Exception e)
throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e);
public static void main(String[] args) throws Exception
Map<String, Object> keyMap;
String cipherText;
// 原始明文
String content = "春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。";
// 生成密钥对
keyMap = initKey(1024);
String publicKey = getPublicKeyStr(keyMap);
log.info("公钥:[],长度:[]", publicKey, publicKey.length());
String privateKey = getPrivateKeyStr(keyMap);
log.info("私钥:[],长度:[]", privateKey, privateKey.length());
// 加密
cipherText = encrypto(content, publicKey);
log.info("加密后的密文:[],长度:[]", cipherText, cipherText.length());
// 解密
String plainText = decrypt1(cipherText, privateKey);
log.info("解密后明文:[]", plainText);
运行上述 main 函数,输出如下所示,可见加解密成功。
公钥:[MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVk5GORH7kMgZBI7xN6dvlQ6NXO8/emNe/KOxFJRI1r49ecxSb41IBV7rcf+5z7hihbhXqUm65d3O0xcqDOqRYI0LgBxERsX0cPcAl7zSGDgynUC8jlTxBWlQjwb758vvmDRX3g5v0btUPObcyB1IgM3cpVDmsb5Obcl0Wxk2gGwIDAQAB],长度:[216]
私钥:[MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJWTkY5EfuQyBkEjvE3p2+VDo1c7z96Y178o7EUlEjWvj15zFJvjUgFXutx/7nPuGKFuFepSbrl3c7TFyoM6pFgjQuAHERGxfRw9wCXvNIYODKdQLyOVPEFaVCPBvvny++YNFfeDm/Ru1Q85tzIHUiAzdylUOaxvk5tyXRbGTaAbAgMBAAECgYBFLe5JNYbWtghMgmGqS3onrEPUqdY3ZsuKHzw/sqicaelOTr1+aAHamx/Ssdywg7Oing7GxH9vij7aogxK64lsf/OD9Eq2DCvpbNValV/GVABRcCQLO6neHWujen3ex13ky1qtqCS6oZgbqDZyFAj5EGydJdbAgGTZ/rU0Mqbn+QJBANzi3i7a14KXRJYXx7r1UcTVbs30P0jk2F5SaPYsM0UW4Ec5qmqCv6mCYn6GHCJnlJiHvYt9QOUvWB9xuwHtXg0CQQCtWrITaT7P/jHqxzVc02nM420sNtOCrkSy60gZtfNSWFtTl/ddz2ACrN+fbfolUJhHaus3+ekyFoqpIdfNkpTHAkEAlrwO6SSYWtrFiDOULiZI9ay837klEqZwbPWKASwqlKRGyvQ0MlklWBTNCBCW1Heg9PH0zGLeTUggt9yRxH/qZQJAGaGhVtFm4iX5h3cw4qq3p/2wdKseluHhcnrrTDHk6jX6Ot/rSUmSLpMU9WOzarUB7v1WDg67dhZzJhLE77ZOnQJAAe/5aqiYn2SvAxMislav4PcNnbqnHVH+mmRlRP0xeuOKQQzAEn2nqYBY7vCUw3+SYGxe+EbNMLnrcHvTpQKPsw==],长度:[848]
明文字符串为:[春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。]
加密后的密文:[iQIlICQHZJUUxslhyUJSjEdTWm+/B/OWhWXZHhX+ji5Zf7FjaNHr2BCb92ZOmA9BxsAPsS3bg1SajGyBP8fYIkKJpiqHuQ70Jt9xgwdTnaazJEw3BXWRIJIGwniKcEHZZQe+PjjZ4XNGaNSqCa8NtbuJJ6dfFYnv/T/6ANdBwyA=],长度:[172]
解密后明文:[春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。]
二、异常及原因分析
当原始明文长度较小时,使用上述工具类加解密,没有问题。但是当明文长度过长时,会出现加密异常,如下所示。
Exception in thread "main" java.lang.RuntimeException: 加密字符串[春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。江流宛转绕芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月?江月何年初照人?人生代代无穷已,江月年年望相似。]时遇到异常
at com.test.utils.RSAUtil.encrypto(RSAUtil.java:100)
at com.test.utils.RSAUtil.main(RSAUtil.java:146)
Caused by: javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.test.utils.RSAUtil.encrypto(RSAUtil.java:97)
... 1 more
原因分析如下。
RSA 算法一次能加密的明文长度与密钥长度成正比,如 RSA 1024 实际可加密的明文长度最大是 1024 bits。如果小于这个长度怎么办?就需要进行数据补齐(padding),因为如果没有 padding,用户则无法确定解密后内容的真实长度。字符串之类的内容问题还不大,以 0 作为结束符,但对二进制数据就很难理解,因为不确定后面的 0 是内容还是内容结束符。
只要用到 padding,那么就要占用实际的明文长度。对于 1024 bits(1024 / 8 = 128 byte[] ) 密钥对而言,如果 padding 方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用 11 字节用于填充),则明文长度最多为 128 - 11 = 117 bytes,于是才有 117 字节的说法,即下面这种常见的说法:len_in_byte(原始明文数据)= len_in_bit(key)/ 8 - 11。
我们一般使用的 padding 标准有 NoPadding、OAEPPadding、PKCS1Padding 等,其中PKCS1 建议的 padding 就占用了 11 个字节。对于 RSA 加密来讲,padding 也是要参与加密的,所以实际的明文只有 117 字节了。
我们在把明文送给 RSA 加密器前,要确认这个值是不是大于位长,也就是如果接近位长,那么需要先 padding 再分段加密。除非我们是 “定长定量自己可控可理解” 的加密,不需要 padding。
各种 padding 对输入数据长度的要求总结如下。
- 私钥加密时的要求
padding | 最大数据长度 |
---|---|
RSA_PKCS1_PADDING | RSA_size - 11 |
RSA_NO_PADDING | RSA_size - 0 |
RSA_X931_PADDING | RSA_size - 2 |
- 公钥加密时的要求
padding | 最大数据长度 |
---|---|
RSA_PKCS1_PADDING | RSA_size - 11 |
RSA_SSLV23_PADDING | RSA_size - 11 |
RSA_X931_PADDING | RSA_size - 2 |
RSA_NO_PADDING | RSA_size - 0 |
RSA_PKCS1_OAEP_PADDING | RSA_size - 2 * SHA_DIGEST_LENGTH - 2 |
三、分段加解密
// 分段加密
public static String encrypt2(String plainText, String publicKeyStr) throws Exception
log.info("明文:[],长度:[]", plainText, plainText.length());
byte[] plainTextArray = plainText.getBytes("UTF-8");
PublicKey publicKey = getPublicKey(publicKeyStr);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = plainTextArray.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
int i = 0;
byte[] cache;
while (inputLen - offSet > 0)
if (inputLen - offSet > MAX_ENCRYPT_BLOCK)
cache = cipher.doFinal(plainTextArray, offSet, MAX_ENCRYPT_BLOCK);
else
cache = cipher.doFinal(plainTextArray, offSet, inputLen - offSet);
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
byte[] encryptText = out.toByteArray();
out.close();
return Base64.getEncoder().encodeToString(encryptText);
// 分段解密
public static String decrypt2(String encryptTextHex, String privateKeyStr) throws Exception
byte[] encryptText = Base64.getDecoder().decode(encryptTextHex);
PrivateKey privateKey = getPrivateKey(privateKeyStr);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int inputLen = encryptText.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0)
if (inputLen - offSet > MAX_DECRYPT_BLOCK)
cache = cipher.doFinal(encryptText, offSet, MAX_DECRYPT_BLOCK);
else
cache = cipher.doFinal(encryptText, offSet, inputLen - offSet);
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
byte[] plainText = out.toByteArray();
out.close();
return new String(plainText);
备注:当密钥对改为 2048 bits 时,最大加密明文大小为 2048 (bits) / 8 - 11(byte) = 245 byte,上述代码中的两个常量就变更为:
// 2048 bits 的 RSA 密钥对,最大加密明文大小
private static final int MAX_ENCRYPT_BLOCK = 245;
// 2048 bits 的 RSA 密钥对,最大解密密文大小
private static final int MAX_DECRYPT_BLOCK = 256;
PKCS1 和 PKCS8 的区别如下。
- 以 “-----BEGIN RSA PRIVATE KEY-----” 开头,以 “-----END RSA PRIVATE KEY-----” 结束的就是 PKCS1 格式;
- 以 “-----BEGIN PRIVATE KEY-----” 开头,以 “-----END PRIVATE KEY-----” 结束的就是 PKCS8 格式。
通常 JAVA 中需要 PKCS8 格式的密钥。
在线 RSA 加解密网站:RSA 加解密。
文章参考:
自己实现简单的RSA秘钥生成与加解密(Java )
最近在学习PKI,顺便接触了一些加密算法。对RSA着重研究了一下,自己也写了一个简单的实现RSA算法的Demo,包括公、私钥生成,加解密的实现。虽然比较简单,但是也大概囊括了RSA加解密的核心思想与流程。这里写下来与大家分享一下。
RSA概述:
RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。 RSA的数学基础是大整数因子分解问题,其说明如下:
-
给定两个素数p、q,计算乘积pq=n很容易
-
给定整数n,求n的素因数p、q使得n=pq十分困难
RSA的密码体制:
设n=pq,其中p和q是大素数。设P=E=Zn,且定义K={(n,p,q,a,b):ab≡1(modΦ(n)) }其中 Φ(n)=(p-1)(q-1)。对于K={(n,p,q,a,b)} 定义加密 E(x)=xb mod n; 解密D(y)=ya mod n;(x,y∈Zn),值n,b组成了公钥,p、q和a组成了私钥
- RSA生成秘钥步骤
- 随机生成两个大素数p、q,并计算他们的乘积n
- 计算k=(p-1)(q-1)
- 生成一个小于k且与k互质的数b,一般可使用65537
- 通过扩展欧几里得算法求出a关于k的反模a。
代码如下:
首先,写出三个封装类,PrivateKey.java PublicKey.java RsaKeyPair.java (JDK中的实现高度抽象,这里我们就简单的封装一下)
1 package com.khalid.pki; 2 3 import java.math.BigInteger; 4 5 public class PublicKey { 6 7 private final BigInteger n; 8 9 private final BigInteger b; 10 11 public PublicKey(BigInteger n,BigInteger b){ 12 this.n=n; 13 this.b=b; 14 } 15 16 public BigInteger getN() { 17 return n; 18 } 19 20 public BigInteger getB() { 21 return b; 22 } 23 }
1 package com.khalid.pki; 2 3 import java.math.BigInteger; 4 5 public class PrivateKey { 6 7 private final BigInteger n; 8 9 private final BigInteger a; 10 11 public PrivateKey(BigInteger n,BigInteger a){ 12 this.n=n; 13 this.a=a; 14 } 15 16 public BigInteger getN() { 17 return n; 18 } 19 20 public BigInteger getA() { 21 return a; 22 } 23 }
1 package com.khalid.pki; 2 3 public class RsaKeyPair { 4 5 private final PrivateKey privateKey; 6 7 private final PublicKey publicKey; 8 9 public RsaKeyPair(PublicKey publicKey,PrivateKey privateKey){ 10 this.privateKey=privateKey; 11 this.publicKey=publicKey; 12 } 13 14 public PrivateKey getPrivateKey() { 15 return privateKey; 16 } 17 18 public PublicKey getPublicKey() { 19 return publicKey; 20 } 21 }
生成大素数的方法没有自己实现,借用了BigInteger类中的probablePrime(int bitlength,SecureRandom random)方法
SecureRandom random=new SecureRandom(); random.setSeed(new Date().getTime()); BigInteger bigPrimep,bigPrimeq; while(!(bigPrimep=BigInteger.probablePrime(bitlength,random)).isProbablePrime(1)){ continue; }//生成大素数p while(!(bigPrimeq=BigInteger.probablePrime(bitlength,random)).isProbablePrime(1)){ continue; }//生成大素数q
计算k、n、b的值
1 BigInteger n=bigPrimep.multiply(bigPrimeq);//生成n 2 //生成k 3 BigInteger k=bigPrimep.subtract(BigInteger.ONE).multiply(bigPrimeq.subtract(BigInteger.ONE)); 4 //生成一个比k小的b,或者使用65537 5 BigInteger b=BigInteger.probablePrime(bitlength-1, random);
核心计算a的值,扩展欧几里得算法如下
注意第二个方法 cal 其实还可以传递第三个参数,模的值,但是我们这里省略了这个参数,因为在RSA中模是1
1 private static BigInteger x; //存储临时的位置变量x,y 用于递归 2 3 private static BigInteger y; 4 5 6 //欧几里得扩展算法 7 public static BigInteger ex_gcd(BigInteger a,BigInteger b){ 8 if(b.intValue()==0){ 9 x=new BigInteger("1"); 10 y=new BigInteger("0"); 11 return a; 12 } 13 BigInteger ans=ex_gcd(b,a.mod(b)); 14 BigInteger temp=x; 15 x=y; 16 y=temp.subtract(a.divide(b).multiply(y)); 17 return ans; 18 19 } 20 21 //求反模 22 public static BigInteger cal(BigInteger a,BigInteger k){ 23 BigInteger gcd=ex_gcd(a,k); 24 if(BigInteger.ONE.mod(gcd).intValue()!=0){ 25 return new BigInteger("-1"); 26 } 27 //由于我们只求乘法逆元 所以这里使用BigInteger.One,实际中如果需要更灵活可以多传递一个参数,表示模的值来代替这里 28 x=x.multiply(BigInteger.ONE.divide(gcd)); 29 k=k.abs(); 30 BigInteger ans=x.mod(k); 31 if(ans.compareTo(BigInteger.ZERO)<0) ans=ans.add(k); 32 return ans; 33 34 }
我们在生成中只需要
1 BigInteger a=cal(b,k);
就可以在Log级别的时间内计算出a的值
将以上代码结合包装成的 RSAGeneratorKey.java
1 package com.khalid.pki; 2 3 import java.math.BigInteger; 4 import java.security.SecureRandom; 5 import java.util.Date; 6 7 public class RSAGeneratorKey { 8 9 private static BigInteger x; //存储临时的位置变量x,y 用于递归 10 11 private static BigInteger y; 12 13 14 //欧几里得扩展算法 15 public static BigInteger ex_gcd(BigInteger a,BigInteger b){ 16 if(b.intValue()==0){ 17 x=new BigInteger("1"); 18 y=new BigInteger("0"); 19 return a; 20 } 21 BigInteger ans=ex_gcd(b,a.mod(b)); 22 BigInteger temp=x; 23 x=y; 24 y=temp.subtract(a.divide(b).multiply(y)); 25 return ans; 26 27 } 28 29 //求反模 30 public static BigInteger cal(BigInteger a,BigInteger k){ 31 BigInteger gcd=ex_gcd(a,k); 32 if(BigInteger.ONE.mod(gcd).intValue()!=0){ 33 return new BigInteger("-1"); 34 } 35 //由于我们只求乘法逆元 所以这里使用BigInteger.One,实际中如果需要更灵活可以多传递一个参数,表示模的值来代替这里 36 x=x.multiply(BigInteger.ONE.divide(gcd)); 37 k=k.abs(); 38 BigInteger ans=x.mod(k); 39 if(ans.compareTo(BigInteger.ZERO)<0) ans=ans.add(k); 40 return ans; 41 42 } 43 44 public static RsaKeyPair generatorKey(int bitlength){ 45 SecureRandom random=new SecureRandom(); 46 random.setSeed(new Date().getTime()); 47 BigInteger bigPrimep,bigPrimeq; 48 while(!(bigPrimep=BigInteger.probablePrime(bitlength, random)).isProbablePrime(1)){ 49 continue; 50 }//生成大素数p 51 52 while(!(bigPrimeq=BigInteger.probablePrime(bitlength, random)).isProbablePrime(1)){ 53 continue; 54 }//生成大素数q 55 56 BigInteger n=bigPrimep.multiply(bigPrimeq);//生成n 57 //生成k 58 BigInteger k=bigPrimep.subtract(BigInteger.ONE).multiply(bigPrimeq.subtract(BigInteger.ONE)); 59 //生成一个比k小的b,或者使用65537 60 BigInteger b=BigInteger.probablePrime(bitlength-1, random); 61 //根据扩展欧几里得算法生成b 62 BigInteger a=cal(b,k); 63 //存储入 公钥与私钥中 64 PrivateKey privateKey=new PrivateKey(n, a); 65 PublicKey publicKey=new PublicKey(n, b); 66 67 //生成秘钥对 返回密钥对 68 return new RsaKeyPair(publicKey, privateKey); 69 } 70 }
编写一个简单的JUnit测试代码,没有把结果以字节流编码显示 ,比较懒。。。。
1 package com.khalid.pki; 2 3 import org.junit.Test; 4 5 public class RSATest { 6 7 @Test 8 public void testGeneratorKey(){ 9 RsaKeyPair keyPair=RSAGeneratorKey.generatorKey(256); 10 System.out.println("n的值是:"+keyPair.getPublicKey().getN()); 11 System.out.println("公钥b:"+keyPair.getPublicKey().getB()); 12 System.out.println("私钥a:"+keyPair.getPrivateKey().getA()); 13 } 14 15 16 }
这样秘钥对的生成工作就完成了
加密与解密
加密与解密的根本就是使用E(x)=xb mod n D(y)=ya mod n这两个公式,所以我们首先要将数据转化为最底层的二进制串,在转换为BigInteger进行计算,将结果做成Base64码
代码如下
1 package com.khalid.pki; 2 3 import java.io.UnsupportedEncodingException; 4 import java.math.BigInteger; 5 import java.util.Arrays; 6 7 import org.apache.commons.codec.binary.Base64; 8 9 public class RSAUtil { 10 11 public static String encrypt(String source,PublicKey key,String charset){ 12 byte[] sourceByte = null; 13 try { 14 sourceByte = source.getBytes(charset); 15 } catch (UnsupportedEncodingException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } 19 BigInteger temp=new BigInteger(1,sourceByte); 20 BigInteger encrypted=temp.modPow(key.getB(), key.getN()); 21 return Base64.encodeBase64String(encrypted.toByteArray()); 22 } 23 24 public static String decrypt(String cryptdata,PrivateKey key,String charset) throws UnsupportedEncodingException{ 25 byte[] byteTmp=Base64.decodeBase64(cryptdata); 26 BigInteger cryptedBig=new BigInteger(byteTmp); 27 byte[] cryptedData=cryptedBig.modPow(key.getA(), key.getN()).toByteArray(); 28 cryptedData=Arrays.copyOfRange(cryptedData, 1, cryptedData.length);//去除符号位的字节 29 return new String(cryptedData,charset); 30 31 } 32 }
很坑爹的一点是要记得BigInteger是有符号位的。重组String时要记得去除符号位,不然中文的时候会莫名其妙在第一位多出空格。
在编写一个测试代码就Ok了
1 package com.khalid.pki; 2 3 import java.io.UnsupportedEncodingException; 4 import org.junit.Test; 5 6 public class RSATest { 7 8 9 @Test 10 public void testRSA() throws UnsupportedEncodingException{ 11 //生成KeyPair 12 RsaKeyPair keyPair=RSAGeneratorKey.generatorKey(1024); 13 // 元数据 14 String source = new String("哈哈哈哈哈~~~嘿嘿嘿嘿嘿~~---呵呵呵呵呵!"); 15 16 System.out.println("加密前:"+source); 17 //使用公钥加密 18 String cryptdata=RSAUtil.encrypt(source, keyPair.getPublicKey(),"UTF-8"); 19 System.out.println("加密后:"+cryptdata); 20 //使用私钥解密 21 try { 22 String result=RSAUtil.decrypt(cryptdata, keyPair.getPrivateKey(),"UTF-8"); 23 System.out.println("解密后:"+result); 24 System.out.println(result.equals(source)); 25 } catch (UnsupportedEncodingException e) { 26 // TODO Auto-generated catch block 27 e.printStackTrace(); 28 } 29 30 } 31 }
测试结果。bingo。
以上是关于RSA 加解密(Java 实现)的主要内容,如果未能解决你的问题,请参考以下文章