Java 如何读取.pem文件以获取私钥和公钥
我正在写一小段代码,读取存储在.pem文件中的公钥和私钥。我正在使用以下命令生成关键点 下面的命令生成一对密钥Java 如何读取.pem文件以获取私钥和公钥,java,openssl,x509,pem,pkcs#8,Java,Openssl,X509,Pem,Pkcs#8,我正在写一小段代码,读取存储在.pem文件中的公钥和私钥。我正在使用以下命令生成关键点 下面的命令生成一对密钥 $openssl genrsa -out mykey.pem 2048 此命令用于生成私钥 $openssl pkcs8 -topk8 -inform PEM -outform PEM -in mykey.pem \ -out private_key.pem -nocrypt 并使用此命令获取公钥 $ openssl rsa -in mykey.pem -pubout
$openssl genrsa -out mykey.pem 2048
此命令用于生成私钥
$openssl pkcs8 -topk8 -inform PEM -outform PEM -in mykey.pem \
-out private_key.pem -nocrypt
并使用此命令获取公钥
$ openssl rsa -in mykey.pem -pubout -outform DER -out public_key.der
我写了两个方法,分别读取私钥和公钥
public PrivateKey getPemPrivateKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----\n", "");
privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
//System.out.println("Private key\n"+privKeyPEM);
Base64 b64 = new Base64();
byte [] decoded = b64.decode(privKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
public PublicKey getPemPublicKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String publicKeyPEM = temp.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
Base64 b64 = new Base64();
byte [] decoded = b64.decode(publicKeyPEM);
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(spec);
}
我觉得这样做很幼稚。我在网上找不到比这更好的方法了。有谁能告诉我,编写相同代码来处理泛型情况的最佳方法是什么。我不想使用任何类型的第三方库。我有非常基本的唱歌/加密知识,几乎不使用任何java安全API。因此,如果我在某个地方没有意义,请指出。好吧,我的代码和你的一样,没有什么区别
public static X509Certificate loadPublicX509(String fileName)
throws GeneralSecurityException {
InputStream is = null;
X509Certificate crt = null;
try {
is = fileName.getClass().getResourceAsStream("/" + fileName);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
crt = (X509Certificate)cf.generateCertificate(is);
} finally {
closeSilent(is);
}
return crt;
}
public static PrivateKey loadPrivateKey(String fileName)
throws IOException, GeneralSecurityException {
PrivateKey key = null;
InputStream is = null;
try {
is = fileName.getClass().getResourceAsStream("/" + fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
boolean inKey = false;
for (String line = br.readLine(); line != null; line = br.readLine()) {
if (!inKey) {
if (line.startsWith("-----BEGIN ") &&
line.endsWith(" PRIVATE KEY-----")) {
inKey = true;
}
continue;
}
else {
if (line.startsWith("-----END ") &&
line.endsWith(" PRIVATE KEY-----")) {
inKey = false;
break;
}
builder.append(line);
}
}
//
byte[] encoded = DatatypeConverter.parseBase64Binary(builder.toString());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
key = kf.generatePrivate(keySpec);
} finally {
closeSilent(is);
}
return key;
}
public static void closeSilent(final InputStream is) {
if (is == null) return;
try { is.close(); } catch (Exception ign) {}
}
一种选择是使用bouncycastle的: 用于解析包含X509的OpenSSL PEM编码流的类 证书、PKCS8编码密钥和PKCS7对象 对于PKCS7对象,读取器将返回CMS ContentInfo 对象公钥将返回格式正确的密钥 SubjectPublicKeyInfo对象,也将返回私钥 已形成PrivateKeyInfo对象。在私钥的情况下 如果编码包含两个 私钥和公钥定义。CRL、证书、PKC#10 请求和属性证书将生成相应的BC holder类 以下是使用的示例:
试试这门课。
package groovy;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
public class RSA {
private static String getKey(String filename) throws IOException {
// Read key from file
String strKeyPEM = "";
BufferedReader br = new BufferedReader(new FileReader(filename));
String line;
while ((line = br.readLine()) != null) {
strKeyPEM += line + "\n";
}
br.close();
return strKeyPEM;
}
public static RSAPrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
String privateKeyPEM = getKey(filename);
return getPrivateKeyFromString(privateKeyPEM);
}
public static RSAPrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
String privateKeyPEM = key;
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----\n", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.decodeBase64(privateKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(keySpec);
return privKey;
}
public static RSAPublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException {
String publicKeyPEM = getKey(filename);
return getPublicKeyFromString(publicKeyPEM);
}
public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
byte[] encoded = Base64.decodeBase64(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
return pubKey;
}
public static String sign(PrivateKey privateKey, String message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initSign(privateKey);
sign.update(message.getBytes("UTF-8"));
return new String(Base64.encodeBase64(sign.sign()), "UTF-8");
}
public static boolean verify(PublicKey publicKey, String message, String signature) throws SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
}
public static String encrypt(String rawText, PublicKey publicKey) throws IOException, GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64String(cipher.doFinal(rawText.getBytes("UTF-8")));
}
public static String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
}
}
Required jar library "common-codec-1.6"
我认为在私钥定义中,应该替换:
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
与:
查看您的openssl
命令:
$openssl **pkcs8** -topk8 -inform PEM -outform PEM -in mykey.pem \ -out private_key.pem -nocrypt
java例外:
Only PCKS8 codification
Java libs使得读取openssl生成的公共证书几乎只需一行程序:
val certificate: X509Certificate = ByteArrayInputStream(
publicKeyCert.toByteArray(Charsets.US_ASCII))
.use {
CertificateFactory.getInstance("X.509")
.generateCertificate(it) as X509Certificate
}
但是,该死的,读取私钥是有问题的:
RSAPrivateKey
private byte[] loadPEM(String resource) throws IOException {
URL url = getClass().getResource(resource);
InputStream in = url.openStream();
String pem = new String(in.readAllBytes(), StandardCharsets.ISO_8859_1);
Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
String encoded = parse.matcher(pem).replaceFirst("$1");
return Base64.getMimeDecoder().decode(encoded);
}
@Test
public void test() throws Exception {
KeyFactory kf = KeyFactory.getInstance("RSA");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
PrivateKey key = kf.generatePrivate(new PKCS8EncodedKeySpec(loadPEM("test.key")));
PublicKey pub = kf.generatePublic(new X509EncodedKeySpec(loadPEM("test.pub")));
Certificate crt = cf.generateCertificate(getClass().getResourceAsStream("test.crt"));
}
Java 8:
将.readAllBytes()中的调用替换为对以下内容的调用:
byte[] readAllBytes(InputStream in) throws IOException {
ByteArrayOutputStream baos= new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read=0; read != -1; read = in.read(buf)) { baos.write(buf, 0, read); }
return baos.toByteArray();
}
感谢Daniel注意到API兼容性问题要获取公钥,您只需执行以下操作:
public static PublicKey getPublicKeyFromCertFile(final String certfile){
return new X509CertImpl(new FileInputStream(new File(certfile))).getPublicKey();
要获取私钥更为棘手,您可以:
public static PrivateKey getPrivateKeyFromKeyFile(final String keyfile){
try {
Process p;
p = Runtime.getRuntime().exec("openssl pkcs8 -nocrypt -topk8 -inform PEM " +
"-in " + keyfile + " -outform DER -out " + keyfile + ".der");
p.waitFor();
System.out.println("Command executed" + (p.exitValue() == 0 ? " successfully" : " with error" ));
} catch ( IOException | InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
PrivateKey myPrivKey = null;
try {
byte[] keyArray = Files.readAllBytes(Paths.get(keyfile + ".der"));
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyArray);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
myPrivKey = keyFactory.generatePrivate(keySpec);
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e){
e.printStackTrace();
System.exit(1);
}
return myPrivKey;
}
如果PEM仅包含一个未加密的RSA私钥,则它必须是包含9个数字的ASN.1序列结构,以表示中国剩余定理(CRT)密钥:
版本(始终为0)
模数(n)
公众指数(e,始终为65537)
私人指数(d)
素数p
素数q
d国防部(p-1)(dp)
d模块(q-1)(dq)
q^-1模块p(qinv)
我们可以实现一个RSAPrivateCrtKey
:
class RSAPrivateCrtKeyImpl implements RSAPrivateCrtKey {
private static final long serialVersionUID = 1L;
BigInteger n, e, d, p, q, dp, dq, qinv;
@Override
public BigInteger getModulus() {
return n;
}
@Override
public BigInteger getPublicExponent() {
return e;
}
@Override
public BigInteger getPrivateExponent() {
return d;
}
@Override
public BigInteger getPrimeP() {
return p;
}
@Override
public BigInteger getPrimeQ() {
return q;
}
@Override
public BigInteger getPrimeExponentP() {
return dp;
}
@Override
public BigInteger getPrimeExponentQ() {
return dq;
}
@Override
public BigInteger getCrtCoefficient() {
return qinv;
}
@Override
public String getAlgorithm() {
return "RSA";
}
@Override
public String getFormat() {
throw new UnsupportedOperationException();
}
@Override
public byte[] getEncoded() {
throw new UnsupportedOperationException();
}
}
然后从PEM文件中读取私钥:
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
static RSAPrivateCrtKey getRSAPrivateKey(String keyFile) {
RSAPrivateCrtKeyImpl prvKey = new RSAPrivateCrtKeyImpl();
try (BufferedReader in = new BufferedReader(new FileReader(keyFile))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
// skip "-----BEGIN/END RSA PRIVATE KEY-----"
if (!line.startsWith("--") || !line.endsWith("--")) {
sb.append(line);
}
}
DerInputStream der = new DerValue(Base64.
getDecoder().decode(sb.toString())).getData();
der.getBigInteger(); // 0
prvKey.n = der.getBigInteger();
prvKey.e = der.getBigInteger(); // 65537
prvKey.d = der.getBigInteger();
prvKey.p = der.getBigInteger();
prvKey.q = der.getBigInteger();
prvKey.dp = der.getBigInteger();
prvKey.dq = der.getBigInteger();
prvKey.qinv = der.getBigInteger();
} catch (IllegalArgumentException | IOException e) {
logger.warn(keyFile + ": " + e.getMessage());
return null;
}
}
从pem(PK或Cert)读取公钥。取决于弹跳舱
private static PublicKey getPublicKeyFromPEM(Reader reader) throws IOException {
PublicKey key;
try (PEMParser pem = new PEMParser(reader)) {
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
Object pemContent = pem.readObject();
if (pemContent instanceof PEMKeyPair) {
PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
key = keyPair.getPublic();
} else if (pemContent instanceof SubjectPublicKeyInfo) {
SubjectPublicKeyInfo keyInfo = (SubjectPublicKeyInfo) pemContent;
key = jcaPEMKeyConverter.getPublicKey(keyInfo);
} else if (pemContent instanceof X509CertificateHolder) {
X509CertificateHolder cert = (X509CertificateHolder) pemContent;
key = jcaPEMKeyConverter.getPublicKey(cert.getSubjectPublicKeyInfo());
} else {
throw new IllegalArgumentException("Unsupported public key format '" +
pemContent.getClass().getSimpleName() + '"');
}
}
return key;
}
从PEM读取私钥:
private static PrivateKey getPrivateKeyFromPEM(Reader reader) throws IOException {
PrivateKey key;
try (PEMParser pem = new PEMParser(reader)) {
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
Object pemContent = pem.readObject();
if (pemContent instanceof PEMKeyPair) {
PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
key = keyPair.getPrivate();
} else if (pemContent instanceof PrivateKeyInfo) {
PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemContent;
key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);
} else {
throw new IllegalArgumentException("Unsupported private key format '" +
pemContent.getClass().getSimpleName() + '"');
}
}
return key;
}
Java支持对开箱即用的公钥和私钥使用DER(这基本上与PEM相同,正如OP所要求的,除了PEM文件包含基本64位数据加上页眉和页脚行)
如果您使用的是Java 8+(假设您的密钥文件在类路径中可用),则无需任何外部库即可依赖此代码(模异常处理):
类签名者{
私人钥匙厂钥匙厂;
公共签名人(){
this.keyFactory=keyFactory.getInstance(“RSA”);
}
公钥getPublicKey(){
byte[]publicKey=readFileAsBytes(“public key.der”);
X509EncodedKeySpec keySpec=新X509EncodedKeySpec(公钥);
返回keyFactory.generatePublic(keySpec);
}
public PrivateKey getPrivateKey(){
byte[]privateKey=readFileAsBytes(“private key.der”);
PKCS8EncodedKeySpec=新的PKCS8EncodedKeySpec(privateKey);
返回keyFactory.generatePrivate(keySpec);
}
私有URI readFileAsBytes(字符串名称){
URI fileUri=getClass().getClassLoader().getResource(名称).toURI();
返回Files.readAllBytes(path.get(fileUri));
}
}
对于记录,您可以使用以下命令将PEM密钥转换为DER密钥:
$openssl pkcs8-topk8-通知PEM-输出DER-输入private-key.PEM-输出private-key.DER-nocrypt
并通过以下方式获取公钥:
$openssl rsa-in-private-key.pem-pubout-outform DER-out public-key.DER
Hmmm。。。我觉得不错。我认为JCE中没有更好的方法,因为它没有PEM处理函数。您已经回答了自己的问题,并为我们提供了很好的示例代码。您可能应该将“getPemPublicKey”中的“privKeyPEM”更改为“pubKeyPEM”。不必使用openssl-nocrypt
命令,如何做到这一点(或可以做到这一点)。这一部分也可以在Java中完成吗?“openssl genrsa”生成一个私钥,而不是密钥对@iznt链接已断开。你能指出区别/解释为什么你的更好吗?更好?更像(有点)不同:-)但是。。。公共X.509的负载使用更少的代码。私钥的负载更具可移植性(有些密钥类似于头文件“RSA”中指定的密钥,如:“----开始RSA私钥------”)并且不使用“Base64”(似乎是外部库);此代码仅使用Jre6-----BEGIN RSA私钥------
是另一种格式,而不是PKCS8,尝试将其作为PKCS8读取将不起作用。此外,证书与原始公钥是不同的东西,因此这根本不适用于此Q中的数据。@dave_thompson_085此代码是从工作系统中提取的。。。你的确认来源是什么?(0)抱歉耽搁了,我很忙。(1) 我知道openssl是如何工作的。(2) 只为你,我爱你
private static PublicKey getPublicKeyFromPEM(Reader reader) throws IOException {
PublicKey key;
try (PEMParser pem = new PEMParser(reader)) {
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
Object pemContent = pem.readObject();
if (pemContent instanceof PEMKeyPair) {
PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
key = keyPair.getPublic();
} else if (pemContent instanceof SubjectPublicKeyInfo) {
SubjectPublicKeyInfo keyInfo = (SubjectPublicKeyInfo) pemContent;
key = jcaPEMKeyConverter.getPublicKey(keyInfo);
} else if (pemContent instanceof X509CertificateHolder) {
X509CertificateHolder cert = (X509CertificateHolder) pemContent;
key = jcaPEMKeyConverter.getPublicKey(cert.getSubjectPublicKeyInfo());
} else {
throw new IllegalArgumentException("Unsupported public key format '" +
pemContent.getClass().getSimpleName() + '"');
}
}
return key;
}
private static PrivateKey getPrivateKeyFromPEM(Reader reader) throws IOException {
PrivateKey key;
try (PEMParser pem = new PEMParser(reader)) {
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
Object pemContent = pem.readObject();
if (pemContent instanceof PEMKeyPair) {
PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
key = keyPair.getPrivate();
} else if (pemContent instanceof PrivateKeyInfo) {
PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemContent;
key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);
} else {
throw new IllegalArgumentException("Unsupported private key format '" +
pemContent.getClass().getSimpleName() + '"');
}
}
return key;
}