2

I'm currently trying to read a pem file containing a private key. The key is generated by another system which uses Python and I receive a pem-file which I need to read the private key from. The content of the pem file looks like this:

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

In the other system, a collegue of mine was generating and reading the pem file with Python. He read the key like this:

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)

Right now, I have to read this generated pem-file in Java. Up until now I tried to read the content of the file via a FileInputStream and tried to generate the private key with the EncodedKeySpec class:

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

This way though I always receive an exception regarding the

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

I noticed that my collegue used a password when reading the key from the pem file and I also have access to this password. Although I did not find any functionality to actually use the password on the file content. I might be missing something crucial here and I hope you can help me out with this.

UPDATE: This problem is also discussed in this thread and has some possible solutions to it: How to read a password encrypted key with java?

Maurice Perry
  • 9,261
  • 2
  • 12
  • 24
Christian
  • 33
  • 1
  • 7
  • 2
    Your EC private key is in "traditional" OpenSSL encoding ("SEC1") and encrypted with AES-256-CBC. To read this kind of keys in Java, you need an external library like BouncyCastle. Or... you code using an ASN1 reader from scratch. – Michael Fehr Sep 02 '21 at 11:24
  • 1
    (@MichaelFehr) ... or use commandline `openssl pkey` (or `openssl pkcs8 -topk8 -nocrypt`) to convert it to PKCS8 unencrypted, and optionally also to DER, if having such an unencrypted key is acceptable in your environment. PKCS8 unencrypted DER can be used directly in PKCS8EncodedKeySpec in KeyFactory, and PKCS8 unencrypted PEM can be converted to DER just by removing the BEGIN/END lines and linebreaks and base64-decoding the rest. – dave_thompson_085 Sep 02 '21 at 11:52
  • Hmm, I see. Thanks for the input I will try this as soon as I can. – Christian Sep 02 '21 at 12:23

1 Answers1

1

Thanks to @MichaelFehr and @dave_thompson_085 I got into the right direction. Unfortunately I won't be able to use commandlines and only work with Java code.

I looked for similar questions and came across this thread: How to read a password encrypted key with java?

I used the solution there to implement the following code snippet to read key files:

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

Not the best code in the world, but it will work for now and will be improved in the future. Thanks again for your help :)

Christian
  • 33
  • 1
  • 7