Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PKCS#8 support #437

Closed
j4ns8i opened this issue Jul 18, 2018 · 8 comments
Closed

PKCS#8 support #437

j4ns8i opened this issue Jul 18, 2018 · 8 comments

Comments

@j4ns8i
Copy link

j4ns8i commented Jul 18, 2018

I'm debugging an application that uses sshj for connecting to other machines when given an ssh key pair. I uploaded a PKCS#8 encoded private key to this application, with which I verified I could manually authenticate to the other machines it manages through ssh (ssh -i ~/.ssh/mykey.pem node). However, the sshj library failed to authenticate with this key with the following stacktrace:

net.schmizz.sshj.userauth.UserAuthException: Exhausted available authentication methods
    at net.schmizz.sshj.SSHClient.auth(SSHClient.java:232)
    at net.schmizz.sshj.SSHClient.auth(SSHClient.java:208)
    at com.cloudera.server.cmf.node.NodeConfigurator.connect(NodeConfigurator.java:368)
    at com.cloudera.server.cmf.node.NodeConfigurator.configure(NodeConfigurator.java:965)
    at com.cloudera.server.cmf.node.NodeConfigurator.run(NodeConfigurator.java:1043)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: net.schmizz.sshj.userauth.UserAuthException: Problem getting public key from PKCS8KeyFile{resource=[PrivateKeyStringResource]}
    at net.schmizz.sshj.userauth.method.KeyedAuthMethod.putPubKey(KeyedAuthMethod.java:46)
    at net.schmizz.sshj.userauth.method.AuthPublickey.buildReq(AuthPublickey.java:62)
    at net.schmizz.sshj.userauth.method.AuthPublickey.buildReq(AuthPublickey.java:81)
    at net.schmizz.sshj.userauth.method.AbstractAuthMethod.request(AbstractAuthMethod.java:63)
    at net.schmizz.sshj.userauth.UserAuthImpl.authenticate(UserAuthImpl.java:68)
    at net.schmizz.sshj.SSHClient.auth(SSHClient.java:226)
    ... 9 more
Caused by: java.io.IOException: Could not read key pair from: [PrivateKeyStringResource]
    at net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile.readKeyPair(PKCS8KeyFile.java:165)
    at net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile.getPublic(PKCS8KeyFile.java:78)
    at net.schmizz.sshj.userauth.method.KeyedAuthMethod.putPubKey(KeyedAuthMethod.java:44)
    ... 14 more

Right before that stacktrace is a debug message (which I believe should be a warning instead):

Expected PEMEncryptedKeyPair or PEMKeyPair, got: org.bouncycastle.asn1.pkcs.PrivateKeyInfo@d11392bd

This happens because my PKCS#8 private key contains the standard header of -----BEGIN PRIVATE KEY-----, which bouncycastle's openssl library parses into a PrivateKeyInfo object. As it is currently, sshj will only properly construct a PKCS8KeyFile from a PEMEncryptedKeyPair or PEMKeyPair (as explained in the debug/warning message), which will only be returned from files with the -----BEGIN RSA PRIVATE KEY----- header and correspond to PKCS#1 encoded files. Thus, it appears like the PKCS8KeyFile will actually only work with PKCS#1 keys.

I believe it should still be possible to construct the PKCS#8 object from the PrivateKeyInfo object that the openssl library returns. The object should contain the modulus and public exponent needed to reconstruct the public key for the key pair.

@ghost
Copy link

ghost commented Aug 1, 2018

I'm having the same issue.

@j4ns8i
Copy link
Author

j4ns8i commented Aug 1, 2018

@Kamil-Cumulocity I could workaround the bug by converting my PKCS#8 private key into a PKCS#1 private key, if that helps you.

openssl rsa -in <PKCS#8 key> -out pkcs1key.pem

That should yield a PKCS#1 key with the necessary -----BEGIN RSA PRIVATE KEY----- header.

@garylewis8
Copy link

garylewis8 commented Oct 10, 2018

I'm encountering the same issue. The openssl conversion above initially didn't work for me. Then I realized my openssl is in FIPS mode. I used openssl on a non-fips system and it worked as expected.

@tancolo
Copy link

tancolo commented Nov 5, 2018

1. From the sshj source code, it seems support the PKCS8 key format. (VERSION 0.26.0)

./src/main/java/net/schmizz/sshj/userauth/keyprovider/KeyFormat.java
public enum KeyFormat {
    PKCS5,
    PKCS8,
    OpenSSH,
    OpenSSHv1,
    PuTTY,
    Unknown
}

2. When to apply PKCS8 format, reference:

./src/main/java/net/schmizz/sshj/userauth/keyprovider/KeyProviderUtil.java
private static KeyFormat keyFormatFromHeader(String header, boolean separatePubKey) {
        if (header.startsWith("-----BEGIN") && header.endsWith("PRIVATE KEY-----")) {
            if (separatePubKey && header.contains(OpenSSHKeyV1KeyFile.OPENSSH_PRIVATE_KEY)) {
                return KeyFormat.OpenSSHv1;
            } else if (separatePubKey) {
                // Can delay asking for password since have unencrypted pubkey
                return KeyFormat.OpenSSH;
            } else if (header.contains("BEGIN PRIVATE KEY") || header.contains("BEGIN ENCRYPTED PRIVATE KEY")) {
                return KeyFormat.PKCS8;
            } else {
                return KeyFormat.PKCS5;
            }
        } else if (header.startsWith("PuTTY-User-Key-File-")) {
            return KeyFormat.PuTTY;
        } else {
            return KeyFormat.Unknown;
        }
    }

When my key format is RSA, use these code, is OK.

PKCS8KeyFile kp8 = new PKCS8KeyFile();
kp8.init(getSshPrivateKey(), getSshPublicKey());

3. How to use PKCS8 and the format of Public and Priviate key

./src/test/java/net/schmizz/sshj/keyprovider/PKCS8KeyFileTest.java

I followed this guaid, just use RSA format key.

@j4ns8i
Copy link
Author

j4ns8i commented Nov 22, 2018

@tancolo In this context, I'm referring to parsing an existing PKCS#8 encoded key pair created through openssl as opposed to creating the key through this library. It does indeed look like the library supports the PKCS#8 encoding, but I think the implementation runs into an issue when trying to parse the key pair based on the heading.

As I said, the bouncycastle openssl library returns a different type of object than sshj expects for a PKCS#8 file; the header that sshj ties to the PKCS#8 format is actually associated with PKCS#1 files.

You can find the definition of the header for PKCS#8 files in RFC 5958, which simply uses BEGIN PRIVATE KEY. I unfortunately can't find any official documentation for the BEGIN RSA PRIVATE KEY header right now.

@yeborisov
Copy link

Indeed as @j4ns8i mentioned there is an issue with PKCS#8 support. I have met the same problem and it is not with SFTP.
This is the stacktrace:
Caused by: java.io.IOException: Could not read key pair from: [PrivateKeyFileResource] /opt/data/test.pem at net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile.readKeyPair(PKCS8KeyFile.java:101) at net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider.getPublic(BaseFileKeyProvider.java:81) at net.schmizz.sshj.userauth.method.KeyedAuthMethod.putPubKey(KeyedAuthMethod.java:45)

@hierynomus
Copy link
Owner

Fixed with #708 and #713. Thanks @exceptionfactory!

@j4ns8i
Copy link
Author

j4ns8i commented Aug 28, 2021

yes, thank you both @hierynomus and @exceptionfactory! i haven’t worked on this since roughly around the time i submitted the issue, but i want to express my appreciation for your contributions to and maintenance of this library. you rock

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants