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

ssh_key_needs_passphrase() doesn't work with OpenSSH-formatted keys #421

Open
jdblischak opened this issue Apr 26, 2020 · 5 comments
Open

Comments

@jdblischak
Copy link
Contributor

The function cred_ssh_key() calls the function ssh_key_needs_passphrase() to determine if a key requires a passphrase. I was informed by @jakejh in workflowr/workflowr#203 (comment) that this function did not work for him. Thus I tested it on Windows and Ubuntu with keys that did or did not require a passphrase. I found that ssh_key_needs_passphrase() always returns FALSE. I also tried this suggestion to check for the string MII. However, at least for the ssh-keygen settings I used, this always returned TRUE.

I used the following line to create the test SSH keys:

ssh-keygen -o -t rsa -b 4096 -C "message"

Key with passphrase on Windows

> packageVersion("git2r")
[1] ‘0.26.1’
> osVersion
[1] "Windows 10 x64 (build 18363)"
> git2r:::ssh_key_needs_passphrase()
[1] FALSE
> privatekey = git2r::ssh_path("id_rsa")
> private_content <- readLines(privatekey, n = 3L)
> grepl("encrypted", private_content, ignore.case = TRUE)
[1] FALSE FALSE FALSE
> !startsWith(private_content[2L], "MII")
[1] TRUE

Key without passphrase on Windows

> packageVersion("git2r")
[1] ‘0.26.1’
> osVersion
[1] "Windows 10 x64 (build 18363)"
> git2r:::ssh_key_needs_passphrase()
[1] FALSE
> privatekey = git2r::ssh_path("id_rsa")
> private_content <- readLines(privatekey, n = 3L)
> grepl("encrypted", private_content, ignore.case = TRUE)
[1] FALSE FALSE FALSE
> !startsWith(private_content[2L], "MII")
[1] TRUE

Key with passphrase on Ubuntu

> packageVersion("git2r")
[1] ‘0.26.1’
> osVersion
[1] "Ubuntu 18.04.4 LTS"
> git2r:::ssh_key_needs_passphrase()
[1] FALSE
> privatekey = git2r::ssh_path("id_rsa")
> private_content <- readLines(privatekey, n = 3L)
> grepl("encrypted", private_content, ignore.case = TRUE)
[1] FALSE FALSE FALSE
> !startsWith(private_content[2L], "MII")
[1] TRUE

Key without passphrase on Ubuntu

> packageVersion("git2r")
[1] ‘0.26.1’
> osVersion
[1] "Ubuntu 18.04.4 LTS"
> git2r:::ssh_key_needs_passphrase()
[1] FALSE
> privatekey = git2r::ssh_path("id_rsa")
> private_content <- readLines(privatekey, n = 3L)
> grepl("encrypted", private_content, ignore.case = TRUE)
[1] FALSE FALSE FALSE
> !startsWith(private_content[2L], "MII")
[1] TRUE
@jdblischak jdblischak changed the title ssh_key_needs_passphrase() always returns FALSE ssh_key_needs_passphrase() always returns FALSE Apr 26, 2020
@jdblischak
Copy link
Contributor Author

I investigated more. I believe the issue is the -o flag I passed to ssh-keygen to create a more secure key:

-o      Causes ssh-keygen to save private keys using the new OpenSSH format
        rather than the more compatible PEM format.  The new format has
        increased resistance to brute-force password cracking but is not
        supported by versions of OpenSSH prior to 6.5.  Ed25519 keys always use
        the new private key format.

I created 4 test keys:

  • Default PEM format without a passphrase
  • Default PEM format with a passphrase
  • OpenSSH format without a passphrase
  • OpenSSH format with a passphrase
mkdir /tmp/test-keys/
ssh-keygen -f /tmp/test-keys/pem -N "" -C "PEM no passphrase"
ssh-keygen -f /tmp/test-keys/pem-passphrase -N "passphrase" -C "PEM with passphrase"
ssh-keygen -o -f /tmp/test-keys/openssh -N "" -C "OpenSSH no passphrase"
ssh-keygen -o -f /tmp/test-keys/openssh-passphrase -N "passphrase" -C "OpenSSH with passphrase"

ssh_key_needs_passphrase() expects that a private key with a passphrase contains the string encrypted (case-insensitive). This works for the PEM-formatted private key, but not the OpenSSH-formatted one.

$ grep -i encrypted /tmp/test-keys/*
/tmp/test-keys/pem-passphrase:Proc-Type: 4,ENCRYPTED

Similarly, searching for MII at the beginning of the private key identifies PEM-formatted keys without a passphrase, but not OpenSSH-formatted keys without a passphrase.

$ grep MII /tmp/test-keys/*
/tmp/test-keys/pem:MIIEowIBAAKCAQEAsSFMNWbCum8atameE0bn+druNi6dePqFZEi7ktMa3P2dcSIX

Thus neither of these strategies can distinguish between OpenSSH-formatted keys with and without a passphrase. Requiring "encrypted" leads to false negatives, and requiring lack of "MII" leads to false positives.

In case it could be helpful, here are the first 5 lines of the different types of keys:

$ ls /tmp/test-keys/* | grep -v pub | xargs head -n 5
==> /tmp/test-keys/openssh <==
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAnhxejhNMhjNzLj8cvUSms47erGc1AfHgGzzlSkAxizopxrynAmOh
gwDY3DyTgI986HSb/Z9wjLwhs4mGvJOrBj/E9RBl4P/qjAnIHB+aXiN8wmmx86H8UGAJv6
7JjYMTacXbVmZ9c1+NVNHmLTduAtojvcq7GQz6oOiFBKEC4YS34Zyl33cOMXGaz//EGnyb

==> /tmp/test-keys/openssh-passphrase <==
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABA0QFxJyb
xNY0hfbR32Fi/uAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC82v422UXD
Vq7Yyr7YIw0midpHl8RzTbTAN/zM854CwH86mIWdFv4+MnItmpdDDrcrd6wO9SHWZkO2e7
K7cdgdre0rZxwLq/slTCnoM2VIxoxrTSwisT5kGd1dqjszTtNYfRpcaHeo/oHb55M1kwDP

==> /tmp/test-keys/pem <==
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAsSFMNWbCum8atameE0bn+druNi6dePqFZEi7ktMa3P2dcSIX
86KXQMmDPML24X9MHwNDc/hnLBddPjiwPFikJ4q7QVmS+pskU0Xwje3ip4jm4s39
Iu+X1QoLFLTfW+zQe1hQd/KVnfDYOMPfHUzS2agGIfY9NKmUFsQpU6gpAkV0yxMr
4vEMS+5F9lrlwIzfZ0le26zwY+KPaCXPqEzmECvAlZYrnQ7qJ7+WIvejh0xgEN+P

==> /tmp/test-keys/pem-passphrase <==
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,8A1DFE364837B5DC91D1C66C36B08D0B

irruI3/AaXZDA2f09IaE7MD2lVP8AGaquZxmhvX6vLsi7s/s8SPfNIM8AhAcXokL

@jdblischak jdblischak changed the title ssh_key_needs_passphrase() always returns FALSE ssh_key_needs_passphrase() doesn't work with OpenSSH-formatted keys Apr 29, 2020
@stewid
Copy link
Member

stewid commented Apr 30, 2020

Thanks for investigating this. I think a solution for git2r could be to instead use the credentials package (https://cran.r-project.org/web/packages/credentials/index.html) to help handle the ssh keys.

@jdblischak
Copy link
Contributor Author

@stewid Sounds good! How much work do you anticipate this will be? Will it require breaking changes, or will the new capabilities be able to be added on top of the existing infrastructure? Please let me know how I can help you test and/or implement this.

@stewid
Copy link
Member

stewid commented Apr 30, 2020

Thanks. I don't know how much time will be required to make this refactoring, but hopefully it shouldn't be too much. I would rather we succeed in making this change by adding it on top of the existing infrastructure. I suggest that we keep the credentials argument for backward compatibility. The gert package has the arguments password = askpass and ssh_key = NULL. If we also add these for the new functionality, we will be consistent with that package. What do you think?

I will create a new branch use-credentials-pkg that we can use for development and testing. I will also create a [WIP] PR so that we get a place for discussions.

@davidski
Copy link

I banged my head on this OpenSSH v. PEM encoding issue for quite a while today. For others that come across this problem (OpenSSH format is the standard on much of MacOS installs), an OpenSSH formatted key can be converted to PEM via ssh-keygen -p -m PEM -f /path/to/private_key.

The symptom of having the OpenSSH formatted private key is for operations (a clone in my case) to fail with a rather ambiguous message of Failed to authenticate SSH session: Unable to send userauth-publickey request

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