在使用 JSch 时“无效的私钥”

我使用以下代码在 Java 应用程序中使用 Git。 我有一个有效的密钥(一直使用它) ,这个特定的代码以前在相同的密钥和 git 存储库中对我有用,但是现在我得到了下面的例外:

无效的私钥: [ B@59c40796。

在这条线上:

jSch.addIdentity("<key_path>/private_key.pem");

我的全部代码:

    String remoteURL = "ssh://git@<git_repository>";
TransportConfigCallback transportConfigCallback = new SshTransportConfigCallback();
File gitFolder = new File(workingDirectory);
if (gitFolder.exists()) FileUtils.delete(gitFolder, FileUtils.RECURSIVE);


Git git = Git.cloneRepository()
.setURI(remoteURL)
.setTransportConfigCallback(transportConfigCallback)
.setDirectory(new File(workingDirectory))
.call();
}




private static class SshTransportConfigCallback implements TransportConfigCallback {
private final SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
@Override
protected void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
}


@Override
protected JSch createDefaultJSch(FS fs) throws JSchException {
JSch jSch = super.createDefaultJSch(fs);
jSch.addIdentity("<key_path>/private_key.pem");


return jSch;
}
};

在网上搜索之后,我将 createDefaultJSch 更改为使用 pemWriter:

@Override
protected JSch createDefaultJSch(FS fs) throws JSchException {
JSch jSch = super.createDefaultJSch(fs);
byte[] privateKeyPEM = null;


try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");


List<String> lines = Files.readAllLines(Paths.get("<my_key>.pem"), StandardCharsets.US_ASCII);
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(String.join("", lines)));
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);


PKCS8Generator pkcs8 = new PKCS8Generator(privKey);


StringWriter writer = new StringWriter();
PemWriter pemWriter = new PemWriter(writer);
pemWriter.writeObject(pkcs8);


privateKeyPEM = writer.toString().getBytes("US-ASCII");


} catch (Exception e) {
e.printStackTrace();
}


jSch.addIdentity("git", privateKeyPEM, null, null);


return jSch;
}

但仍然得到 “无效私钥”异常。

82286 次浏览
  1. You read a file named .pem and de-base64 all of it and treat the result as PKCS8-unencrypted, apparently successfully. This means the file was NOT PEM-format. PEM format at minimum MUST have the dash-BEGIN and dash-END lines to be valid, which if not removed cause de-base64 to either fail or be wrong. (Some PEM formats also have 822-style headers which must be handled.)

  2. You appear to be using BouncyCastle, but in my versions there is no PKCS8Generator constructor that takes only RSAPrivateKey. The closest thing that works is JcaPKCS8Generator (RSAPrivateKey implements PrivateKey, OutputEncryptor=null) (i.e. a different but related class, and two arguments not one).

  3. PemWriter is buffered, and you didn't flush it before looking at the underlying StringWriter. As a result writer.toString().getBytes() is an empty/zero-length array, which JSch rightly considers invalid.

With #2 and #3 fixed and using my input, and calling JSch directly instead of via JGit, it works for me.

I also stumbled upon this issue. running Jgit on mac, for some users we saw the following exception:

org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:160)
at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:137)
at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:274)
at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:169)
at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:136)
at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:122)
at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1236)
at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:234)
... 17 more
Caused by: com.jcraft.jsch.JSchException: invalid privatekey: [B@e4487af
at com.jcraft.jsch.KeyPair.load(KeyPair.java:664)
at com.jcraft.jsch.KeyPair.load(KeyPair.java:561)
at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:40)
at com.jcraft.jsch.JSch.addIdentity(JSch.java:407)
at com.jcraft.jsch.JSch.addIdentity(JSch.java:367)
at org.eclipse.jgit.transport.JschConfigSessionFactory.getJSch(JschConfigSessionFactory.java:276)
at org.eclipse.jgit.transport.JschConfigSessionFactory.createSession(JschConfigSessionFactory.java:220)
at org.eclipse.jgit.transport.JschConfigSessionFactory.createSession(JschConfigSessionFactory.java:176)
at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:110)

The root cause was discovered to be the ssh private key mismatch. The exception only happened for users with key of newer kind ed25519, which outputs this key header:

-----BEGIN OPENSSH PRIVATE KEY-----

instead of kind RSA:

-----BEGIN RSA PRIVATE KEY-----

regenerating an RSA key (ssh-keygen -t rsa), made the exception go away.

Edit following comments: If you have OpenSSH 7.8 and above you might need to add -m PEM to the generation command: ssh-keygen -t rsa -m PEM

Recent versions of OpenSSH (7.8 and newer) generate keys in new OpenSSH format by default, which starts with:

-----BEGIN OPENSSH PRIVATE KEY-----

JSch does not support this key format.


You can use ssh-keygen to convert the key to the classic OpenSSH format:

ssh-keygen -p -f <privateKeyFile> -m pem -P passphrase -N passphrase

This "abuses" -p (change passphrase) command. It will overwrite the private key file identified by the -f option with a new private key in the classic OpenSSH format (pem). You can keep the current passphrase as the new passphrase. If the key was not encrypted with a passphrase, use "" instead of passphrase. After, you can use ssh-keygen -y -e -f <privateKeyFile> >temp.pub to compare public keys and verify the existing public key works with the new format of private key.

For Windows users: Note that ssh-keygen.exe is now built-in in Windows 10/11. And can be downloaded from Microsoft Win32-OpenSSH project for older versions of Windows.


On Windows, you can also use PuTTYgen (from PuTTY package):

  • Start PuTTYgen
  • Load the key
  • Go to Conversions > Export OpenSSH key.
    For RSA keys, it will use the classic format.

If you are creating a new key with ssh-keygen, just add -m PEM to generate the new key in the classic format:

ssh-keygen -m PEM

Actually, the original JSch does not seem to be actively maintained anymore. So particularly, if you are starting new project, it might not be the best library to begin with. JSch suffers from many compatibility problems nowadays. For some important ones, see:

Instead, you might consider using this JSch fork:
https://github.com/mwiede/jsch

Among other, it does support the new OpenSSH key format.

JSch does not support this key format. It supports only RSAPrivateKey. This command works for me. Try this solution

ssh-keygen -m PEM -t rsa -b 2048

//edited to rsa with 2048 keysize

Quite late to reply, but want to leave track of how to face the issue.

The point, as meny mentioned, is actually the way you generate the key and with the -m PEM option resolves.

However if, just as happened to me, you could not regenerate the key because the public part had already been installed in several servers, you can still convert your private key to a suitable format.

To do so, just issue the following command:

ssh-keygen -p -m pem -f id_rsa

It will ask for input of a new passphrase. With parameters -P (old passphrase) and -N (new passphrase) you can provide them at once, if needed.

Instead of converting the OPENSSH key format to the format, which original JSch supports, you can also switch to a fork of JSch, which you can find at https://github.com/mwiede/jsch

Your only need to replace your JSch Maven coordinates with com.github.mwiede:jsch:0.1.61.

The fork does support the OPENSSH key format and several more algorithms, which might become important in the future, as OpenSSH servers will restrict the allowed sets of algorithms to the most secure ones.

I wanted to add that in order to avoid the below headers you need to create the key with

-C "any-comment"

Headers that will be removed from the private Key:

Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,3551DFC375229D5758289E8D366082FE

Leaving only

 -----BEGIN RSA PRIVATE KEY-----
YOUR_KEY_HERE
-----END RSA PRIVATE KEY-----

So have the same problem fix it by PuTTYgen

enter image description here

1)Open PuTTYgen

  1. Press "Load" and choose the privateKey

enter image description here

3)press now on "Conversions" -> and choose the first option "Export OPENSSH key"

enter image description here

  1. save it as file (not need any format) and use it