1.RSA工具类
package com.river.blog.utils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
/**
* RSA工具类
* @author River
* @date 2018/6/14
*/
public class RSAUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
private static final String ALGORITHM = "RSA";
private static final String CHARSET = "UTF-8";
private static final int DEFAULT_KEY_SIZE = 1024;
/**
* 获取公钥和私钥
* @return
*/
public static KeyPair getKeys() {
KeyPair keyPair = null;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(DEFAULT_KEY_SIZE);
keyPair = keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
LOGGER.error("Get keys error! Exception:{}", e.getMessage());
}
return keyPair;
}
/**
* 公钥加密
* @param data
* @param publicKey
* @return
*/
public static String publicEncrypt(String data, Key publicKey) {
String result = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = cipher.doFinal(Base64.encodeBase64(data.getBytes(CHARSET)));
result = Base64.encodeBase64URLSafeString(bytes);
} catch (Exception e) {
LOGGER.error("Encrypted with a public key error! Exception:{}", e.getMessage());
}
return result;
}
/**
* 私钥解密
* @param data
* @param privateKey
* @return
*/
public static String privateDecrypt(String data, Key privateKey) {
String result = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes = cipher.doFinal(Base64.decodeBase64(data));
result = new String(Base64.decodeBase64(bytes));
} catch (Exception e) {
LOGGER.error("Decrypted with a private key error! Exception:{}", e.getMessage());
}
return result;
}
public static void main(String[] args) throws Exception {
KeyPair keys = getKeys();
String data = "Hello World!(你好 世界!)";
System.out.println("准备加密的数据是:" + data);
System.out.println("数据的长度是:" + data.getBytes(CHARSET).length);
String encryptData = publicEncrypt(data, keys.getPublic());
System.out.println("通过公钥加密后的数据是:" + encryptData);
String decryptData = privateDecrypt(encryptData, keys.getPrivate());
System.out.println("通过私钥解密后的数据是:" + decryptData);
}
public RSAUtils() {
}
}
测试结果:
经过测试,加密的时候,如果数据的长度超过117byte,会报错:Data must not be longer than 117 bytes
跟踪一下源码看看这个117是怎么来的,
查看源码可以发现有一个方法:
public static int getByteLength(BigInteger var0) {
int var1 = var0.bitLength();
return var1 + 7 >> 3;
}
这个是IDEA反编译出来的结果
var1的值就是之前设置过得keySize的值
写一个main方法测试一下:
public static void main(String[] args) {
System.out.println(1024+7>>3);
System.out.println(2048+7>>3);
}
结果:(小发现:+号的优先级大于>>)
128
256
长度是128和256,keySize是1024的情况下长度限制是128,2048是256,但是事实上keySize=1024的时候长度限制并不是128而是117。
还有一个方法:RSAPadding.getInstance(var10, var7, var3);IDEA反编译的代码有点乱就不贴上来了。
里面写了maxDataSize=byteLength-11;所以才会有117这么个限制,如果把keySize设置成2048,
那么会提示超过了245(256-11)bytes的限制。
解密的时候限制是128和256,也就是keySize>>3的结果。
好,找到了问题,那么改善一下代码:
package com.river.blog.utils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
/**
* RSA工具类
* @author River
* @date 2018/6/14
*/
public class RSAUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
private static final String ALGORITHM = "RSA";
private static final String CHARSET = "UTF-8";
private static final int DEFAULT_KEY_SIZE = 1024;
/**
* 获取公钥和私钥
* @return
*/
public static KeyPair getKeys() {
KeyPair keyPair = null;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(DEFAULT_KEY_SIZE);
keyPair = keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
LOGGER.error("Get keys error! Exception:{}", e.getMessage());
}
return keyPair;
}
/**
* 公钥加密
* @param data
* @param publicKey
* @return
*/
public static String publicEncrypt(String data, Key publicKey) {
String result = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = doFinal(cipher, Cipher.ENCRYPT_MODE, Base64.encodeBase64(data.getBytes(CHARSET)));
result = Base64.encodeBase64URLSafeString(bytes);
} catch (Exception e) {
LOGGER.error("Encrypted with a public key error! Exception:{}", e.getMessage());
}
return result;
}
/**
* 私钥解密
* @param data
* @param privateKey
* @return
*/
public static String privateDecrypt(String data, Key privateKey) {
String result = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes = doFinal(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data.getBytes(CHARSET)));
result = new String(Base64.decodeBase64(bytes));
} catch (Exception e) {
LOGGER.error("Decrypted with a private key error! Exception:{}", e.getMessage());
}
return result;
}
/**
* 真正要加密/解密执行的方法,包含了分段加密/解密
* @param cipher
* @param mode
* @param data
* @return
* @throws Exception
*/
private static byte[] doFinal(Cipher cipher, int mode, byte[] data) throws Exception {
int maxByteLength = DEFAULT_KEY_SIZE >> 3;
if (mode == Cipher.ENCRYPT_MODE) {
maxByteLength -= 11;
}
int dataLength = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream(dataLength);
int offSet;
int index = 0;
byte[] tmp;
while (dataLength > (offSet=index*maxByteLength)) {
if (dataLength-offSet>maxByteLength) {
tmp = cipher.doFinal(data, offSet, maxByteLength);
} else {
tmp = cipher.doFinal(data, offSet, dataLength-offSet);
}
index++;
out.write(tmp, 0, tmp.length);
}
byte[] result = out.toByteArray();
out.close();
return result;
}
public static void main(String[] args) throws Exception {
KeyPair keys = getKeys();
String data = "今日头条是个挺好的软件,每天在上面看一些新闻,看一些笑话,看一些视频。\n" +
"确实是挺有意思,但是也浪费了很多时间。不过最近关注了一个名字是‘Excel精选技巧’\n" +
"的头条号,他会经常发一些Excel的使用技巧,还有一些Word的使用技巧。我想不定期的看\n" +
"他的视频,同时自己实验把这些技巧学会,用写博客的方式记录下来(也搜一下别的关于\n" +
"Office技巧的视频)。";
System.out.println("准备加密的数据是:\n" + data);
System.out.println("数据的长度是:" + data.getBytes(CHARSET).length);
String encryptData = publicEncrypt(data, keys.getPublic());
System.out.println("通过公钥加密后的数据是:\n" + encryptData);
String decryptData = privateDecrypt(encryptData, keys.getPrivate());
System.out.println("通过私钥解密后的数据是:\n" + decryptData);
}
public RSAUtils() {
}
}
测试结果: