本文使用 Java Security API 演示 RSA 算法的使用过程。
1 RSA 加密算法属于非对称加密算法,第一步需要生成密钥对
public static KeyPair initKeyPair()
throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
生成的 java.security.KeyPair
对象虽然已经实现了 java.io.Serializable
接口,但为了更方便存储和传输,常需要使用 Base64 编码成字符串。
public static Map initKeys()
throws NoSuchAlgorithmException {
KeyPair keyPair = initKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
return new HashMap() {{
put(RSA_PUBLIC_KEY, Base64.getEncoder().encodeToString(publicKey.getEncoded()));
put(RSA_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey.getEncoded()));
}};
}
2 有了密钥对后就可以使用其中一个加密,使用另一个解密,首先给出公钥加密的代码片段
public static byte[] encryptByPublicKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
使用公钥加密后的密文只能使用私钥解密
public static byte[] decryptByPrivateKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
3 下面是使用私钥加密的代码片段
public static byte[] encryptByPrivateKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
同样,使用私钥加密的密文只能使用公钥解密
public static byte[] decryptByPublicKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
以上便是使用 Java 原生的 Security API 实现 RSA 加解密的工具代码,可以在此基础上进一步封装,如接收加解密的数据不止是 byte[]
类型,可以直接接收 String
或其他特殊类型的数据,返回的加解密后的结果也可以封装成 String
等其它类型。同一类型密钥的加解密代码中也存在大量重复代码,可以统一提取成样板代码。
以下是完整的 RSA 加解密工具类代码
package learn.spring.security.util;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
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;
public class RSAUtils {
private static final String ALGORITHM = "RSA";
private static final int KEY_SIZE = 1024;
public static final String RSA_PUBLIC_KEY = "RSA_PUBLIC_KEY";
public static final String RSA_PRIVATE_KEY = "RSA_PRIVATE_KEY";
/**
* 生成密钥对
*/
public static KeyPair initKeyPair()
throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
/**
* 生成密钥对,并保存为 Map 对象
*/
public static Map initKeys()
throws NoSuchAlgorithmException {
KeyPair keyPair = initKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
return new HashMap() {{
put(RSA_PUBLIC_KEY, Base64.getEncoder().encodeToString(publicKey.getEncoded()));
put(RSA_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey.getEncoded()));
}};
}
/**
* 公钥加密
*/
public static byte[] encryptByPublicKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*/
public static byte[] decryptByPrivateKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
*/
public static byte[] encryptByPrivateKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*/
public static byte[] decryptByPublicKey(byte[] data, String key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
}
附单元测试代码
package learn.spring.security.util;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Map;
import java.util.Objects;
public class RSAUtilsTests {
@Test
public void testInitKeyPair()
throws NoSuchAlgorithmException {
KeyPair keyPair = RSAUtils.initKeyPair();
Assert.assertNotNull(keyPair);
Assert.assertNotNull(keyPair.getPublic());
Assert.assertNotNull(keyPair.getPrivate());
Assert.assertNotEquals(keyPair.getPublic(), keyPair.getPrivate());
}
@Test
public void testInitKeys()
throws NoSuchAlgorithmException {
Map keys = RSAUtils.initKeys();
for (Map.Entry entry : keys.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Assert.assertTrue(key.equals(RSAUtils.RSA_PUBLIC_KEY) || key.equals(RSAUtils.RSA_PRIVATE_KEY));
Assert.assertTrue(Objects.nonNull(value) && !value.trim().equals(""));
}
}
@Test
public void testEncryptByPublicAndDecryptByPrivate()
throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException,
InvalidKeySpecException, NoSuchPaddingException {
String msg = "Hello, RSA!";
Map keys = RSAUtils.initKeys();
byte[] encryptedMsg = RSAUtils.encryptByPublicKey(msg.getBytes(), keys.get(RSAUtils.RSA_PUBLIC_KEY));
byte[] decryptedMsg = RSAUtils.decryptByPrivateKey(encryptedMsg, keys.get(RSAUtils.RSA_PRIVATE_KEY));
String restoredMsg = new String(decryptedMsg);
System.out.println(restoredMsg);
Assert.assertEquals(msg, restoredMsg);
}
@Test
public void testEncryptByPrivateAndDecryptByPublic()
throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException,
InvalidKeySpecException, NoSuchPaddingException {
String msg = "Introduce RSA";
Map keys = RSAUtils.initKeys();
byte[] encryptedMsg = RSAUtils.encryptByPrivateKey(msg.getBytes(), keys.get(RSAUtils.RSA_PRIVATE_KEY));
byte[] decryptedMsg = RSAUtils.decryptByPublicKey(encryptedMsg, keys.get(RSAUtils.RSA_PUBLIC_KEY));
String restoredMsg = new String(decryptedMsg);
System.out.println(restoredMsg);
Assert.assertEquals(msg, restoredMsg);
}
}