如何读取 .pem 文件以获取私钥和公钥
Posted
技术标签:
【中文标题】如何读取 .pem 文件以获取私钥和公钥【英文标题】:How to read .pem file to get private and public key 【发布时间】:2012-08-01 00:30:20 【问题描述】:我正在编写一小段代码来读取存储在 .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 -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。因此,如果我在某处没有意义,请指出。
【问题讨论】:
嗯...对我来说看起来不错。我认为 JCE 中没有更好的方法,它没有 PEM 处理功能。您已经回答了自己的问题,并为我们提供了很好的示例代码。 您应该将“getPemPublicKey”中的“privKeyPEM”更改为“pubKeyPEM”。 如何在不使用openssl -nocrypt
命令的情况下完成(或可以完成)。那部分也可以用Java完成吗?
"openssl genrsa" 生成私钥,而不是密钥对? wiki.openssl.org/index.php/Manual:Genrsa(1)
@iznt 链接已失效。 openssl.org/docs/man1.0.2/apps/genrsa.html
【参考方案1】:
嗯,我的代码和你的一样,差别不大……
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)
【讨论】:
你能指出差异/解释为什么你的更好吗? 更好?更像(有点)不同:-) 但是......公共 X.509 的负载使用更少的代码。私钥的负载更便携(在标题“RSA”中指定了类似的密钥,例如:“-----BEGIN RSA PRIVATE KEY-----”)并且不要使用“Base64”(似乎是外部库);此代码仅使用 Jre6-----BEGIN RSA PRIVATE KEY-----
是一种不同的格式,而不是 PKCS8,并且尝试将其读取为 PKCS8 将不起作用。此外,证书与原始公钥不同,因此这根本不适用于此 Q 中的数据。
@dave_thompson_085 此代码是从工作系统中提取的...您的确认来源是什么?¿?
(0) 抱歉耽搁了,我很忙。 (1) 我知道 openssl 是如何工作的。 (2) 只是为了你,我使用 Q 中的第一个和第三个命令创建了新文件,为了完整起见,第三个更改为 PEM。正如我预期的那样,您的代码在这些文件上出现异常,但在 different 文件上工作正常(由 second 命令创建的 PKCS8-u 密钥,而由命令创建的证书完全不是在这个 Q 中使用)。如果您可以显示由 OpenSSL 创建的(仅测试)文件,其 PEM 类型为“RSA PRIVATE KEY”和/或“PUBLIC KEY”,您发布的代码可以读取这些文件,我将支付 100 美元。【参考方案2】:
一种选择是使用 bouncycastle 的PEMParser:
用于解析包含 X509 的 OpenSSL PEM 编码流的类 证书、PKCS8 编码密钥和 PKCS7 对象。
对于 PKCS7 对象,阅读器将返回 CMS ContentInfo 目的。公钥将按照格式返回 SubjectPublicKeyInfo 对象,也会返回私钥 形成 PrivateKeyInfo 对象。在私钥的情况下 如果编码同时包含 私钥和公钥定义。 CRL、证书、PKCS#10 请求,并且属性证书将生成适当的 BC 持有者类。
这里是一个使用Parser test code的例子:
package org.bouncycastle.openssl.test;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.util.test.SimpleTest;
/**
* basic class for reading test.pem - the password is "secret"
*/
public class ParserTest
extends SimpleTest
private static class Password
implements PasswordFinder
char[] password;
Password(
char[] word)
this.password = word;
public char[] getPassword()
return password;
public String getName()
return "PEMParserTest";
private PEMParser openPEMResource(
String fileName)
InputStream res = this.getClass().getResourceAsStream(fileName);
Reader fRd = new BufferedReader(new InputStreamReader(res));
return new PEMParser(fRd);
public void performTest()
throws Exception
PEMParser pemRd = openPEMResource("test.pem");
Object o;
PEMKeyPair pemPair;
KeyPair pair;
while ((o = pemRd.readObject()) != null)
if (o instanceof KeyPair)
//pair = (KeyPair)o;
//System.out.println(pair.getPublic());
//System.out.println(pair.getPrivate());
else
//System.out.println(o.toString());
// test bogus lines before begin are ignored.
pemRd = openPEMResource("extratest.pem");
while ((o = pemRd.readObject()) != null)
if (!(o instanceof X509CertificateHolder))
fail("wrong object found");
//
// pkcs 7 data
//
pemRd = openPEMResource("pkcs7.pem");
ContentInfo d = (ContentInfo)pemRd.readObject();
if (!d.getContentType().equals(CMSObjectIdentifiers.envelopedData))
fail("failed envelopedData check");
//
// ECKey
//
pemRd = openPEMResource("eckey.pem");
ASN1ObjectIdentifier ecOID = (ASN1ObjectIdentifier)pemRd.readObject();
X9ECParameters ecSpec = ECNamedCurveTable.getByOID(ecOID);
if (ecSpec == null)
fail("ecSpec not found for named curve");
pemPair = (PEMKeyPair)pemRd.readObject();
pair = new JcaPEMKeyConverter().setProvider("BC").getKeyPair(pemPair);
Signature sgr = Signature.getInstance("ECDSA", "BC");
sgr.initSign(pair.getPrivate());
byte[] message = new byte[] (byte)'a', (byte)'b', (byte)'c' ;
sgr.update(message);
byte[] sigBytes = sgr.sign();
sgr.initVerify(pair.getPublic());
sgr.update(message);
if (!sgr.verify(sigBytes))
fail("EC verification failed");
if (!pair.getPublic().getAlgorithm().equals("ECDSA"))
fail("wrong algorithm name on public got: " + pair.getPublic().getAlgorithm());
if (!pair.getPrivate().getAlgorithm().equals("ECDSA"))
fail("wrong algorithm name on private");
//
// ECKey -- explicit parameters
//
pemRd = openPEMResource("ecexpparam.pem");
ecSpec = (X9ECParameters)pemRd.readObject();
pemPair = (PEMKeyPair)pemRd.readObject();
pair = new JcaPEMKeyConverter().setProvider("BC").getKeyPair(pemPair);
sgr = Signature.getInstance("ECDSA", "BC");
sgr.initSign(pair.getPrivate());
message = new byte[] (byte)'a', (byte)'b', (byte)'c' ;
sgr.update(message);
sigBytes = sgr.sign();
sgr.initVerify(pair.getPublic());
sgr.update(message);
if (!sgr.verify(sigBytes))
fail("EC verification failed");
if (!pair.getPublic().getAlgorithm().equals("ECDSA"))
fail("wrong algorithm name on public got: " + pair.getPublic().getAlgorithm());
if (!pair.getPrivate().getAlgorithm().equals("ECDSA"))
fail("wrong algorithm name on private");
//
// writer/parser test
//
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
pair = kpGen.generateKeyPair();
keyPairTest("RSA", pair);
kpGen = KeyPairGenerator.getInstance("DSA", "BC");
kpGen.initialize(512, new SecureRandom());
pair = kpGen.generateKeyPair();
keyPairTest("DSA", pair);
//
// PKCS7
//
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut));
pWrt.writeObject(d);
pWrt.close();
pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
d = (ContentInfo)pemRd.readObject();
if (!d.getContentType().equals(CMSObjectIdentifiers.envelopedData))
fail("failed envelopedData recode check");
// OpenSSL test cases (as embedded resources)
doOpenSslDsaTest("unencrypted");
doOpenSslRsaTest("unencrypted");
doOpenSslTests("aes128");
doOpenSslTests("aes192");
doOpenSslTests("aes256");
doOpenSslTests("blowfish");
doOpenSslTests("des1");
doOpenSslTests("des2");
doOpenSslTests("des3");
doOpenSslTests("rc2_128");
doOpenSslDsaTest("rc2_40_cbc");
doOpenSslRsaTest("rc2_40_cbc");
doOpenSslDsaTest("rc2_64_cbc");
doOpenSslRsaTest("rc2_64_cbc");
doDudPasswordTest("7fd98", 0, "corrupted stream - out of bounds length found");
doDudPasswordTest("ef677", 1, "corrupted stream - out of bounds length found");
doDudPasswordTest("800ce", 2, "unknown tag 26 encountered");
doDudPasswordTest("b6cd8", 3, "DEF length 81 object truncated by 56");
doDudPasswordTest("28ce09", 4, "DEF length 110 object truncated by 28");
doDudPasswordTest("2ac3b9", 5, "DER length more than 4 bytes: 11");
doDudPasswordTest("2cba96", 6, "DEF length 100 object truncated by 35");
doDudPasswordTest("2e3354", 7, "DEF length 42 object truncated by 9");
doDudPasswordTest("2f4142", 8, "DER length more than 4 bytes: 14");
doDudPasswordTest("2fe9bb", 9, "DER length more than 4 bytes: 65");
doDudPasswordTest("3ee7a8", 10, "DER length more than 4 bytes: 57");
doDudPasswordTest("41af75", 11, "unknown tag 16 encountered");
doDudPasswordTest("1704a5", 12, "corrupted stream detected");
doDudPasswordTest("1c5822", 13, "unknown object in getInstance: org.bouncycastle.asn1.DERUTF8String");
doDudPasswordTest("5a3d16", 14, "corrupted stream detected");
doDudPasswordTest("8d0c97", 15, "corrupted stream detected");
doDudPasswordTest("bc0daf", 16, "corrupted stream detected");
doDudPasswordTest("aaf9c4d",17, "corrupted stream - out of bounds length found");
doNoPasswordTest();
// encrypted private key test
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().build("password".toCharArray());
pemRd = openPEMResource("enckey.pem");
PKCS8EncryptedPrivateKeyInfo encPrivKeyInfo = (PKCS8EncryptedPrivateKeyInfo)pemRd.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
RSAPrivateCrtKey privKey = (RSAPrivateCrtKey)converter.getPrivateKey(encPrivKeyInfo.decryptPrivateKeyInfo(pkcs8Prov));
if (!privKey.getPublicExponent().equals(new BigInteger("10001", 16)))
fail("decryption of private key data check failed");
// general PKCS8 test
pemRd = openPEMResource("pkcs8test.pem");
Object privInfo;
while ((privInfo = pemRd.readObject()) != null)
if (privInfo instanceof PrivateKeyInfo)
privKey = (RSAPrivateCrtKey)converter.getPrivateKey(PrivateKeyInfo.getInstance(privInfo));
else
privKey = (RSAPrivateCrtKey)converter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo)privInfo).decryptPrivateKeyInfo(pkcs8Prov));
if (!privKey.getPublicExponent().equals(new BigInteger("10001", 16)))
fail("decryption of private key data check failed");
private void keyPairTest(
String name,
KeyPair pair)
throws IOException
PEMParser pemRd;
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut));
pWrt.writeObject(pair.getPublic());
pWrt.close();
pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
SubjectPublicKeyInfo pub = SubjectPublicKeyInfo.getInstance(pemRd.readObject());
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PublicKey k = converter.getPublicKey(pub);
if (!k.equals(pair.getPublic()))
fail("Failed public key read: " + name);
bOut = new ByteArrayOutputStream();
pWrt = new PEMWriter(new OutputStreamWriter(bOut));
pWrt.writeObject(pair.getPrivate());
pWrt.close();
pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
KeyPair kPair = converter.getKeyPair((PEMKeyPair)pemRd.readObject());
if (!kPair.getPrivate().equals(pair.getPrivate()))
fail("Failed private key read: " + name);
if (!kPair.getPublic().equals(pair.getPublic()))
fail("Failed private key public read: " + name);
private void doOpenSslTests(
String baseName)
throws IOException
doOpenSslDsaModesTest(baseName);
doOpenSslRsaModesTest(baseName);
private void doOpenSslDsaModesTest(
String baseName)
throws IOException
doOpenSslDsaTest(baseName + "_cbc");
doOpenSslDsaTest(baseName + "_cfb");
doOpenSslDsaTest(baseName + "_ecb");
doOpenSslDsaTest(baseName + "_ofb");
private void doOpenSslRsaModesTest(
String baseName)
throws IOException
doOpenSslRsaTest(baseName + "_cbc");
doOpenSslRsaTest(baseName + "_cfb");
doOpenSslRsaTest(baseName + "_ecb");
doOpenSslRsaTest(baseName + "_ofb");
private void doOpenSslDsaTest(
String name)
throws IOException
String fileName = "dsa/openssl_dsa_" + name + ".pem";
doOpenSslTestFile(fileName, DSAPrivateKey.class);
private void doOpenSslRsaTest(
String name)
throws IOException
String fileName = "rsa/openssl_rsa_" + name + ".pem";
doOpenSslTestFile(fileName, RSAPrivateKey.class);
private void doOpenSslTestFile(
String fileName,
Class expectedPrivKeyClass)
throws IOException
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().setProvider("BC").build("changeit".toCharArray());
PEMParser pr = openPEMResource("data/" + fileName);
Object o = pr.readObject();
if (o == null || !((o instanceof PEMKeyPair) || (o instanceof PEMEncryptedKeyPair)))
fail("Didn't find OpenSSL key");
KeyPair kp = (o instanceof PEMEncryptedKeyPair) ?
converter.getKeyPair(((PEMEncryptedKeyPair)o).decryptKeyPair(decProv)) : converter.getKeyPair((PEMKeyPair)o);
PrivateKey privKey = kp.getPrivate();
if (!expectedPrivKeyClass.isInstance(privKey))
fail("Returned key not of correct type");
private void doDudPasswordTest(String password, int index, String message)
// illegal state exception check - in this case the wrong password will
// cause an underlying class cast exception.
try
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().setProvider("BC").build(password.toCharArray());
PEMParser pemRd = openPEMResource("test.pem");
Object o;
while ((o = pemRd.readObject()) != null)
if (o instanceof PEMEncryptedKeyPair)
((PEMEncryptedKeyPair)o).decryptKeyPair(decProv);
fail("issue not detected: " + index);
catch (IOException e)
if (e.getCause() != null && !e.getCause().getMessage().endsWith(message))
fail("issue " + index + " exception thrown, but wrong message");
else if (e.getCause() == null && !e.getMessage().equals(message))
e.printStackTrace();
fail("issue " + index + " exception thrown, but wrong message");
private void doNoPasswordTest()
throws IOException
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().setProvider("BC").build("".toCharArray());
PEMParser pemRd = openPEMResource("smimenopw.pem");
Object o;
PrivateKeyInfo key = null;
while ((o = pemRd.readObject()) != null)
key = (PrivateKeyInfo)o;
if (key == null)
fail("private key not detected");
public static void main(
String[] args)
Security.addProvider(new BouncyCastleProvider());
runTest(new ParserTest());
【讨论】:
代码没有正确处理密码。密码完成后应该用 0 覆盖。例如,请参阅 Java JCE 架构文档中的 Using Password-Based Encryption。 @jww 作为一个好公民,您向 bouncycastle 团队提出了这个问题吗?【参考方案3】:试试这个课程。
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"
【讨论】:
Only RSAPublicKeySpec and X509EncodedKeySpec supported for RSA public keys
。切换到X509EncodedKeySpec
,然后得到java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=111, too big.
。这是由 AWS EC2 生成的 pem 文件
@Hooli 您需要使用RSAPublicKeySpec
,因为您的公钥似乎是PKCS#1
格式而不是PKCS#8
格式。在后一种情况下,您需要使用X509EncodedKeySpec
(而不是PKCS8EncodedKeySpec
,因为目前的答案是)。您可以通过查看标题来区分两者,BEGIN RSA PUBLIC KEY
与 BEGIN PUBLIC KEY
。
顺便说一句,我还没有找到以编程方式提取RSAPublicKeySpec
的构造函数所需的模数和公共指数的可能性。因此,使用openssl
或类似程序从PKCS#1
转换为PKCS#8
可能更容易......
谢谢这确实有效。奇怪的是,Java 使读取公共证书很容易,但很难读取私钥。
这行得通,但是我必须将私钥转换为 pks8 格式:openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in 我认为在您的私钥定义中,您应该替换:
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
与:
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
查看您的openssl
命令:
$openssl **pkcs8** -topk8 -inform PEM -outform PEM -in mykey.pem \ -out private_key.pem -nocrypt
还有java异常:
Only PCKS8 codification
【讨论】:
Smandoli - 我听从了你的建议,但两种方式都不起作用。请参考***.com/questions/39311157/…。我也提出了这个问题。【参考方案5】:Java libs 几乎可以让读取由 openssl 生成的公共证书:
val certificate: X509Certificate = ByteArrayInputStream(
publicKeyCert.toByteArray(Charsets.US_ASCII))
.use
CertificateFactory.getInstance("X.509")
.generateCertificate(it) as X509Certificate
但是,天啊,读取私钥是有问题的:
-
首先必须删除开始和结束标签,读取公钥时不需要这样做。
然后我必须删除所有新行,否则它会发出嘶哑的声音!
然后我必须使用字节 64 解码回字节
然后我可以生成一个
RSAPrivateKey
。
看到这个:Final solution in kotlin
【讨论】:
【参考方案6】:Java 9+:
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:
将in.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 兼容性问题
【讨论】:
令我惊讶的是,我们要么需要获取整个 bouncycastle 库,只是因为 java 无法手动读取开箱即用的.pem
文件,要么我们需要这样做 :) 感谢解决方案虽然【参考方案7】:
要获取公钥,您只需执行以下操作:
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;
【讨论】:
我们需要在机器上安装“openssl”才能使用它吗? 是的,我这样做是因为在问题中openssl已经被使用了。【参考方案8】:如果一个PEM只包含一个没有加密的RSA私钥,它必须是一个包含9个数字的ASN.1序列结构才能呈现一个中国剩余定理(CRT)密钥:
-
版本(始终为 0)
模数 (n)
公共指数(e,始终为 65537)
私有指数 (d)
素数
素数
d mod (p - 1) (dp)
d mod (q - 1) (dq)
q^-1 mod 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;
【讨论】:
可以为 PublicKey 修改相同的代码吗?上面的代码是否经过测试?尝试部分代码似乎没有返回任何值...RSAPublicKeyImpl
(实现RSAPublicKey
)是RSAPrivateCrtKeyImpl
的一部分:getModulus
返回n
,getPublicExponent
返回e
。
只需使用Cipher.getInstance("RSA")
或Signature.getInstance("RSA...")
加载RSAPublicKey
或RSAPrivateCrtKey
,JCE 将使用这些参数进行加密/解密/签名/验证。【参考方案9】:
从 pem(PK 或 Cert)读取公钥。取决于 Bouncycastle。
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;
【讨论】:
【参考方案10】:Java 支持将 DER 用于开箱即用的公钥和私钥(这与 OP 要求的 PEM 基本相同,除了 PEM 文件包含 base 64 数据以及页眉和页脚行)。
如果您使用的是 Java 8+(假设您的密钥文件在类路径中可用),则无需任何外部库即可依赖此代码(模异常处理):
class Signer
private KeyFactory keyFactory;
public Signer()
this.keyFactory = KeyFactory.getInstance("RSA");
public PublicKey getPublicKey()
byte[] publicKey = readFileAsBytes("public-key.der");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
return keyFactory.generatePublic(keySpec);
public PrivateKey getPrivateKey()
byte[] privateKey = readFileAsBytes("private-key.der");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
return keyFactory.generatePrivate(keySpec);
private URI readFileAsBytes(String name)
URI fileUri = getClass().getClassLoader().getResource(name).toURI();
return Files.readAllBytes(Paths.get(fileUri));
作为记录,您可以使用以下命令将 PEM 密钥转换为 DER 密钥:
$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private-key.pem -out private-key.der -nocrypt
并通过以下方式获取 DER 中的公钥:
$ openssl rsa -in private-key.pem -pubout -outform DER -out public-key.der
【讨论】:
感谢您为我节省了数小时的烦恼 - 如果采用 DER 形式,所有示例都有效以上是关于如何读取 .pem 文件以获取私钥和公钥的主要内容,如果未能解决你的问题,请参考以下文章