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
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/
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());
}
I found no ready-to-use function for this in BouncyCastle. So, the workaround is to use PemReader
and 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;
}
}
}