关于jsch使用rsa私钥登录-JSch login using RSA private key

本文不涉及任何高深技术难点,仅记录解决问题的过程,供自己在下次遇到的时候参考。
最近在对接工行的对账平台,需要使用sftp配合rsa私钥下载对账单,翻看了以前的代码以及网上搜索了一下,发现代码实现的话用jsch比较方便。
然后开始代码实现,然后发现网上大部分是通过密码或者私钥文件登录,但是这边不想存储文件,所以废了点时间找了直接通过私钥字符串来实现:

jsch.addIdentity("id_rsa", key.getBytes(), null, null);

本地密钥对是通过工行提供的工具来生成的,私钥类似于这种:

MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCP3OMU9GXXXXXXXX...XXXXXX4W0F7bIvUZeuurhxv8MfER3B/H8hErSia81+HY=

是没有头部和尾部的
然后第一遍尝试果然出错了:

Caused by: com.jcraft.jsch.JSchException: invalid privatekey: [B@44e81672

于是开始仔细查看,然后发现应该是缺少头部和尾部,于是把私钥的头部为尾部加上,变成如下格式:

-----BEGIN RSA PRIVATE KEY-----
XXX...XXX=
-----END RSA PRIVATE KEY-----

再试一次,还是同样的错误,我一度怀疑是不是工行的工具有问题
后来在网上搜索rsa的密钥格式时,发现原来有pkcs1和pkcs8的区别,但是此时我粗心的没看他们具体区别是什么(毕竟大段的英文很费脑),只是简单的看到有区别,于是我去网上找了个简单的函数,可以把私钥在pkcs1和pkcs8之间转化,由于我也不确定我自己的是1还是8,因此两个都执行了,最后确定我的是8,然后转成1之后拼上头尾,果然可以了,正常事情到这里应该结束了,毕竟已经解决了
但是我又思考了下,jsch这么多人用,没道理不支持pkcs8,最终在官网发现这么一行:

	•	supporting private keys in OpenSSL(traditional SSLeay) and PKCS#8 format.

也就是说他们应该是支持的,那为什么我的不行呢
于是只能去看了下1和8的示例,参考文档:
cryptography - PKCS#1 and PKCS#8 format for RSA private key - Stack Overflow
然后发现原来1的头部和尾部是 “BEGIN RSA PRIVATE KEY” 和 “END RSA PRIVATE KEY”,而8的是“BEGIN PRIVATE KEY”和“END PRIVATE KEY”,果然是我的问题,我直接把1和8的转换函数都删了,然后在私钥拼了不带“RSA”的头尾:

-----BEGIN PRIVATE KEY-----
XXX...XXX=
-----END PRIVATE KEY-----

果然可以了
本来就只要拼一个正确的头尾的事情,走了很多弯路 😮‍💨

JSch login using RSA private key

Translate the blog into English:

This article does not involve any technical difficulties, just records the process of problem-solving for reference the next time it is encountered.
Recently, when integrating with ICBC's reconciliation platform, it is necessary to use SFTP in combination with an RSA private key to download the account statement. After reviewing previous code and searching online, it was found that using JSch is more convenient for code implementation.
Then I started implementing the code and found that most online examples log in using a password or a private key file. However, on this side, I did not want to store the file, so I spent some time finding a way to implement it directly using the private key string:
jsch.addIdentity(“id_rsa”, key.getBytes(), null, null);

The local key pair was generated using a tool provided by ICBC, and the private key is similar to this format: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCP3OMU9GXXXXXXXX…XXXXXX4W0F7bIvUZeuurhxv8MfER3B/H8hErSia81+HY=

There are no headers and footers.
The first attempt resulted in an error: Caused by: com.jcraft.jsch.JSchException: invalid privatekey: [B@44e81672

So I carefully checked it, and then realized that it should be lacking the headers and footers. Therefore, I added the headers and footers to the private key, making it in the following format:
-----BEGIN RSA PRIVATE KEY-----
XXX…XXX=
-----END RSA PRIVATE KEY-----

After trying again, the same error occurred. I once doubted whether there was a problem with ICBC's tool. Later, when searching online for the format of the RSA key, I found that there is a difference between pkcs1 and pkcs8, but at that time I carelessly did not see the specific differences between them (after all, long English is very taxing), but only briefly noted that there is a difference. So, I went online to find a simple function that can convert the private key between pkcs1 and pkcs8, because I was not sure whether mine was 1 or 8, so I executed both, and finally confirmed mine was 8. Then, after converting it to 1 and adding the headers and footers, it worked. This should have been the end of the normal events after solving it.
But then, I thought again. So many people use JSch, it doesn’t make sense that it doesn’t support pkcs8. Eventually, I found a line on their official website: • supporting private keys in OpenSSL (traditional SSLeay) and PKCS#8 format.

This means they should support it. So why didn’t it work for me?
Therefore, I had to look at examples of 1 and 8, and found reference documentation:
https://stackoverflow.com/questions/48958304/pkcs1-and-pkcs8-format-for-rsa-private-key


Then I found out that the headers and footers for 1 are "BEGIN RSA PRIVATE KEY" and "END RSA PRIVATE KEY", while for 8 it is "BEGIN PRIVATE KEY" and "END PRIVATE KEY". Indeed, the problem was mine. I directly deleted the conversion functions between 1 and 8, then added the headers and footers to the private key without "RSA":
--—BEGIN PRIVATE KEY-----
XXX…XXX=
-----END PRIVATE KEY-----

This worked.
It was just a matter of adding the correct headers and footers, but I took a lot of detours 😮‍💨