使用 JSch 时出现“无效的私钥”

Posted

技术标签:

【中文标题】使用 JSch 时出现“无效的私钥”【英文标题】:"Invalid privatekey" when using JSch 【发布时间】:2019-04-07 14:54:49 【问题描述】:

我正在使用以下代码在 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;

但仍然得到“invalid privatekey”异常。

【问题讨论】:

【参考方案1】:

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 中。对于旧版本的 Windows,可以是 downloaded from Microsoft Win32-OpenSSH project。


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

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

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

ssh-keygen -m PEM

【讨论】:

额外的荣誉指出如何转换现有密钥,而不是仅仅生成一个新密钥【参考方案2】:

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

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 的密钥的用户身上,它会输出此密钥标头:

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

而不是那种 RSA

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

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

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

【讨论】:

此外,JSch 似乎读取了~/ssh/config,如果通过IdentityFile 指令将任何非RSA 和/或非PEM 文件添加到列表中,则JSch 会失败。【参考方案3】:

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

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

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

【讨论】:

【参考方案4】:

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

    您似乎在使用 BouncyCastle,但在我的版本中,没有 PKCS8Generator 构造函数只接受 RSAPrivateKey。最接近的工作是JcaPKCS8Generator (RSAPrivateKey implements PrivateKey, OutputEncryptor=null)(即一个不同但相关的类,两个参数不是一个)。

    PemWriter 被缓冲,在查看底层StringWriter 之前没有刷新它。结果writer.toString().getBytes() 是一个空/零长度数组,JSch 正确地认为它无效。

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

【讨论】:

【参考方案5】:

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

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

//用2048 keysize编辑成rsa

【讨论】:

【参考方案6】:

回复晚了,但想留下如何面对问题的轨迹。

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

但是,如果像我一样,您无法重新生成密钥,因为公共部分已经安装在多个服务器上,您仍然可以将您的私钥转换为合适的格式。

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

ssh-keygen -p -m pem -f id_rsa

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

【讨论】:

没错,但这就是我上面的回答已经说了。【参考方案7】:

我想添加它以避免出现以下标题,您需要使用以下标题创建密钥

-C "any-comment"

将从私钥中删除的标头:

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

只留下

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

【讨论】:

这不正确。这些标头表明密钥是使用密码加密的。他们与-C无关。 由于某种原因,在我的配置中,带有-C 的相同命令返回了没有标题的文件。如果我手动删除 RSA 密钥将不起作用,我必须使用 -C 在一种情况下出现提示时,您可能输入了密码,而在另一种情况下则没有。

以上是关于使用 JSch 时出现“无效的私钥”的主要内容,如果未能解决你的问题,请参考以下文章

SharpSSH 无效的私钥

DBeaver ssh 隧道无效的私钥

使用 JSch 从 Java 连接到 SSH 服务器时出现“JSchException:拒绝 HostKey”

使用私钥取消 JSch Auth

使用 JSch 接受的 ssh-keygen 创建 SSH 私钥 [重复]

使用私钥连接到服务器时出现pysftp AuthenticationException