生成公钥私钥配对加解密
注意:
RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BASE64 码再传输。
RSA 加密时,对要加密数据的大小有限制,最大不大于密钥长度。例如在使用 1024 bit 的密钥时(genrsa -out rsa_private_key.pem 1024),最大可以加密 1024/8=128 Bytes 的数据。数据大于 128 Bytes 时,需要对数据进行分组加密(如果数据超限,加解密时会失败,openssl 函数会返回 false),分组加密后的加密串拼接成一个字符串后发送给客户端。
为了保证每次加密的结果都不同,RSA 加密时会在待加密数据后拼接一个随机字符串,再进行加密。不同的填充方式 Padding 表示这个字符串的不同长度,在对超限数据进行分组后,会按照这个 Padding 指定的长度填入随机字符串。例如如果 Padding 填充方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用 11 个字节用于填充),那么明文长度最多只能就是 128-11=117 Bytes。
一般默认使用 OPENSSL_PKCS1_PADDING。PHP 支持的 Padding 有 OPENSSL_PKCS1_PADDING、OPENSSL_SSLV23_PADDING、OPENSSL_PKCS1_OAEP_PADDING 和 OPENSSL_NO_PADDING。
接收方解密时也需要分组。将加密后的原始二进制数据(对于经过 BASE64 的数据,需要解码),每 128 Bytes 分为一组,然后再进行解密。解密后,根据 Padding 的长度丢弃随机字符串,把得到的原字符串拼接起来,就得到原始报文。
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.util.Base64Utils;
import com.bessky.hrmis.common.uconfig.UConfigHelper;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class RSAHelper
{
private static final String publicKeyBase64 = "";
private static final String privateKeyBase64 = "";
/**
* RSA密钥长度必须是64的倍数,在512~65536之间。默认是1024
*/
public static final int KEY_SIZE = 2048;
/**
* 获取公钥对象
*
* @param publicKeyBase64
* @return
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public static PublicKey getPublicKey(String publicKeyBase64) throws InvalidKeySpecException, NoSuchAlgorithmException, Base64DecodingException
{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicpkcs8KeySpec = new X509EncodedKeySpec(Base64.decode(publicKeyBase64));
PublicKey publicKey = keyFactory.generatePublic(publicpkcs8KeySpec);
return publicKey;
}
/**
* 获取私钥对象
*
* @param privateKeyBase64
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
public static PrivateKey getPrivateKey(String privateKeyBase64) throws NoSuchAlgorithmException, InvalidKeySpecException, Base64DecodingException
{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privatekcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyBase64));
PrivateKey privateKey = keyFactory.generatePrivate(privatekcs8KeySpec);
return privateKey;
}
/**
* 使用工钥加密
*
* @param content 待加密内容
* @param publicKeyBase64 公钥 base64 编码
* @return 经过 base64 编码后的字符串
*/
public static String encipher(String content)
{
return encipher(content, publicKeyBase64, KEY_SIZE / 8 - 11);
}
/**
* 使用公司钥加密(分段加密)
*
* @param content 待加密内容
* @param publicKeyBase64 公钥 base64 编码
* @param segmentSize 分段大小,一般小于 keySize/8(段小于等于0时,将不使用分段加密)
* @return 经过 base64 编码后的字符串
*/
public static String encipher(String content, String publicKeyBase64, int segmentSize)
{
try
{
PublicKey publicKey = getPublicKey(publicKeyBase64);
return encipher(content, publicKey, segmentSize);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
/**
* 分段加密
*
* @param ciphertext 密文
* @param key 加密秘钥
* @param segmentSize 分段大小,<=0 不分段
* @return
*/
public static String encipher(String ciphertext, java.security.Key key, int segmentSize)
{
try
{
// 用公钥加密
byte[] srcBytes = ciphertext.getBytes();
// Cipher负责完成加密或解密工作,基于RSA
Cipher cipher = Cipher.getInstance("RSA");
// 根据公钥,对Cipher对象进行初始化
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] resultBytes = null;
if (segmentSize > 0)
resultBytes = cipherDoFinal(cipher, srcBytes, segmentSize); // 分段加密
else
resultBytes = cipher.doFinal(srcBytes);
String base64Str = Base64Utils.encodeToString(resultBytes);
return base64Str;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
/**
* 分段大小
*
* @param cipher
* @param srcBytes
* @param segmentSize
* @return
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws IOException
*/
public static byte[] cipherDoFinal(Cipher cipher, byte[] srcBytes, int segmentSize) throws IllegalBlockSizeException, BadPaddingException, IOException
{
if (segmentSize <= 0)
throw new RuntimeException("分段大小必须大于0");
ByteArrayOutputStream out = new ByteArrayOutputStream();
int inputLen = srcBytes.length;
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0)
{
if (inputLen - offSet > segmentSize)
{
cache = cipher.doFinal(srcBytes, offSet, segmentSize);
}
else
{
cache = cipher.doFinal(srcBytes, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * segmentSize;
}
byte[] data = out.toByteArray();
out.close();
return data;
}
/**
* 使用私钥解密
*
* @param contentBase64 待加密内容,base64 编码
* @param privateKeyBase64 私钥 base64 编码
* @return
* @segmentSize 分段大小
*/
public static String decipher(String contentBase64)
{
return decipher(contentBase64, privateKeyBase64, KEY_SIZE / 8);
}
/**
* 使用私钥解密(分段解密)
*
* @param contentBase64 待加密内容,base64 编码
* @param privateKeyBase64 私钥 base64 编码
* @return
* @segmentSize 分段大小
*/
public static String decipher(String contentBase64, String privateKeyBase64, int segmentSize)
{
try
{
PrivateKey privateKey = getPrivateKey(privateKeyBase64);
return decipher(contentBase64, privateKey, segmentSize);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
/**
* 分段解密
*
* @param contentBase64 密文
* @param key 解密秘钥
* @param segmentSize 分段大小(小于等于0不分段)
* @return
*/
public static String decipher(String contentBase64, java.security.Key key, int segmentSize)
{
try
{
// 用私钥解密
byte[] srcBytes = Base64Utils.decodeFromString(contentBase64);
// Cipher负责完成加密或解密工作,基于RSA
Cipher deCipher = Cipher.getInstance("RSA");
// 根据公钥,对Cipher对象进行初始化
deCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decBytes = null;// deCipher.doFinal(srcBytes);
if (segmentSize > 0)
decBytes = cipherDoFinal(deCipher, srcBytes, segmentSize); // 分段加密
else
decBytes = deCipher.doFinal(srcBytes);
String decrytStr = new String(decBytes);
return decrytStr;
}
catch (Exception e)
{
e.printStackTrace();
}
}
}