Java读取OpenSSL生成的秘钥, 进行RSA加解密
大约 4 分钟
提示
OpenSSL生成的秘钥与java RSA加解密类生成的秘钥类型不一致, 导致加载秘钥失败. 通过引入一个工具包. 来实现加解密
1. 生成秘钥
1.1 使用OpenSSL
生成秘钥
openssl genrsa -out rsa_private_key.pem 1024
1.2 查看私钥
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQCtSXcCfb5pJ/CrOWDAdHVEO8wzoj/Waskia3T9nBOXJKbf1TqT
S7bn8m/2kn/bx0p8FuK+LcH2TeCNa5DXyCE+k+qkqCXTI6U48aSKjzZ0eiCPRDm0
ZfdCJoB3l7rxOx2jO/EXtjXC3VrX0vddjR/3AGDm/TUmlIkm0IpHzdPSAwIDAQAB
AoGBAI0KxKD0c+znI05aL1O19lSJT1Wfc9IzrG4lhF1/kbptjJ2W0kxdTgxQGa1Z
5FOgTBHgFsGwQLUXAfRZXtUjAVq0sEVFzM0rWRr8B63UFokY198jL/1XgcgAp8jP
BbJRI1Mp/TdNtsdkK4bE/6pJsZrlLCpsA9uz91AaddN60bhRAkEA2t3Sk1aqfwSP
Delwv96nFwgb8XbZ5i4BgHp4UDFyoW/g6VhaLEOyM+VBUOK4iVKaNOZdtdFazlrS
NhFY18iyxwJBAMqv9LZNeDajQ2Hju1B8/ZV2plCROh8Jvy7Dike6BI/NvVgm/h9O
dsZzqenlMX24232Sl4WnUBH+gr6DzwsO6uUCQQCroMdAQvXwJOMJRcXkr/TXWsSO
1WSJ3AnRdjhiqmP5tIHqeh48F9nmWCCgDSoohV3nnb5elY1fxFiTjYbdr8SBAkBW
MBRDIRYpRat1iL0yMLQ0Rkvanqa0ZBSj8gpvDa6656XIQmx1K4xePvjVuNwetei/
sQI1lzv5Kty13p6/+QvxAkEArSPbBXRhFKVKiR9tvvz87UYA4bchiuDseGWhqB6H
37grzW5eGPfd8CAoJKWp2CmZa1Ad86+YRXvWwKhE5/vBpg==
-----END RSA PRIVATE KEY-----
1.3 生成查看公钥
openssl rsa -in rsa_private_key.pem -out rsa_public_key_1024.pub -pubout
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtSXcCfb5pJ/CrOWDAdHVEO8wz
oj/Waskia3T9nBOXJKbf1TqTS7bn8m/2kn/bx0p8FuK+LcH2TeCNa5DXyCE+k+qk
qCXTI6U48aSKjzZ0eiCPRDm0ZfdCJoB3l7rxOx2jO/EXtjXC3VrX0vddjR/3AGDm
/TUmlIkm0IpHzdPSAwIDAQAB
-----END PUBLIC KEY-----
1.4 加解密引入包
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.56</version>
</dependency>
1.5 加解密代码
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.X509EncodedKeySpec;
@Component
@Slf4j
public class RSAUtil {
@Value("${config.rsa.privateKey}")
private String privateKey;
@Value("${config.rsa.publicKey}")
private String publicKey;
public static void main(String[] args) {
RSAUtil rsaUtil = new RSAUtil();
rsaUtil.publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtSXcCfb5pJ/CrOWDAdHVEO8wzoj/Waskia3T9nBOXJKbf1TqTS7bn8m/2kn/bx0p8FuK+LcH2TeCNa5DXyCE+k+qkqCXTI6U48aSKjzZ0eiCPRDm0ZfdCJoB3l7rxOx2jO/EXtjXC3VrX0vddjR/3AGDm/TUmlIkm0IpHzdPSAwIDAQAB";
rsaUtil.privateKey = "MIICXgIBAAKBgQCtSXcCfb5pJ/CrOWDAdHVEO8wzoj/Waskia3T9nBOXJKbf1TqTS7bn8m/2kn/bx0p8FuK+LcH2TeCNa5DXyCE+k+qkqCXTI6U48aSKjzZ0eiCPRDm0ZfdCJoB3l7rxOx2jO/EXtjXC3VrX0vddjR/3AGDm/TUmlIkm0IpHzdPSAwIDAQABAoGBAI0KxKD0c+znI05aL1O19lSJT1Wfc9IzrG4lhF1/kbptjJ2W0kxdTgxQGa1Z5FOgTBHgFsGwQLUXAfRZXtUjAVq0sEVFzM0rWRr8B63UFokY198jL/1XgcgAp8jPBbJRI1Mp/TdNtsdkK4bE/6pJsZrlLCpsA9uz91AaddN60bhRAkEA2t3Sk1aqfwSPDelwv96nFwgb8XbZ5i4BgHp4UDFyoW/g6VhaLEOyM+VBUOK4iVKaNOZdtdFazlrSNhFY18iyxwJBAMqv9LZNeDajQ2Hju1B8/ZV2plCROh8Jvy7Dike6BI/NvVgm/h9OdsZzqenlMX24232Sl4WnUBH+gr6DzwsO6uUCQQCroMdAQvXwJOMJRcXkr/TXWsSO1WSJ3AnRdjhiqmP5tIHqeh48F9nmWCCgDSoohV3nnb5elY1fxFiTjYbdr8SBAkBWMBRDIRYpRat1iL0yMLQ0Rkvanqa0ZBSj8gpvDa6656XIQmx1K4xePvjVuNwetei/sQI1lzv5Kty13p6/+QvxAkEArSPbBXRhFKVKiR9tvvz87UYA4bchiuDseGWhqB6H37grzW5eGPfd8CAoJKWp2CmZa1Ad86+YRXvWwKhE5/vBpg==";
System.out.println(rsaUtil.encrypt("李四"));
System.out.println(rsaUtil.encrypt("123456789012345678"));
System.out.println("李四".equals(rsaUtil.decrypt(rsaUtil.encrypt("李四"))));
}
public String encrypt(String str) {
try {
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
log.error("encrypt failed: str[" + str + "], publicKey[" + publicKey + "]");
throw new RunTimeException("解密失败", e);
}
}
public String decrypt(String str) {
try {
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
org.bouncycastle.asn1.pkcs.RSAPrivateKey rsaPrivateKey = org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(decoded);
RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(rsaPrivKeySpec);
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
return new String(cipher.doFinal(inputByte));
} catch (Exception e) {
log.error("decrypt failed: str[" + str + "], privateKey[" + publicKey + "]", e);
throw new RunTimeException("解密失败", e);
}
}
}
1.6 RSA
秘钥类型说明
公钥加密标准(Public Key Cryptography Standards, PKCS)
PKCS | 版本 | 名称 | 简介 |
---|---|---|---|
PKCS #1 [1] | 2.1 | RSA密码编译标准(RSA Cryptography Standard) | 定义了RSA的数理基础、公/私钥格式,以及加/解密、签/验章的流程。1.5版本曾经遭到攻击。 |
PKCS #2 | - | 撤销 | 原本是用以规范RSA加密摘要的转换方式,现已被纳入PKCS#1之中。 |
PKCS #3 | 1.4 | DH密钥协议标准(Diffie-Hellman key agreement Standard) | 规范以DH密钥协议为基础的密钥协议标准。其功能,可以让两方通过金议协议,拟定一把会议密钥(Session key)。 |
PKCS #4 | - | 撤销 | 原本用以规范转换RSA密钥的流程。已被纳入PKCS#1之中。 |
PKCS #5 | 2.0 | 密码基植加密标准(Password-based Encryption Standard) | 参见RFC 2898与PBKDF2。 |
PKCS #6 | 1.5 | 证书扩展语法标准(Extended-Certificate Syntax Standard) | 将原本X.509的证书格式标准加以扩充。 |
PKCS #7 | 1.5 | 密码消息语法标准(Cryptographic Message Syntax Standard) | 参见RFC 2315。规范了以公开密钥基础设施(PKI)所产生之签名/密文之格式。其目的一样是为了拓展数字证书的应用。其中,包含了S/MIME与CMS。 |
PKCS #8 [2] | 1.2 | 私钥消息表示标准(Private-Key Information Syntax Standard). | Apache读取证书私钥的标准。 |
PKCS #9 | 2.0 | 选择属性格式(Selected Attribute Types) | 定义PKCS#6、7、8、10的选择属性格式。 |
PKCS #10 | 1.7 | 证书申请标准(Certification Request Standard) 参见RFC 2986。 | 规范了向证书中心申请证书之CSR(certificate signing request)的格式。 |
PKCS #11 | 2.20 | 密码设备标准接口(Cryptographic Token Interface (Cryptoki)) | 定义了密码设备的应用程序接口(API)之规格。 |
PKCS #12 | 1.0 | 个人消息交换标准(Personal Information Exchange Syntax Standard) | 定义了包含私钥与公钥证书(public key certificate)的文件格式。私钥采密码(password)保护。常见的PFX就履行了PKCS#12。 |
PKCS #13 | – | 椭圆曲线密码学标准(Elliptic curve cryptography Standard) | 制定中。规范以椭圆曲线密码学为基础所发展之密码技术应用。椭圆曲线密码学是新的密码学技术,其强度与效率皆比现行以指数运算为基础之密码学算法来的优秀。然而,该算法的应用尚不普及。 |
PKCS #14 | – | 拟随机数产生器标准(Pseudo-random Number Generation) | 制定中。规范拟随机数产生器的使用与设计。 |
PKCS #15 | 1.1 | 密码设备消息格式标准(Cryptographic Token Information Format Standard) | 定义了密码 |
java默认使用的RSAKey
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
2 RSA
格式转换
2.1 生成PKCS1
私钥
OpenSSL
生成PKCS1
格式的私钥,密钥长度1024位
openssl genrsa -out private.pem 1024
2.2 PKCS1
私钥转换为PKCS8
openssl pkcs8 -topk8 -inform PEM -in private.pem -outform pem -nocrypt -out pkcs8.pem
2.3 PKCS8
私钥转换为PKCS1
openssl rsa -in pkcs8.pem -out pkcs1.pem
2.4 从PKCS1
私钥中生成PKCS8
公钥
openssl rsa -in private.pem -pubout -out public.pem
2.5 从PKCS8
私钥中生成PKCS8
公钥
openssl rsa -in pkcs8.pem -pubout -out public_pkcs8.pem
与2.4生成的结果一致
2.6 从PKCS8
公钥转换PKCS1
公钥
openssl rsa -pubin -in public_pkcs8.pem -RSAPublicKey_out
生成前后文件一致
2.7 从PKCS1
公钥转换PKCS8
公钥
openssl rsa -RSAPublicKey_in -in pub_pkcs1.pem -pubout
生成前后文件一致