【问题标题】:"Invalid privatekey" when using JSch使用 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”异常。

【问题讨论】:

    标签: java ssh jsch


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

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

      3. 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
          • 在一种情况下出现提示时,您可能输入了密码,而在另一种情况下则没有。
          猜你喜欢
          • 2013-02-26
          • 1970-01-01
          • 2012-12-25
          • 1970-01-01
          • 2018-10-12
          • 1970-01-01
          • 1970-01-01
          • 2015-04-11
          • 1970-01-01
          相关资源
          最近更新 更多