RSA 算法是一种非对称加密算法,会生成一对 RSA 秘钥,即公钥+私钥,将公钥提供给调用方,调用方使用公钥对数据进行加密后,接口根据私钥进行解密
RSA 加解密工具类
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.Date; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class RSAUtil { public static final String KEY_ALGORITHM = "RSA"; private static final String PUBLIC_KEY = "RSAPublicKey"; private static final String PRIVATE_KEY = "RSAPrivateKey"; /** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** * 生成密钥对 */ public static Map<String, Object> initKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); //设置密钥对的bit数 越大越安全 keyPairGen.initialize(1024); 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) throws Exception { //获得map中的公钥对象 转为key对象 Key key = (Key) keyMap.get(PUBLIC_KEY); //编码返回字符串 return encryptBASE64(key.getEncoded()); } /** * 获得私钥字符串 */ public static String getPrivateKeyStr(Map<String, Object> keyMap) throws Exception { //获得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); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; } /** * 获取私钥 */ 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); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; } /** * BASE64解码 返回字节数组 * key 需要解码的字符串 */ public static byte[] decryptBASE64(String key) { return Base64.getDecoder().decode(key); } /** * BASE64编码返回加密字符串 * key 需要编码的字节数组 */ public static String encryptBASE64(byte[] key) throws Exception { return new String(Base64.getEncoder().encode(key)); } /** * 公钥加密 */ public static String encrypto(String text, String publicKeyStr) { try { System.out.println("明文lenth为"+text.length()); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr)); byte tempBytes[] = cipher.doFinal(text.getBytes()); String secretText = Base64.getEncoder().encodeToString(tempBytes); return secretText; } catch (Exception e) { throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e); } } /** * 私钥解密 * * @param secretText */ public static String decrypto(String secretText, String privateKeyStr) { try { //生成公钥 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr)); // 密文解码 byte[] secretText_decode = Base64.getDecoder().decode(secretText.getBytes()); byte tempBytes[] = cipher.doFinal(secretText_decode); String text = new String(tempBytes); return text; } catch (Exception e) { throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e); } } public static void main(String[] args) throws Exception { Map<String, Object> keyMap; String cipherText; String input = "数字测试就是多久覅偶按实际说就大佛我按时间覅哦啊师傅欧艾斯师傅到了独爱偶素大放"; try { keyMap = initKey(); String publicKey = getPublicKeyStr(keyMap); System.out.println("公钥------------------"); System.out.println(publicKey); System.out.println("length: " + publicKey.length()); String privateKey = getPrivateKeyStr(keyMap); System.out.println("私钥------------------"); System.out.println(privateKey); System.out.println("length: " + privateKey.length()) cipherText = encrypto(input, publicKey); //加密后的东西 System.out.println("密文=======" + cipherText); System.out.println("length: " + cipherText.length()); //开始解密 String plainText = decrypto(cipherText, privateKey); System.out.println("解密后明文===== " + plainText); } catch (Exception e) { e.printStackTrace(); } } }
如果所示, 生成一对1024 bit 的 RSA 密钥对,并加解密成功。
报文长度过长加解密失败
测试发现当明文过长时,加密异常,返回如下报错
原因
RSA 加解密时,对加密的数据大小有限制,最大不大于密钥长度。
在使用 1024 位的密钥时,最大可以加密 1024/8 = 128字节的数据,此时需要对数据进行分组加密,分组加密后的加密串拼接成一个字符串返回给客户端。如果 Padding 方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用11字节用于填充),则明文长度最多为 128 - 11 = 117 Bytes
同理当解密的密文超过128Byte时,也需要进行分组解密
分段加解密
/** * 分段加密 */ public static String encrypt(String plainText, String publicKeyStr) throws Exception { System.out.println("明文lenth为"+plainText.length()); byte[] plainTextArray = plainText.getBytes(); 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 decrypt(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 bit
如上文提到, 当密钥对改为 2048 位时, 最大加密明文大小 = 2048(bit) / 8 - 11(byte) = 245 byte
/** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 245; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 256; /** * 生成密钥对 */ public static Map<String, Object> initKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); //设置密钥对的bit数 越大越安全 keyPairGen.initialize(2048); 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; }
附
PKCS1 和 PKCS8 的区别
- PKCS#1格式
以-----BEGIN RSA PRIVATE KEY-----开头 以-----END RSA PRIVATE KEY-----结束
以-----BEGIN PRIVATE KEY-----开头 以-----END PRIVATE KEY-----结束
通常JAVA中需要PKCS8 格式的密钥
在线 RSA 加解密网站