Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting an RSA Public Key into a RFC 4716 Public Key with Bouncy Castle

I am looking to convert an RSA public key into something that I could use as an SSH public key.

Currently I have Bouncy Castle producing me a public key that looks like this:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq1Y5300i8bN+cI2U3wJE
Kh3xG/.........jbuz+WB0vvG
P25UwCle2k5siVMwbImEYsr+Xt0dsMmGVB3/6MHAqrM3QQdQ8p2E5TyzL+JYa1FT
gwIDAQAB
-----END PUBLIC KEY-----

I want it to have an RFC 4716 format similar to this:

ssh-rsa AAAAB3NzaC1yc2.......G1p2Ag3mZLFsks7RNHVLgMsGIAikQ==

My Code so far using Bouncy Castle looks like this:

var r = new Org.BouncyCastle.Crypto.Generators.RsaKeyPairGenerator();
r.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
var keys = r.GenerateKeyPair();

var stringWriter = new StringWriter();

var pemWriter = new PemWriter(stringWriter);
pemWriter.WriteObject(keys.Private);
pemWriter.Writer.Flush();
stringWriter.Close();
PrivateKey = stringWriter.ToString();

stringWriter = new StringWriter();
pemWriter = new PemWriter(stringWriter);
pemWriter.WriteObject(keys.Public);
pemWriter.Writer.Flush();
stringWriter.Close();
PublicKey = stringWriter.ToString();

How do I reformat and encode the key to make it look like this?

Has anyone created SSH public keys with Bouncy Castle or similar?

like image 387
Doug Avatar asked Mar 17 '13 04:03

Doug


2 Answers

I found no ready-to-use function for this in BouncyCastle. So, the workaround is to use PemReaderand then format the result. The result will be available as PublicSSH property:

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Text;

namespace Deploy4Me.Common.Utils
{
    public class RSAKey
    {
        public string PublicPEM { get; set; }
        public string PrivatePEM { get; set; }
        public string PublicSSH { get; set; }
    }

    public static class RSA
    {
        public static RSAKey Generate()
        {
            try
            {
                RSAKey result = new RSAKey();
                IAsymmetricCipherKeyPairGenerator gen;
                KeyGenerationParameters param;
                gen = new RsaKeyPairGenerator();
                param = new RsaKeyGenerationParameters(
                    BigInteger.ValueOf(3L),
                    new SecureRandom(),
                    2048,
                    80
                );
                gen.Init(param);
                AsymmetricCipherKeyPair pair = gen.GenerateKeyPair(); 
                using(TextWriter textWriter = new StringWriter())
                {
                    PemWriter wr = new PemWriter(textWriter);
                    wr.WriteObject(pair.Private);
                    wr.Writer.Flush();

                    result.PrivatePEM = textWriter.ToString();
                }

                using (TextWriter textWriter = new StringWriter())
                {
                    PemWriter wr = new PemWriter(textWriter);
                    wr.WriteObject(pair.Public);
                    wr.Writer.Flush();

                    result.PublicPEM = textWriter.ToString();
                }

                using (StringReader sr = new StringReader(result.PublicPEM))
                {
                    PemReader reader = new PemReader(sr);
                    RsaKeyParameters r = (RsaKeyParameters)reader.ReadObject();
                    byte[] sshrsa_bytes = Encoding.Default.GetBytes("ssh-rsa");
                    byte[] n = r.Modulus.ToByteArray();
                    byte[] e = r.Exponent.ToByteArray();

                    string buffer64;
                    using(MemoryStream ms = new MemoryStream()){
                        ms.Write(ToBytes(sshrsa_bytes.Length), 0, 4);
                        ms.Write(sshrsa_bytes, 0, sshrsa_bytes.Length);
                        ms.Write(ToBytes(e.Length), 0, 4);
                        ms.Write(e, 0, e.Length);
                        ms.Write(ToBytes(n.Length), 0, 4);
                        ms.Write(n, 0, n.Length);
                        ms.Flush();
                        buffer64 = Convert.ToBase64String(ms.ToArray());
                    }

                    result.PublicSSH = string.Format("ssh-rsa {0} generated-key", buffer64);
                }

                return result;
            }
            catch (Org.BouncyCastle.Crypto.CryptoException ex)
            {
                throw ex;
            }
        }

        private static byte[] ToBytes(int i)
        {
            byte[] bts = BitConverter.GetBytes(i);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bts);
            }
            return bts;
        }
    }
}
like image 132
Pavel Korsukov Avatar answered Sep 21 '22 13:09

Pavel Korsukov


Note: I work at Microsoft but this is not a Microsoft Answer, just Personal.

Adding to Pavels Answer,

I've found that for some reason when generating a 3072 Bit RSA key, PuttyGen would generate a different RSA public key than mine.

After researching, I found that it seems in the source code of the Putty Gen Program it would do .Length + 1 to the Byte array, adding a leading 0.

For the BouncyCastle, you would change this line.

                    ms.Write(ToBytes(n.Length), 0, 4);
                    ms.Write(n, 0, n.Length);

to

                    ms.Write(ToBytes(n.Length+1), 0, 4);//Add +1 to Emulate PuttyGen
                    ms.Write(new byte[] { 0 }, 0, 1); //Add a 0 to Emulate PuttyGen
                    ms.Write(n, 0, n.Length);

For Microsoft .net RSACryptoServiceProvider it would look like this

            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(3072);
            byte[] sshrsa_bytes = Encoding.Default.GetBytes("ssh-rsa");
            byte[] n = RSA.ExportParameters(false).Modulus;
            byte[] e = RSA.ExportParameters(false).Exponent;
            string buffer64;
            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write(ToBytes(sshrsa_bytes.Length), 0, 4);
                ms.Write(sshrsa_bytes, 0, sshrsa_bytes.Length);
                ms.Write(ToBytes(e.Length), 0, 4);
                ms.Write(e, 0, e.Length);
                ms.Write(ToBytes(n.Length+1), 0, 4); //Remove the +1 if not Emulating Putty Gen
                ms.Write(new byte[] { 0 }, 0, 1); //Add a 0 to Emulate PuttyGen
                ms.Write(n, 0, n.Length);
                ms.Flush();
                buffer64 = Convert.ToBase64String(ms.ToArray());
            }

            string pubssh = string.Format("ssh-rsa {0} generated-key", buffer64);

You can see my Private Key I used for testing & the putty gen source code link https://www.cameronmoten.com/2017/12/21/rsacryptoserviceprovider-create-a-ssh-rsa-public-key/

like image 38
Cam Moten Avatar answered Sep 18 '22 13:09

Cam Moten