如何读取.pem 文件以获得私钥和公钥

我正在编写一小段代码,它可以读取存储在。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。所以如果我在哪里说不通,请指出来。

248737 次浏览

Well, my code is like yours, with little diferences...

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) {}
}

One option is to use bouncycastle's PEMParser:

Class for parsing OpenSSL PEM encoded streams containing X509 certificates, PKCS8 encoded keys and PKCS7 objects.

In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Public keys will be returned as well formed SubjectPublicKeyInfo objects, private keys will be returned as well formed PrivateKeyInfo objects. In the case of a private key a PEMKeyPair will normally be returned if the encoding contains both the private and public key definition. CRLs, Certificates, PKCS#10 requests, and Attribute Certificates will generate the appropriate BC holder class.

Here is an example of using the 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());
}
}

Try this class.

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"

I think in your private key definition, You should replace:

X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);

with:

PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);

Look your openssl command:

$openssl **pkcs8** -topk8 -inform PEM -outform PEM -in mykey.pem \ -out private_key.pem -nocrypt

And the java Exception:

Only PCKS8 codification

Java libs makes it almost a one liner to read the public cert, as generated by openssl:

val certificate: X509Certificate = ByteArrayInputStream(
publicKeyCert.toByteArray(Charsets.US_ASCII))
.use {
CertificateFactory.getInstance("X.509")
.generateCertificate(it) as X509Certificate
}

But, o hell, reading the private key was problematic:

  1. First had to remove the begin and end tags, which is not nessarry when reading the public key.
  2. Then I had to remove all the new lines, otherwise it croaks!
  3. Then I had to decode back to bytes using byte 64
  4. Then I was able to produce an RSAPrivateKey.

see this: Final solution in kotlin

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:

replace the in.readAllBytes() call with a call to this:

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();
}

thanks to Daniel for noticing API compatibility issues

To get the public key you can simply do:

public static PublicKey getPublicKeyFromCertFile(final String certfile){


return new X509CertImpl(new FileInputStream(new File(certfile))).getPublicKey();

To get the private key is trickier, you can:

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;
}

If a PEM contains only one RSA private key without encryption, it must be an ASN.1 sequence structure including 9 numbers to present a Chinese Remainder Theorem (CRT) key:

  1. version (always 0)
  2. modulus (n)
  3. public exponent (e, always 65537)
  4. private exponent (d)
  5. prime p
  6. prime q
  7. d mod (p - 1) (dp)
  8. d mod (q - 1) (dq)
  9. q^-1 mod p (qinv)

We can implement an 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();
}
}

Then read the private key from a PEM file:

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;
}
}

Read public key from pem (PK or Cert). Depends on 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;
}

Read private key from 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 supports using DER for public and private keys out of the box (which is basically the same as PEM, as the OP asks, except PEM files contain base 64 data plus header and footer lines).

You can rely on this code (modulo exception handling) without any external library if you are on Java 8+ (this assumes your key files are available in the classpath):

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));
}
}

For the record, you can convert a PEM key to a DER key with the following command:

$ openssl pkcs8 -topk8 -inform PEM -outform DER -in private-key.pem -out private-key.der -nocrypt

And get the public key in DER with:

$ openssl rsa -in private-key.pem -pubout -outform DER -out public-key.der