使用 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】:我也偶然发现了这个问题。 在 mac 上运行 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 时出现“无效的私钥”的主要内容,如果未能解决你的问题,请参考以下文章
使用 JSch 从 Java 连接到 SSH 服务器时出现“JSchException:拒绝 HostKey”