"Invalid privatekey" 使用 JSch 时

"Invalid privatekey" when using JSch

我正在使用以下代码在 Java 应用程序中使用 Git。 我有一个有效的密钥(一直使用它),并且此特定代码之前使用相同的密钥和 git 存储库对我有用,但现在我得到以下异常:

invalid privatekey: [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;
}

但仍然出现 "invalid privatekey" 异常。

  1. 您读取了一个名为 .pem 的文件并将其 de-base64 all 并将结果视为未加密的 PKCS8,显然是成功的。这意味着该文件不是 PEM-format。 PEM 格式至少必须有 dash-BEGIN 和 dash-END 行是有效的,如果不删除它们会导致 de-base64 失败或错误。 (一些 PEM 格式也有 822 样式 headers 必须处理。)

  2. 您似乎在使用 BouncyCastle,但在我的版本中没有仅采用 RSAPrivateKeyPKCS8Generator 构造函数。最接近的是 JcaPKCS8Generator (RSAPrivateKey implements PrivateKey, OutputEncryptor=null)(即不同但相关的 class,两个参数不是一个)。

  3. PemWriter 已缓冲,您在查看底层 StringWriter 之前没有刷新它。结果 writer.toString().getBytes() 是一个 empty/zero-length 数组,JSch 正确地认为它无效。

修复#2 和#3 并使用我的输入,并直接调用 JSch 而不是通过 JGit,它对我有用。

我也偶然发现了这个问题。 运行 Jgit on mac,对于某些用户,我们看到了以下异常:

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)

发现根本原因是ssh私钥不匹配。 异常仅发生在使用较新类型密钥 ed25519 的用户身上,它输出此密钥 header:

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

而不是种类 RSA:

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

正在重新生成 RSA 密钥 (ssh-keygen -t rsa),使异常消失。

编辑以下评论: 如果您有 OpenSSH 7.8 及更高版本,您可能需要将 -m PEM 添加到生成命令中: ssh-keygen -t rsa -m PEM

最新版本的 OpenSSH(7.8 及更新版本)默认以 new OpenSSH 格式生成密钥,其开头为:

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

JSch 不支持这种密钥格式。


您可以使用 ssh-keygen 将密钥转换为 经典 OpenSSH 格式:

ssh-keygen -p -f file -m pem -P passphrase -N passphrase

(如果密钥未使用密码加密,请使用 "" 而不是 passphrase

对于 Windows 用户:请注意 ssh-keygen.exe 现在内置于 Windows 10/11 中。对于 Windows.

的旧版本,可以是 downloaded from Microsoft Win32-OpenSSH project

在 Windows 上,您还可以使用 PuTTYgen(来自 PuTTY package):

  • 启动 PuTTYgen
  • 加载密钥
  • 转到转换 > 导出 OpenSSH 密钥
    对于 RSA 密钥,它将使用 经典 格式。

如果您使用 ssh-keygen 创建新密钥,只需添加 -m PEM 即可生成 经典 格式的新密钥:

ssh-keygen -m PEM

JSch 不支持这种密钥格式。它仅支持 RSAPrivateKey。 这个命令对我有用。试试这个解决方案

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

//编辑为 2048 密钥大小的 rsa

很晚才回复,但想留下如何面对问题的线索。

正如许多人提到的那样,重点实际上是您生成密钥的方式以及使用 -m PEM 选项解析的方式。

然而,如果像我一样,由于 public 部分已经安装在多个服务器中,您无法重新生成密钥,您仍然可以将您的私钥转换为合适的格式。

为此,只需发出以下命令:

ssh-keygen -p -m pem -f id_rsa

它将要求输入新密码。使用参数 -P(旧密码)和 -N(新密码),如果需要,您可以立即提供它们。

除了将 OPENSSH 密钥格式转换为原始 JSch 支持的格式,您还可以切换到 JSch 的分支,您可以在 https://github.com/mwiede/jsch

找到

您只需将 JSch Maven 坐标替换为 com.github.mwiede:jsch:0.1.61

分支确实支持 OPENSSH 密钥格式和更多算法,这在未来可能会变得很重要,因为 OpenSSH 服务器会将允许的算法集限制为最安全的算法集。

我想补充一点,为了避免以下情况 headers 您需要使用

创建密钥
-C "any-comment"

Headers 将从私钥中删除:

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

只剩下

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