Converting an RSA Public Key into a RFC 4716 Publi

2019-01-18 07:36发布

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?

3条回答
爷、活的狠高调
2楼-- · 2019-01-18 07:58

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/

查看更多
走好不送
3楼-- · 2019-01-18 08:00

Realise your post is a few months old now but if you're still looking try the code snippet below, inspired by gotoalberto on Using public key from authorized_keys with Java security ...

public static String getPublicOpenSSHKey(String pem, String userComment)
   throws IOException, EWAException
{
    // Read the PEM supplied using Bouncy Castle's PEMReader ...
    PEMReader r = new PEMReader(new StringReader(pem));
    try { keyPair = (KeyPair) r.readObject(); }
    catch (IOException ioe) { ioe.printStackTrace(); }
    finally { try { r.close(); } catch (Throwable ignore) { } }

    PublicKey publicKey = keyPair.getPublic();

    if (publicKey.getAlgorithm().equals("RSA"))
    {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(byteOs);
        dos.writeInt("ssh-rsa".getBytes().length);
        dos.write("ssh-rsa".getBytes());
        dos.writeInt(rsaPublicKey.getPublicExponent().toByteArray().length);
        dos.write(rsaPublicKey.getPublicExponent().toByteArray());
        dos.writeInt(rsaPublicKey.getModulus().toByteArray().length);
        dos.write(rsaPublicKey.getModulus().toByteArray());
        String enc = Base64.encode(byteOs.toByteArray());
        return("ssh-rsa " + enc + " " + userComment);
     }
     else
        throw new IllegalArgumentException("Unknown public key encoding: " + publicKey.getAlgorithm());
}
查看更多
▲ chillily
4楼-- · 2019-01-18 08:12

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;
        }
    }
}
查看更多
登录 后发表回答