首页 > 解决方案 > 从 Java 文件中读取 PEM 编码的加密私钥

问题描述

我目前正在尝试读取包含私钥的 pem 文件。密钥是由另一个使用 Python 的系统生成的,我收到一个 pem 文件,我需要从中读取私钥。pem 文件的内容如下所示:

-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,F58A9640CD4A47DF1C8FB281C7B36D69

jLMaq0JWEV/rJnM0Pxho7jyY/c5I7lZ8mCawGINPDz7Zpdv6MEsc8rFjVZHur4+1
I3AxD8NDNkhNVofGpSlAnciOVeILwsScgJSfxHA14s+SCqOX3q4zEB+jJDDfJGn3
w+7I9X0qB4MYSWLwT8r3GUAETigeJZ7m7cSlcO9cSMg=
-----END EC PRIVATE KEY-----

在另一个系统中,我的一位同事正在使用 Python 生成和读取 pem 文件。他是这样读钥匙的:

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec

def read(path):
    with open(path, "rb") as f:
        data = f.read()

    return data

# Load a PEM-encoded, encrypted private key
def load(path, passphrase):
    return serialization.load_pem_private_key(read(path), passphrase)

现在,我必须用 Java 阅读这个生成的 pem 文件。到目前为止,我尝试通过 FileInputStream 读取文件的内容,并尝试使用 EncodedKeySpec 类生成私钥:

FileInputStream is = new FileInputStream(file);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(is.readAllBytes());
KeyFactory factory = KeyFactory.getInstance("EC");
PrivateKey key = factory.generatePrivate(spec);

这样,虽然我总是收到关于

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : Short read of DER length
    at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePrivate(ECKeyFactory.java:169)
    at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)
    at my.package.name.here.MyInputReader.loadKey(MyInputReader.java:107)
    at my.package.name.here.MyInputReader.createDLCBinary(MyInputReader.java:47)
    at my.package.name.here.MyInputReaderTest.test(MyInputReaderTest.java:11)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.security.InvalidKeyException: IOException : Short read of DER length
    at java.base/sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:350)
    at java.base/sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:355)
    at jdk.crypto.ec/sun.security.ec.ECPrivateKeyImpl.<init>(ECPrivateKeyImpl.java:74)
    at jdk.crypto.ec/sun.security.ec.ECKeyFactory.implGeneratePrivate(ECKeyFactory.java:237)
    at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePrivate(ECKeyFactory.java:165)
    ... 69 more

我注意到我的同事在从 pem 文件中读取密钥时使用了密码,我也可以访问此密码。虽然我没有找到任何功能来实际使用文件内容上的密码。我可能在这里遗漏了一些重要的东西,我希望你能帮我解决这个问题。

更新: 这个问题也在这个线程中讨论,并有一些可能的解决方案:如何用java读取密码加密密钥?

标签: javabouncycastleprivate-keypem

解决方案


感谢@MichaelFehr 和@dave_thompson_085,我找到了正确的方向。不幸的是,我将无法使用命令行,只能使用 Java 代码。

我寻找了类似的问题并遇到了这个线程: 如何使用 java 读取密码加密密钥?

我使用那里的解决方案来实现以下代码片段来读取关键文件:

    public PrivateKey loadKey(String path, String passphrase) {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

    try {
        File file = ResourceUtils.getFile(path);
        PEMParser pemParser = new PEMParser(new FileReader(file));
        Object o = pemParser.readObject();
        PrivateKeyInfo pki;

        if (o instanceof PKCS8EncryptedPrivateKeyInfo) {

            PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;

            JcePKCSPBEInputDecryptorProviderBuilder builder =
                    new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC");

            InputDecryptorProvider idp = builder.build(passphrase.toCharArray());

            pki = epki.decryptPrivateKeyInfo(idp);
        } else if (o instanceof PEMEncryptedKeyPair) {

            PEMEncryptedKeyPair epki = (PEMEncryptedKeyPair) o;
            PEMKeyPair pkp = epki.decryptKeyPair(new BcPEMDecryptorProvider(passphrase.toCharArray()));

            pki = pkp.getPrivateKeyInfo();
        } else {
            throw new PKCSException("Invalid encrypted private key class: " + o.getClass().getName());
        }

        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        return converter.getPrivateKey(pki);
    } catch (Exception e) {
        System.out.println("Error happened while loading key");
        e.printStackTrace();
        return null;
    }
}

不是世界上最好的代码,但它现在可以工作,将来会得到改进。再次感谢你的帮助 :)


推荐阅读