Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid Private Key for UNIX runs #266

Closed
PopDanAlexandru opened this issue Jan 11, 2023 · 5 comments
Closed

Invalid Private Key for UNIX runs #266

PopDanAlexandru opened this issue Jan 11, 2023 · 5 comments

Comments

@PopDanAlexandru
Copy link

PopDanAlexandru commented Jan 11, 2023

Hi. I'm using the JSch library in order to establish the SFTP connection to a remote server.

My Spring Boot app runs within UNIX Operating System, from a WAR file, and I encountered this error "JSchException: invalid privatekey" when I:

The reason for this is an if statement from the JSch source code, class KeyPair, method parseHeader at line 1274:
if (buf[i] == 0x0d) {...}: https://github.com/mwiede/jsch/blob/master/src/main/java/com/jcraft/jsch/KeyPair.java#L1274

Because of this if statement, are taken into consideration only the newline characters encoded like \r (0x0D == 13) (applicable to Windows and MacOS). But UNIX uses \n (0x0A == 10). I've seen the encodings explained in this thread, for example: What are the differences between char literals '\n' and '\r' in Java?


So if the Private Key has the correct structure, but I run the app from Linux (or any other UNIX OS), then the Byte Array that corresponds to the content of the Private Key will be different based on the Operating System from which the app is being run.

This is an example where the Byte Arrays have different contents:
When app runs on Linux: [114, 115, 97, 10, 69, 110] => 10 is the \n
When app runs on Windows: [114, 115, 97, 13, 10, 69, 110] => 13 10 is the \r\n

This image illustrates the different contents of the Byte Arrays converted back as Strings , when the app is run as a WAR file from Linux and Windows, captured through remote debugging. I used the latest version of JSch that is currently available: https://mvnrepository.com/artifact/com.jcraft/jsch/0.1.55


Therefore, a temporary solution that I found would be to:

1. Get the InputStream from your Private and Public Keys (the files were added to the "resources" directory of the Spring project):

InputStream privateKeyInputStream = new ClassPathResource("private-key.ppk").getInputStream();
InputStream publicKeyInputStream = new ClassPathResource("public-key.ppk").getInputStream();

2. Convert the InputStream to ByteArrays

byte[] privateKeyAsByteArray = IOUtils.toByteArray(privateKeyInputStream);
byte[] publicKeyAsByteArray = IOUtils.toByteArray(publicKeyInputStream);

3. Fix the encoding, by replacing the bytes of 10 (0x0A) with bytes of 13 (0x0D), before calling the addIdentity method from JSch:

for (int i = 0; i < privateKeyAsByteArray.length; i++) {
    if (privateKeyAsByteArray[i] == 10) {   // if current element is a 10 (\n) (UNIX)
        privateKeyAsByteArray[i] = 13;      // replace it with 13 (\r) (a byte that can be interpreted)
    }
}

for (int i = 0; i < publicKeyAsByteArray.length; i++) {
    if (publicKeyAsByteArray[i] == 10) {    // if current element is a 10 (\n) (UNIX)
        publicKeyAsByteArray[i] = 13;       // replace it with 13 (\r) (a byte that can be interpreted)
    }
}

4. Call the addIdentity method:

jSch.addIdentity("private-key.ppk", privateKeyAsByteArray, publicKeyAsByteArray, passphraseAsString.getBytes());

Is there a better solution, other than this workaround? I think that the official solution would be to update that if statement from the JSch source code, class KeyPair, method parseHeader at line 1274, such that it considers also byte 0x0A (\n == 10), not only 0x0D (\r == 13).

I'm looking forward to your response.
Best regards

@norrisjeremy
Copy link
Contributor

Hi @PopDanAlexandru,

I suspect it was coded this way originally because it was assumed that any usage of PuTTY type keys would occur on Windows, not Unix.

Do you know how your PuTTY type keys were generated? Were they generated on a Unix machine originally? Or were they copied from a Windows machine and somehow had their newlines changed?

Thanks,
Jeremy

@PopDanAlexandru
Copy link
Author

Thanks for your reply.

The keys were initially generated on Windows, using the RSA encryption, and added to the "resources" directory of the Spring Boot project.

When the project is deployed to the cloud instance, it is build using mvn install command, being generated the WAR file.
The app is run from that WAR file. So if I deploy the app to a Linux cloud instance, and also to a Windows cloud instance, then there would be two WAR files (one for each Operating System, for each cloud instance).

The Private Key is the exact same file, but when it is opened/accessed, then the byte array differs based on the Operating System from which the app is being run.

I think that even if I would have generated the Keys from Unix, then it would be same issue, because I've seen that the byte representation depends on (is influenced) by the OS from which the file is accessed.

@norrisjeremy
Copy link
Contributor

Hi @PopDanAlexandru,

I've added a change in #264 that hopefully will address this issue.

Thanks,
Jeremy

@PopDanAlexandru
Copy link
Author

PopDanAlexandru commented Jan 12, 2023

Thank you very much. I looked over the Pull Request, and I think it's fine.

The issue will be closed when the fix is publicly available?

norrisjeremy added a commit to norrisjeremy/jsch that referenced this issue Jan 13, 2023
norrisjeremy added a commit to norrisjeremy/jsch that referenced this issue Jan 14, 2023
@mwiede
Copy link
Owner

mwiede commented Jan 28, 2023

0.2.7 is published now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants