0 votes
by (280 points)

We are currently trying to establish an SSH connection to an Azure Linux VM using a Microsoft Entra ID user account. The existing approach, as documented by Microsoft, uses an OpenSSH certificate-based authentication mechanism.

The prerequisites and current process are as follows:

  1. Generate an RSA key pair
    Generate the RSA private and public key pair required for SSH authentication.

  2. Authenticate to Microsoft Entra ID
    Sign in to Azure using the Microsoft Entra ID credentials:
    az login --username "EntraIDUsername" --password "EntraIDPassword"

  3. Generate the Microsoft Entra ID–signed SSH certificate
    Use the Azure CLI SSH extension to generate the short-lived SSH certificate:
    az ssh cert --public-key-file "PublicKeyPath" --file "OutputCertificatePath"

  4. Use the private key and Microsoft Entra ID–signed certificate
    The generated private key and certificate are then used to establish an SSH session with the target Azure VM.

  5. Connect using PuTTY
    The generated certificate and corresponding private key can be used with an SSH client such as PuTTY, provided the client supports OpenSSH certificate-based authentication.

We would like to implement the same authentication flow using the Rebex SSH client/library instead of PuTTY/OpenSSH
Specifically, we would like to understand:
Does the current version of Rebex SSH support authentication using an OpenSSH user certificate of type ssh-rsa-cert-v01@openssh.com?
If supported, could you provide an example or recommended implementation for:
Loading the RSA private key.
Loading/associating the Microsoft Entra ID–issued OpenSSH certificate.
Using the certificate and private key to authenticate to the Azure VM.

1 Answer

0 votes
by (152k points)
edited by

Support for OpenSSH user certs such as ssh-rsa-cert-v01@openssh.com is currently experimental and can be performed like this:

using Rebex.Net;
using Rebex.Security.Cryptography;
...

// load proprietary OpenSSH certificate into SshPublicKey
var publicCert = new SshPublicKey("user01-opensshcert.pub");

// load OpenSSH certificate's private key into SshPrivateKey
var privateKey = new SshPrivateKey("user01.pri", password);

// associate the certificate with the private key
CryptoHelper.SetOption(privateKey, "OpenSshCert", publicCert);

// connect to a server using the key with associated cert
var sftp = new Sftp();

// enable experimental OpenSSH certificate support
CryptoHelper.SetOption(sftp.Settings.SshParameters, "EnableOpenSshCerts", true);

// disable experimental OpenSSH server cert support (only enable non-experimental host key algs)
sftp.Settings.SshParameters.SetHostKeyAlgorithms(SshParameters.GetSupportedHostKeyAlgorithms());

// register server key check handler
sftp.FingerprintCheck += MyServerKeyCheck;

// connect to a server
sftp.Connect("server01");

// authenticate using OpenSSH user cert
sftp.Login("user01", privateKey);
...

Additional information, mostly for context:

Historically, Rebex SFTP has supported the standard X.509 certificates, as specified by IETF RFC 6187. These use types x509v3-rsa2048-sha256 for RSA certificates and ecdsa-sha2-* for ECDSA certificates, and uses the same certificates as HTTP and other common protocols.

But strangely, it looks like Microsoft instead chose to use the proprietary OpenSSH certificates for Entra ID. These are not compatible with standard X.509 certificates and not endorsed by IETF. Rebex recommends using standard X.509 certificates whenever possible.

by (280 points)
We tried the solution provided above but getting the error as "Error while decoding key." while creating the object of SSHPublickKey("AzureCertificatePath");
  try
                    {
                        
                            //var publicCert = new Rebex.Net.SshPublicKey("user01-opensshcert.pub");
                            var publicCert = new Rebex.Net.SshPublicKey(AzCertificateFilePath);

                            // load OpenSSH certificate's private key into SshPrivateKey
                            var privateKey = new Rebex.Net.SshPrivateKey(PrivateKeyFile, "");

                            // associate the certificate with the private key
                            Rebex.Security.Cryptography.CryptoHelper.SetOption(privateKey, "OpenSshCert", publicCert);

                            // enable experimental OpenSSH certificate support
                            Rebex.Security.Cryptography.CryptoHelper.SetOption(SshClient.Settings.SshParameters, "EnableOpenSshCerts", true);

                            // register server key check handler
                            SshClient.FingerprintCheck += SshClient_FingerprintCheck; ;

                            // connect to a server
                             SshClient.Connect("server01");

                            // authenticate using OpenSSH user cert
                            SshClient.Login("user01", privateKey);
                        }
                    
                    catch (Exception Ex)
                    {
                       //getting the error here as "Error while decoding key.";
                    }
by (152k points)
How does the AzCertificateFilePath file look like? Does it start with "-----BEGIN" string, or is there just a single line that starts with "ssh-rsa-cert-v01@openssh.com" followed by encoded certificate data? In that case, instead of this:
    var publicCert = new Rebex.Net.SshPublicKey(AzCertificateFilePath);
Try this:
    var publicCert = SshPublicKey.Parse(File.ReadAllText(AzCertificateFilePath));
by (280 points)
File of AzCertificateFilePath starts with "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2" and now when i use above recomended solution [var publicCert = SshPublicKey.Parse(File.ReadAllText(AzCertificateFilePath));], I am getting error as "Unable to parse key."
by (152k points)
That's strange! However, the "Unable to parse key" error is most likely a CryptographicException, and it has an InnerException property set. Can you let us know what it contains? Ideally, try something like this in your catch block and post the result:

    catch (Exception Ex)
    {
        Console.WriteLine(Ex.ToString());
    }
by (280 points)
Below is the response for above catch block
---------------------------
Unable to parse key.
---------------------------
System.Security.Cryptography.CryptographicException: Unable to parse key. ---> System.Security.Cryptography.CryptographicException: Key algorithm is not supported.
   at Rebex.Net.SshPublicKey.ocqrj(Byte[] p0, AsymmetricKeyAlgorithm& p1, Certificate& p2)
   at Rebex.Net.SshPublicKey.scfjs(Byte[] p0, Boolean p1)
   at Rebex.Net.SshPublicKey.Parse(String key)
   --- End of inner exception stack trace ---
   at Rebex.Net.SshPublicKey.Parse(String key)
   at RebexTestPage.btnAzTest_Click(Object sender, EventArgs e) in RebexTestPage.cs:line 1027
by (152k points)
This looks like you might be using an older version of Rebex SFTP.
Please try v7.0.9649 or v8.0.9709.
by (280 points)
Thanks Lukas Pokorny , We updated the version and not able to create the object of sshprivatekey object but getting the error when login to ssh client as "Private key algorithm is not supported."


---------------------------
Private key algorithm is not supported.
---------------------------
System.InvalidOperationException: Private key algorithm is not supported.
at Rebex.Net.SshSession.fwvcl(exbwl`1 p0, Int32 p1)
at Rebex.Net.SshSession.ubfls(String p0, String p1, exbwl`1 p2, SshGssApiCredentials p3, Boolean p4)
at Rebex.Net.SshSession.feixm(String p0, String p1, exbwl`1 p2)
at Rebex.Net.Ssh.dxzde.ouqih(String p0, String p1, exbwl`1 p2, tkxjg p3)
at Rebex.Net.Ssh.mpxnc(String p0, String p1, exbwl`1 p2, tkxjg p3)
at Rebex.Net.Ssh.Login(String userName, String password, SshPrivateKey privateKey)
at RebexTestPage.btnAzTest_Click(Object sender, EventArgs e) in RebexTestPage.cs:line 1038
by (152k points)
Please try the sample app from https://www.rebex.net/getfile/d8640a4571ad4e3ba910f8ac8265db4d/SftpOpenSshUserCert.zip

Change the server name and the key files, and give it a try.
...