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

后端 未结 3 702
伪装坚强ぢ
伪装坚强ぢ 2020-12-09 23:37

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 l

相关标签:
3条回答
  • 2020-12-09 23:45

    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/

    0 讨论(0)
  • 2020-12-09 23:55

    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());
    }
    
    0 讨论(0)
  • 2020-12-09 23:56

    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;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题