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

Java 如何读取.pem文件以获取私钥和公钥,java,openssl,x509,pem,pkcs#8,Java,Openssl,X509,Pem,Pkcs#8,我正在写一小段代码,读取存储在.pem文件中的公钥和私钥。我正在使用以下命令生成关键点 下面的命令生成一对密钥 $openssl genrsa -out mykey.pem 2048 此命令用于生成私钥 $openssl pkcs8 -topk8 -inform PEM -outform PEM -in mykey.pem \ -out private_key.pem -nocrypt 并使用此命令获取公钥 $ openssl rsa -in mykey.pem -pubout

我正在写一小段代码,读取存储在.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。因此,如果我在某个地方没有意义,请指出。

好吧,我的代码和你的一样,没有什么区别

public static X509Certificate loadPublicX509(String fileName) 
        throws GeneralSecurityException {
    InputStream is = null;
    X509Certificate crt = null;
    try {
        is = fileName.getClass().getResourceAsStream("/" + fileName);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        crt = (X509Certificate)cf.generateCertificate(is);
    } finally {
        closeSilent(is);
    }
    return crt;
}

public static PrivateKey loadPrivateKey(String fileName) 
        throws IOException, GeneralSecurityException {
    PrivateKey key = null;
    InputStream is = null;
    try {
        is = fileName.getClass().getResourceAsStream("/" + fileName);
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder = new StringBuilder();
        boolean inKey = false;
        for (String line = br.readLine(); line != null; line = br.readLine()) {
            if (!inKey) {
                if (line.startsWith("-----BEGIN ") && 
                        line.endsWith(" PRIVATE KEY-----")) {
                    inKey = true;
                }
                continue;
            }
            else {
                if (line.startsWith("-----END ") && 
                        line.endsWith(" PRIVATE KEY-----")) {
                    inKey = false;
                    break;
                }
                builder.append(line);
            }
        }
        //
        byte[] encoded = DatatypeConverter.parseBase64Binary(builder.toString());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        key = kf.generatePrivate(keySpec);
    } finally {
        closeSilent(is);
    }
    return key;
}

public static void closeSilent(final InputStream is) {
    if (is == null) return;
    try { is.close(); } catch (Exception ign) {}
}

一种选择是使用bouncycastle的:

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

对于PKCS7对象,读取器将返回CMS ContentInfo 对象公钥将返回格式正确的密钥 SubjectPublicKeyInfo对象,也将返回私钥 已形成PrivateKeyInfo对象。在私钥的情况下 如果编码包含两个 私钥和公钥定义。CRL、证书、PKC#10 请求和属性证书将生成相应的BC holder类

以下是使用的示例:


试试这门课。

package groovy;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

public class RSA {

private static String getKey(String filename) throws IOException {
    // Read key from file
    String strKeyPEM = "";
    BufferedReader br = new BufferedReader(new FileReader(filename));
    String line;
    while ((line = br.readLine()) != null) {
        strKeyPEM += line + "\n";
    }
    br.close();
    return strKeyPEM;
}
public static RSAPrivateKey getPrivateKey(String filename) throws IOException, GeneralSecurityException {
    String privateKeyPEM = getKey(filename);
    return getPrivateKeyFromString(privateKeyPEM);
}

public static RSAPrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
    String privateKeyPEM = key;
    privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----\n", "");
    privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
    byte[] encoded = Base64.decodeBase64(privateKeyPEM);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    RSAPrivateKey privKey = (RSAPrivateKey) kf.generatePrivate(keySpec);
    return privKey;
}


public static RSAPublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException {
    String publicKeyPEM = getKey(filename);
    return getPublicKeyFromString(publicKeyPEM);
}

public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
    String publicKeyPEM = key;
    publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
    publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
    byte[] encoded = Base64.decodeBase64(publicKeyPEM);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
    return pubKey;
}

public static String sign(PrivateKey privateKey, String message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
    Signature sign = Signature.getInstance("SHA1withRSA");
    sign.initSign(privateKey);
    sign.update(message.getBytes("UTF-8"));
    return new String(Base64.encodeBase64(sign.sign()), "UTF-8");
}


public static boolean verify(PublicKey publicKey, String message, String signature) throws SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
    Signature sign = Signature.getInstance("SHA1withRSA");
    sign.initVerify(publicKey);
    sign.update(message.getBytes("UTF-8"));
    return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
}

public static String encrypt(String rawText, PublicKey publicKey) throws IOException, GeneralSecurityException {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    return Base64.encodeBase64String(cipher.doFinal(rawText.getBytes("UTF-8")));
}

public static String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
}
}


Required jar library "common-codec-1.6"

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

X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
与:

查看您的
openssl
命令:

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

Only PCKS8 codification 

Java libs使得读取openssl生成的公共证书几乎只需一行程序:

val certificate: X509Certificate = ByteArrayInputStream(
        publicKeyCert.toByteArray(Charsets.US_ASCII))
        .use {
            CertificateFactory.getInstance("X.509")
                    .generateCertificate(it) as X509Certificate
        }
但是,该死的,读取私钥是有问题的:

  • 首先必须删除开始和结束标记,这在读取公钥时不是nessarry
  • 然后我必须把所有的新台词都去掉,否则它会嘎嘎作响
  • 然后我不得不用字节64解码回字节
  • 然后我就能够生成一个
    RSAPrivateKey
  • 请参见:

    Java9+:

    private byte[] loadPEM(String resource) throws IOException {
        URL url = getClass().getResource(resource);
        InputStream in = url.openStream();
        String pem = new String(in.readAllBytes(), StandardCharsets.ISO_8859_1);
        Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
        String encoded = parse.matcher(pem).replaceFirst("$1");
        return Base64.getMimeDecoder().decode(encoded);
    }
    
    @Test
    public void test() throws Exception {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        PrivateKey key = kf.generatePrivate(new PKCS8EncodedKeySpec(loadPEM("test.key")));
        PublicKey pub = kf.generatePublic(new X509EncodedKeySpec(loadPEM("test.pub")));
        Certificate crt = cf.generateCertificate(getClass().getResourceAsStream("test.crt"));
    }
    
    Java 8:

    将.readAllBytes()中的
    调用替换为对以下内容的调用:

    byte[] readAllBytes(InputStream in) throws IOException {
        ByteArrayOutputStream baos= new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        for (int read=0; read != -1; read = in.read(buf)) { baos.write(buf, 0, read); }
        return baos.toByteArray();
    }
    

    感谢Daniel注意到API兼容性问题

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

    public static PublicKey getPublicKeyFromCertFile(final String certfile){
    
         return new X509CertImpl(new FileInputStream(new File(certfile))).getPublicKey();
    
    要获取私钥更为棘手,您可以:

    public static PrivateKey getPrivateKeyFromKeyFile(final String keyfile){
        try {
            Process p;
            p = Runtime.getRuntime().exec("openssl pkcs8 -nocrypt -topk8 -inform PEM " +
                    "-in " + keyfile + " -outform DER -out " + keyfile + ".der");
    
            p.waitFor();
            System.out.println("Command executed" + (p.exitValue() == 0 ? " successfully" : " with error" ));
        } catch ( IOException | InterruptedException e) {
            e.printStackTrace();
            System.exit(1);
        }
    
        PrivateKey myPrivKey = null;
        try {
            byte[] keyArray = Files.readAllBytes(Paths.get(keyfile + ".der"));
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyArray);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            myPrivKey = keyFactory.generatePrivate(keySpec);
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e){
            e.printStackTrace();
            System.exit(1);
        }
    
        return myPrivKey;
    }
    

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

  • 版本(始终为0)
  • 模数(n)
  • 公众指数(e,始终为65537)
  • 私人指数(d)
  • 素数p
  • 素数q
  • d国防部(p-1)(dp)
  • d模块(q-1)(dq)
  • q^-1模块p(qinv)
  • 我们可以实现一个
    RSAPrivateCrtKey

    class RSAPrivateCrtKeyImpl implements RSAPrivateCrtKey {
        private static final long serialVersionUID = 1L;
    
        BigInteger n, e, d, p, q, dp, dq, qinv;
    
        @Override
        public BigInteger getModulus() {
            return n;
        }
    
        @Override
        public BigInteger getPublicExponent() {
            return e;
        }
    
        @Override
        public BigInteger getPrivateExponent() {
            return d;
        }
    
        @Override
        public BigInteger getPrimeP() {
            return p;
        }
    
        @Override
        public BigInteger getPrimeQ() {
            return q;
        }
    
        @Override
        public BigInteger getPrimeExponentP() {
            return dp;
        }
    
        @Override
        public BigInteger getPrimeExponentQ() {
            return dq;
        }
    
        @Override
        public BigInteger getCrtCoefficient() {
            return qinv;
        }
    
        @Override
        public String getAlgorithm() {
            return "RSA";
        }
    
        @Override
        public String getFormat() {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public byte[] getEncoded() {
            throw new UnsupportedOperationException();
        }
    }
    
    然后从PEM文件中读取私钥:

    import sun.security.util.DerInputStream;
    import sun.security.util.DerValue;
    
    static RSAPrivateCrtKey getRSAPrivateKey(String keyFile) {
        RSAPrivateCrtKeyImpl prvKey = new RSAPrivateCrtKeyImpl();
        try (BufferedReader in = new BufferedReader(new FileReader(keyFile))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = in.readLine()) != null) {
                // skip "-----BEGIN/END RSA PRIVATE KEY-----"
                if (!line.startsWith("--") || !line.endsWith("--")) {
                    sb.append(line);
                }
            }
            DerInputStream der = new DerValue(Base64.
                    getDecoder().decode(sb.toString())).getData();
            der.getBigInteger(); // 0
            prvKey.n = der.getBigInteger();
            prvKey.e = der.getBigInteger(); // 65537
            prvKey.d = der.getBigInteger();
            prvKey.p = der.getBigInteger();
            prvKey.q = der.getBigInteger();
            prvKey.dp = der.getBigInteger();
            prvKey.dq = der.getBigInteger();
            prvKey.qinv = der.getBigInteger();
        } catch (IllegalArgumentException | IOException e) {
            logger.warn(keyFile + ": " + e.getMessage());
            return null;
        }
    }
    
    从pem(PK或Cert)读取公钥。取决于弹跳舱

    private static PublicKey getPublicKeyFromPEM(Reader reader) throws IOException {
    
        PublicKey key;
    
        try (PEMParser pem = new PEMParser(reader)) {
            JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
            Object pemContent = pem.readObject();
            if (pemContent instanceof PEMKeyPair) {
                PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
                KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
                key = keyPair.getPublic();
            } else if (pemContent instanceof SubjectPublicKeyInfo) {
                SubjectPublicKeyInfo keyInfo = (SubjectPublicKeyInfo) pemContent;
                key = jcaPEMKeyConverter.getPublicKey(keyInfo);
            } else if (pemContent instanceof X509CertificateHolder) {
                X509CertificateHolder cert = (X509CertificateHolder) pemContent;
                key = jcaPEMKeyConverter.getPublicKey(cert.getSubjectPublicKeyInfo());
            } else {
                throw new IllegalArgumentException("Unsupported public key format '" +
                    pemContent.getClass().getSimpleName() + '"');
            }
        }
    
        return key;
    }
    
    从PEM读取私钥:

    private static PrivateKey getPrivateKeyFromPEM(Reader reader) throws IOException {
    
        PrivateKey key;
    
        try (PEMParser pem = new PEMParser(reader)) {
            JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
            Object pemContent = pem.readObject();
            if (pemContent instanceof PEMKeyPair) {
                PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
                KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
                key = keyPair.getPrivate();
            } else if (pemContent instanceof PrivateKeyInfo) {
                PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemContent;
                key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);
            } else {
                throw new IllegalArgumentException("Unsupported private key format '" +
                    pemContent.getClass().getSimpleName() + '"');
            }
        }
    
        return key;
    }
    

    Java支持对开箱即用的公钥和私钥使用DER(这基本上与PEM相同,正如OP所要求的,除了PEM文件包含基本64位数据加上页眉和页脚行)

    如果您使用的是Java 8+(假设您的密钥文件在类路径中可用),则无需任何外部库即可依赖此代码(模异常处理):

    类签名者{
    私人钥匙厂钥匙厂;
    公共签名人(){
    this.keyFactory=keyFactory.getInstance(“RSA”);
    }
    公钥getPublicKey(){
    byte[]publicKey=readFileAsBytes(“public key.der”);
    X509EncodedKeySpec keySpec=新X509EncodedKeySpec(公钥);
    返回keyFactory.generatePublic(keySpec);
    }
    public PrivateKey getPrivateKey(){
    byte[]privateKey=readFileAsBytes(“private key.der”);
    PKCS8EncodedKeySpec=新的PKCS8EncodedKeySpec(privateKey);
    返回keyFactory.generatePrivate(keySpec);
    }
    私有URI readFileAsBytes(字符串名称){
    URI fileUri=getClass().getClassLoader().getResource(名称).toURI();
    返回Files.readAllBytes(path.get(fileUri));
    }
    }
    
    对于记录,您可以使用以下命令将PEM密钥转换为DER密钥:

    $openssl pkcs8-topk8-通知PEM-输出DER-输入private-key.PEM-输出private-key.DER-nocrypt
    
    并通过以下方式获取公钥:

    $openssl rsa-in-private-key.pem-pubout-outform DER-out public-key.DER
    
    Hmmm。。。我觉得不错。我认为JCE中没有更好的方法,因为它没有PEM处理函数。您已经回答了自己的问题,并为我们提供了很好的示例代码。您可能应该将“getPemPublicKey”中的“privKeyPEM”更改为“pubKeyPEM”。不必使用
    openssl-nocrypt
    命令,如何做到这一点(或可以做到这一点)。这一部分也可以在Java中完成吗?“openssl genrsa”生成一个私钥,而不是密钥对@iznt链接已断开。你能指出区别/解释为什么你的更好吗?更好?更像(有点)不同:-)但是。。。公共X.509的负载使用更少的代码。私钥的负载更具可移植性(有些密钥类似于头文件“RSA”中指定的密钥,如:“----开始RSA私钥------”)并且不使用“Base64”(似乎是外部库);此代码仅使用Jre6
    -----BEGIN RSA私钥------
    是另一种格式,而不是PKCS8,尝试将其作为PKCS8读取将不起作用。此外,证书与原始公钥是不同的东西,因此这根本不适用于此Q中的数据。@dave_thompson_085此代码是从工作系统中提取的。。。你的确认来源是什么?(0)抱歉耽搁了,我很忙。(1) 我知道openssl是如何工作的。(2) 只为你,我爱你
    private static PublicKey getPublicKeyFromPEM(Reader reader) throws IOException {
    
        PublicKey key;
    
        try (PEMParser pem = new PEMParser(reader)) {
            JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
            Object pemContent = pem.readObject();
            if (pemContent instanceof PEMKeyPair) {
                PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
                KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
                key = keyPair.getPublic();
            } else if (pemContent instanceof SubjectPublicKeyInfo) {
                SubjectPublicKeyInfo keyInfo = (SubjectPublicKeyInfo) pemContent;
                key = jcaPEMKeyConverter.getPublicKey(keyInfo);
            } else if (pemContent instanceof X509CertificateHolder) {
                X509CertificateHolder cert = (X509CertificateHolder) pemContent;
                key = jcaPEMKeyConverter.getPublicKey(cert.getSubjectPublicKeyInfo());
            } else {
                throw new IllegalArgumentException("Unsupported public key format '" +
                    pemContent.getClass().getSimpleName() + '"');
            }
        }
    
        return key;
    }
    
    private static PrivateKey getPrivateKeyFromPEM(Reader reader) throws IOException {
    
        PrivateKey key;
    
        try (PEMParser pem = new PEMParser(reader)) {
            JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
            Object pemContent = pem.readObject();
            if (pemContent instanceof PEMKeyPair) {
                PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
                KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
                key = keyPair.getPrivate();
            } else if (pemContent instanceof PrivateKeyInfo) {
                PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemContent;
                key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);
            } else {
                throw new IllegalArgumentException("Unsupported private key format '" +
                    pemContent.getClass().getSimpleName() + '"');
            }
        }
    
        return key;
    }