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

时间:2012-08-03 00:09:55

标签: 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 -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。所以如果我在某个地方没有意义,那么请指出。

10 个答案:

答案 0 :(得分:25)

试试此课程。

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"

答案 1 :(得分:13)

一种选择是使用bouncycastle' PEMParser

  

用于解析包含X509的OpenSSL PEM编码流的类   证书,PKCS8编码密钥和PKCS7对象。

     

对于PKCS7对象,阅读器将返回CMS ContentInfo   宾语。公钥将在返回时生成   SubjectPublicKeyInfo对象,也将返回私钥   形成了PrivateKeyInfo对象。在私钥的情况下a   如果编码包含两者,则通常会返回PEMKeyPair   私钥和公钥定义。 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());
    }
}

答案 2 :(得分:9)

嗯,我的代码就像你的代码,差别很小......

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

答案 3 :(得分:5)

Java 9 +:

private byte[] loadPEM(String resource) throws IOException {
    URL url = getClass().getResource(resource);
    InputStream in = url.openStream();
    String pem = new String(in.readAllBytes(), 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兼容性问题

答案 4 :(得分:2)

我认为在您的私钥定义中,您应该替换:

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 

答案 5 :(得分:2)

Java libs几乎是一个读取公共证书的工具,由openssl生成:

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

但是,地狱,阅读私钥是有问题的:

  1. 首先必须删除开始和结束标记,这在阅读公钥时并不紧张。
  2. 然后我不得不删除所有新行,否则就会呱呱叫!
  3. 然后我不得不使用byte 64
  4. 解码回字节
  5. 然后我就能够制作RSAPrivateKey
  6. 请参阅:Final solution in kotlin

答案 6 :(得分:1)

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

答案 7 :(得分:1)

Java支持开箱即用地使用DER作为公钥和私钥(这与OP所要求的PEM基本相同,只是PEM文件包含基本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

答案 8 :(得分:0)

要获取公共密钥,您只需执行以下操作:

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

答案 9 :(得分:0)

如果PEM仅包含一个未加密的RSA私钥,则它必须是包含9个数字的ASN.1序列结构,以显示中国剩余定理(CRT)密钥:

  1. 版本(始终为0)
  2. 模数(n)
  3. 公共指数(始终为65537)
  4. 私有指数(d)
  5. 素数p
  6. 素数
  7. d mod(p-1)(dp)
  8. d mod(q-1)(dq)
  9. 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;
    }
}