一、什么是非对称加密算法?
简单点讲,就是加密密钥和解密密钥不一样的一种加密算法。非对称加密是指通过特定算法获取一对密钥对:公钥和私钥,公钥可以对外公开,私钥由你自己保存。我们使用其中一个密钥对数据进行加密,使用另一个密钥对加密后的数据进行解密。
优点:保密性比较好,不需要用户交换密钥,不适合于对文件加密;
缺点:加密和解密花费时间长、速度慢,适用于对少量数据进行加密。
常见的非对称加密算法有:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)。
二、RSA加解密
PKCS1Padding,最常用的填充方式,要求
输入:必须 比 密钥长度 短至少11个字节, 也就是 KEYSIZE/8 – 11,如果输入的明文过长,必须切割,然后填充
输出:和密钥长度一样长
具体算法以及填充方式感兴趣的可以自己去网上找下。
在 pom.xml文件中加入:
commons-codec
commons-codec
1.10
RSAUtil.java:
package com.su.mybatis.mysql.util;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
/**
* @Title: RSAUtil.java
* @author sucb
* @date 2017年3月28日 下午8:09:41
*/
public final class RSAUtil {
private RSAUtil() {
}
/**
* 密钥长度(bit)
*/
private static final int KEYSIZE = 1024;
/**
* CIPHER_ALGORITHM RSA
*/
private static final String CIPHER_ALGORITHM_RSA = "RSA/ECB/PKCS1Padding";
/**
* RSA加密
*/
public static final String KEY_ALGORITHM_RSA = "RSA";
/**
* RSAPublicKey
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* RSAPrivateKey
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 获取配对的公钥和私钥
* @return keyMap
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:29:51
*/
public static Map initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA);
keyPairGen.initialize(KEYSIZE);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map keyMap = new HashMap(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 对公钥进行编码转换,并进行BASE64转码
* @param keyMap 配对的公钥和私钥组成的map
* @return 处理后的公钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:33:03
*/
public static String getPublicKey(Map keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* 对私钥进行编码转换,并进行BASE64转码
* @param keyMap 配对的公钥和私钥组成的map
* @return 处理后的私钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:34:39
*/
public static String getPrivateKey(Map keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* RSA加密
* @param publicKeyStr 公钥
* @param plainText 需要加密的数组
* @return 加密后数组
* @author sucb
* @date 2017年3月29日下午2:45:26
*/
public static byte[] encodeRSA(String publicKeyStr, byte[] plainText) {
try {
RSAPublicKey key = restorePublicKey(publicKeyStr);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_RSA);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* RSA解密
* @param privateKeyStr 私钥
* @param plainText 需要解密的数组
* @return 解密后数组
* @author sucb
* @date 2017年3月29日下午2:46:14
*/
public static byte[] decodeRSA(String privateKeyStr, byte[] plainText) {
try {
RSAPrivateKey key = restorePrivateKey(privateKeyStr);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_RSA);
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(plainText);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 还原公钥,X509EncodedKeySpec 用于构建公钥的规范
* @param publicKeyStr 公钥
* @return 还原后的公钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:47:54
*/
private static RSAPublicKey restorePublicKey(String publicKeyStr) throws Exception {
byte[] keyBytes = decryptBASE64(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM_RSA);
RSAPublicKey publicKey = (RSAPublicKey) factory.generatePublic(x509EncodedKeySpec);
return publicKey;
}
/**
* 还原私钥 PKCS8EncodedKeySpec
* @param privateKeyStr 私钥
* @return 还原后的私钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:47:37
*/
private static RSAPrivateKey restorePrivateKey(String privateKeyStr) throws Exception {
byte[] keyBytes = decryptBASE64(privateKeyStr);
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM_RSA);
RSAPrivateKey privateKey = (RSAPrivateKey) factory.generatePrivate(priPKCS8);
return privateKey;
}
/**
* 进行BASE64转码
* @param key byte型密钥
* @return 转码后的String型密钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:49:58
*/
private static String encryptBASE64(byte[] key) throws Exception {
return Base64.encodeBase64String(key);
}
/**
* 进行BASE64解码
* @param key String型密钥
* @return 解码后的byte型密钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:52:29
*/
private static byte[] decryptBASE64(String key) throws Exception {
return Base64.decodeBase64(key);
}
public static void main(String[] args) throws Exception {
Map keyMap = initKey();
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
String a = "aaaaaaaaaaaa";
byte[] miByte = encodeRSA(publicKey, a.getBytes());
byte[] mingByte = decodeRSA(privateKey, miByte);
String actual = new String(mingByte);
System.out.println("actual : " + actual);
System.out.println(a.equals(actual));
}
}
上面方法只适用于输入的明文长度比密钥长度少11个字节的情况,明文长度不限的可以参考下下面这个
RSAUtils.java:
package com.su.mybatis.mysql.util;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
public final class RSAUtils {
private RSAUtils() {
}
/**
* 密钥长度(bit)
*/
private static final int KEYSIZE = 1024;
/**
* 最大加密明文长度
*/
private static final int MAX_ENCODE_LENGTH = KEYSIZE/8 -11;
/**
* 最大解密文长度
*/
private static final int MAX_DECODE_LENGTH = KEYSIZE/8;
/**
* CIPHER_ALGORITHM RSA
*/
private static final String CIPHER_ALGORITHM_RSA = "RSA/ECB/PKCS1Padding";
/**
* RSA加密
*/
public static final String KEY_ALGORITHM_RSA = "RSA";
/**
* RSAPublicKey
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* RSAPrivateKey
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 获取配对的公钥和私钥
* @return keyMap
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:29:51
*/
public static Map initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA);
keyPairGen.initialize(KEYSIZE);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map keyMap = new HashMap(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 对公钥进行编码转换,并进行BASE64转码
* @param keyMap 配对的公钥和私钥组成的map
* @return 处理后的公钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:33:03
*/
public static String getPublicKey(Map keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* 对私钥进行编码转换,并进行BASE64转码
* @param keyMap 配对的公钥和私钥组成的map
* @return 处理后的私钥
* @throws Exception 异常
* @author sucb
* @date 2017年3月29日下午2:34:39
*/
public static String getPrivateKey(Map keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* RSA加密
* @param publicKeyStr 公钥
* @param plainText 需要加密的数组
* @return 加密后数组
* @author sucb
* @date 2017年3月29日下午2:45:26
*/
public static byte[] encodeRSA(String publicKeyStr, byte[] plainText) {
try {
int i = plainText.length/(MAX_DECODE_LENGTH-11) +1; //加密数组需要切割的段数
byte[] resultByte = new byte[i * KEYSIZE/8];//用于接收加密后数组
for (int j = 0; j < i; j++) {
byte[] adata ={}; //需要加密的每段数据
if(plainText.length <= adata.length) {//数据不需要切割的情况
adata = new byte[plainText.length];
System.arraycopy(plainText, 0, adata, 0, plainText.length);
}else {
if(j keyMap = initKey();
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
String a = "aaaaaaaaaaaa";
byte[] miByte = encodeRSA(publicKey, a.getBytes());
//将密文转换成String,使用Base64.encodeBase64String转换;转换后密文转byte,使用Base64.decodeBase64。
System.out.println(Arrays.equals(miByte, Base64.decodeBase64(Base64.encodeBase64String(miByte))));
byte[] mingByte = decodeRSA(privateKey, miByte);
String actual = new String(mingByte);
System.out.println("actual : " + actual);
System.out.println(a.equals(actual));
String message = "然而,这几乎是个不可能完成的任务。当p和q是非常大的质数时,根据pq的乘积去分解因子p和q,这是数学上公认的难题。通常,p和q都会选的非常大,比如说200位。这导致n也非常大,有400位。寻找一个400位数字的质数分解并不容易,我们要做的除法运算次数大约为10 199!世界最强的超级计算机天河2号每秒浮点运算是1016级别。那么,分解出p和q,大约需要10174年。10174就是1的后面跟上174个0,时间是不是很长?";
byte[] miByte2 = encodeRSA(publicKey, message.getBytes());
byte[] mingByte2 = decodeRSA(privateKey, miByte2);
String actual2 = new String(mingByte2);
System.out.println("actual2 : " + actual2);
System.out.println(message.equals(actual2));
}
}
以上就是RSA加解密。
如果有写的不对的地方,请大家多多批评指正,非常感谢!