summary refs log tree commit diff
path: root/crypto/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/util')
-rw-r--r--crypto/src/util/ssh/OpenSSHPrivateKeyUtil.cs314
-rw-r--r--crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs178
-rw-r--r--crypto/src/util/ssh/SSHBuffer.cs150
-rw-r--r--crypto/src/util/ssh/SSHBuilder.cs83
-rw-r--r--crypto/src/util/ssh/SSHNamedCurves.cs96
5 files changed, 821 insertions, 0 deletions
diff --git a/crypto/src/util/ssh/OpenSSHPrivateKeyUtil.cs b/crypto/src/util/ssh/OpenSSHPrivateKeyUtil.cs
new file mode 100644
index 000000000..a918d3483
--- /dev/null
+++ b/crypto/src/util/ssh/OpenSSHPrivateKeyUtil.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pkcs;
+
+namespace Org.BouncyCastle.Utilities.SSH
+{
+    public class OpenSSHPrivateKeyUtil
+    {
+        private OpenSSHPrivateKeyUtil()
+        {
+
+        }
+
+        /**
+         * Magic value for proprietary OpenSSH private key.
+         **/
+        static readonly byte[] AUTH_MAGIC = Strings.ToByteArray("openssh-key-v1\0"); // C string so null terminated
+
+        /**
+         * Encode a cipher parameters into an OpenSSH private key.
+         * This does not add headers like ----BEGIN RSA PRIVATE KEY----
+         *
+         * @param parameters the cipher parameters.
+         * @return a byte array
+         */
+        public static byte[] EncodePrivateKey(AsymmetricKeyParameter parameters)
+        {
+            if (parameters == null)
+            {
+                throw new ArgumentException("parameters is null");
+            }
+
+            if (parameters is RsaPrivateCrtKeyParameters || parameters is ECPrivateKeyParameters)
+            {
+                PrivateKeyInfo pInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(parameters);
+                return pInfo.ParsePrivateKey().GetEncoded();
+            }
+            else if (parameters is DsaPrivateKeyParameters dsaPrivateKey)
+            {
+                DsaParameters dsaparameters = dsaPrivateKey.Parameters;
+
+                Asn1EncodableVector vec = new Asn1EncodableVector
+                {
+                    new DerInteger(0),
+                    new DerInteger(dsaparameters.P),
+                    new DerInteger(dsaparameters.Q),
+                    new DerInteger(dsaparameters.G)
+                };
+
+                // public key = g.modPow(x, p);
+                BigInteger pubKey = dsaparameters.P.ModPow(dsaPrivateKey.X, dsaparameters.P);
+                vec.Add(new DerInteger(pubKey));
+                vec.Add(new DerInteger(dsaPrivateKey.X));
+                try
+                {
+                    return new DerSequence(vec).GetEncoded();
+                }
+                catch (Exception ex)
+                {
+                    throw new InvalidOperationException("unable to encode DSAPrivateKeyParameters " + ex.Message);
+                }
+            }
+            else if (parameters is Ed25519PrivateKeyParameters ed25519PrivateKey)
+            {
+                Ed25519PublicKeyParameters publicKeyParameters = ed25519PrivateKey.GeneratePublicKey();
+
+                SSHBuilder builder = new SSHBuilder();
+                builder.WriteBytes(AUTH_MAGIC);
+                builder.WriteString("none");    // cipher name
+                builder.WriteString("none");    // KDF name
+                builder.WriteString("");        // KDF options
+
+                builder.U32(1); // Number of keys
+
+                {
+                    byte[] pkEncoded = OpenSSHPublicKeyUtil.EncodePublicKey(publicKeyParameters);
+                    builder.WriteBlock(pkEncoded);
+                }
+
+                {
+                    SSHBuilder pkBuild = new SSHBuilder();
+
+                    int checkint = CryptoServicesRegistrar.GetSecureRandom().NextInt();
+                    pkBuild.U32((uint)checkint);
+                    pkBuild.U32((uint)checkint);
+
+                    pkBuild.WriteString("ssh-ed25519");
+
+                    // Public key (as part of private key pair)
+                    byte[] pubKeyEncoded = publicKeyParameters.GetEncoded();
+                    pkBuild.WriteBlock(pubKeyEncoded);
+
+                    // The private key in SSH is 64 bytes long and is the concatenation of the private and the public keys
+                    pkBuild.WriteBlock(Arrays.Concatenate(ed25519PrivateKey.GetEncoded(), pubKeyEncoded));
+
+                    // Comment for this private key (empty)
+                    pkBuild.WriteString("");    
+
+                    builder.WriteBlock(pkBuild.GetPaddedBytes());
+                }
+
+                return builder.GetBytes();
+            }
+
+            throw new ArgumentException("unable to convert " + parameters.GetType().Name + " to openssh private key");
+
+        }
+
+        /**
+         * Parse a private key.
+         * <p>
+         * This method accepts the body of the OpenSSH private key.
+         * The easiest way to extract the body is to use PemReader, for example:
+         * <p>
+         * byte[] blob = new PemReader([reader]).readPemObject().getContent();
+         * CipherParameters params = parsePrivateKeyBlob(blob);
+         *
+         * @param blob The key.
+         * @return A cipher parameters instance.
+         */
+        public static AsymmetricKeyParameter ParsePrivateKeyBlob(byte[] blob)
+        {
+            AsymmetricKeyParameter result = null;
+
+            if (blob[0] == 0x30)
+            {
+                Asn1Sequence sequence = Asn1Sequence.GetInstance(blob);
+
+                if (sequence.Count == 6)
+                {
+                    if (AllIntegers(sequence) && ((DerInteger)sequence[0]).PositiveValue.Equals(BigIntegers.Zero))
+                    {
+                        // length of 6 and all Integers -- DSA
+                        result = new DsaPrivateKeyParameters(
+                            ((DerInteger)sequence[5]).PositiveValue,
+                            new DsaParameters(
+                                ((DerInteger)sequence[1]).PositiveValue,
+                                ((DerInteger)sequence[2]).PositiveValue,
+                                ((DerInteger)sequence[3]).PositiveValue)
+                        );
+                    }
+                }
+                else if (sequence.Count == 9)
+                {
+                    if (AllIntegers(sequence) && ((DerInteger)sequence[0]).PositiveValue.Equals(BigIntegers.Zero))
+                    {
+                        // length of 8 and all Integers -- RSA
+                        RsaPrivateKeyStructure rsaPrivateKey = RsaPrivateKeyStructure.GetInstance(sequence);
+
+                        result = new RsaPrivateCrtKeyParameters(
+                            rsaPrivateKey.Modulus,
+                            rsaPrivateKey.PublicExponent,
+                            rsaPrivateKey.PrivateExponent,
+                            rsaPrivateKey.Prime1,
+                            rsaPrivateKey.Prime2,
+                            rsaPrivateKey.Exponent1,
+                            rsaPrivateKey.Exponent2,
+                            rsaPrivateKey.Coefficient);
+                    }
+                }
+                else if (sequence.Count == 4)
+                {
+                    if (sequence[3] is Asn1TaggedObject && sequence[2] is Asn1TaggedObject)
+                    {
+                        ECPrivateKeyStructure ecPrivateKey = ECPrivateKeyStructure.GetInstance(sequence);
+                        DerObjectIdentifier curveOID = DerObjectIdentifier.GetInstance(ecPrivateKey.GetParameters());
+                        X9ECParameters x9Params = ECNamedCurveTable.GetByOid(curveOID);
+                        result = new ECPrivateKeyParameters(
+                            ecPrivateKey.GetKey(),
+                            new ECNamedDomainParameters(
+                                curveOID,
+                                x9Params));
+                    }
+                }
+            }
+            else
+            {
+                SSHBuffer kIn = new SSHBuffer(AUTH_MAGIC, blob);
+
+                String cipherName = kIn.ReadString();
+                if (!"none".Equals(cipherName))
+                {
+                    throw new InvalidOperationException("encrypted keys not supported");
+                }
+
+                // KDF name
+                kIn.SkipBlock();
+
+                // KDF options
+                kIn.SkipBlock();
+
+                int publicKeyCount = kIn.ReadU32();
+                if (publicKeyCount != 1)
+                {
+                    throw new InvalidOperationException("multiple keys not supported");
+                }
+
+                // Burn off public key.
+                OpenSSHPublicKeyUtil.ParsePublicKey(kIn.ReadBlock());
+
+                byte[] privateKeyBlock = kIn.ReadPaddedBlock();
+
+                if (kIn.HasRemaining())
+                {
+                    throw new InvalidOperationException("decoded key has trailing data");
+                }
+
+                SSHBuffer pkIn = new SSHBuffer(privateKeyBlock);
+                int check1 = pkIn.ReadU32();
+                int check2 = pkIn.ReadU32();
+
+                if (check1 != check2)
+                {
+                    throw new InvalidOperationException("private key check values are not the same");
+                }
+
+                String keyType = pkIn.ReadString();
+
+                if ("ssh-ed25519".Equals(keyType))
+                {
+                    // Public key
+                    pkIn.ReadBlock();
+                    // Private key value..
+                    byte[] edPrivateKey = pkIn.ReadBlock();
+                    if (edPrivateKey.Length != Ed25519PrivateKeyParameters.KeySize + Ed25519PublicKeyParameters.KeySize)
+                    {
+                        throw new InvalidOperationException("private key value of wrong length");
+                    }
+
+                    result = new Ed25519PrivateKeyParameters(edPrivateKey, 0);
+                }
+                else if (keyType.StartsWith("ecdsa"))
+                {
+                    DerObjectIdentifier oid = SSHNamedCurves.GetByName(Strings.FromByteArray(pkIn.ReadBlock())) ?? 
+                        throw new InvalidOperationException("OID not found for: " + keyType);
+                    X9ECParameters curveParams = NistNamedCurves.GetByOid(oid) ?? throw new InvalidOperationException("Curve not found for: " + oid);
+
+                    // Skip public key.
+                    pkIn.ReadBlock();
+                    byte[] privKey = pkIn.ReadBlock();
+
+                    result = new ECPrivateKeyParameters(new BigInteger(1, privKey),
+                        new ECNamedDomainParameters(oid, curveParams));
+                }
+                else if (keyType.StartsWith("ssh-rsa"))
+                {
+                    BigInteger modulus = new BigInteger(1, pkIn.ReadBlock());
+                    BigInteger pubExp = new BigInteger(1, pkIn.ReadBlock());
+                    BigInteger privExp = new BigInteger(1, pkIn.ReadBlock());
+                    BigInteger coef = new BigInteger(1, pkIn.ReadBlock());
+                    BigInteger p = new BigInteger(1, pkIn.ReadBlock());
+                    BigInteger q = new BigInteger(1, pkIn.ReadBlock());
+
+                    BigInteger pSub1 = p.Subtract(BigIntegers.One);
+                    BigInteger qSub1 = q.Subtract(BigIntegers.One);
+                    BigInteger dP = privExp.Remainder(pSub1);
+                    BigInteger dQ = privExp.Remainder(qSub1);
+
+                    result = new RsaPrivateCrtKeyParameters(
+                                    modulus,
+                                    pubExp,
+                                    privExp,
+                                    p,
+                                    q,
+                                    dP,
+                                    dQ,
+                                    coef);
+                }
+
+                // Comment for private key
+                pkIn.SkipBlock();
+
+                if (pkIn.HasRemaining())
+                {
+                    throw new ArgumentException("private key block has trailing data");
+                }
+            }
+
+            if (result == null)
+            {
+                throw new ArgumentException("unable to parse key");
+            }
+
+            return result;
+        }
+
+        /**
+         * allIntegers returns true if the sequence holds only DerInteger types.
+         **/
+        private static Boolean AllIntegers(Asn1Sequence sequence)
+        {
+            for (int t = 0; t < sequence.Count; t++)
+            {
+                if (!(sequence[t] is DerInteger))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+}
diff --git a/crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs b/crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs
new file mode 100644
index 000000000..8f1fa8ec1
--- /dev/null
+++ b/crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs
@@ -0,0 +1,178 @@
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Utilities.SSH
+{
+    public class OpenSSHPublicKeyUtil
+    {
+        private OpenSSHPublicKeyUtil()
+        {
+
+        }
+
+        private static readonly String RSA = "ssh-rsa";
+        private static readonly String ECDSA = "ecdsa";
+        private static readonly String ED_25519 = "ssh-ed25519";
+        private static readonly String DSS = "ssh-dss";
+
+        /**
+         * Parse a public key.
+         * <p>
+         * This method accepts the bytes that are Base64 encoded in an OpenSSH public key file.
+         *
+         * @param encoded The key.
+         * @return An AsymmetricKeyParameter instance.
+         */
+        public static AsymmetricKeyParameter ParsePublicKey(byte[] encoded)
+        {
+            SSHBuffer buffer = new SSHBuffer(encoded);
+            return ParsePublicKey(buffer);
+        }
+
+        /**
+         * Encode a public key from an AsymmetricKeyParameter instance.
+         *
+         * @param cipherParameters The key to encode.
+         * @return the key OpenSSH encoded.
+         * @throws IOException
+         */
+        public static byte[] EncodePublicKey(AsymmetricKeyParameter cipherParameters)
+        {
+            if (cipherParameters == null)
+            {
+                throw new ArgumentException("cipherParameters was null.");
+            }
+
+            if (cipherParameters is RsaKeyParameters)
+            {
+                if (cipherParameters.IsPrivate)
+                {
+                    throw new ArgumentException("RSAKeyParamaters was for encryption");
+                }
+
+                RsaKeyParameters rsaPubKey = (RsaKeyParameters)cipherParameters;
+
+                SSHBuilder builder = new SSHBuilder();
+                builder.WriteString(RSA);
+                builder.WriteBigNum(rsaPubKey.Exponent);
+                builder.WriteBigNum(rsaPubKey.Modulus);
+
+                return builder.GetBytes();
+
+            }
+            else if (cipherParameters is ECPublicKeyParameters ecPublicKey)
+            {
+                SSHBuilder builder = new SSHBuilder();
+
+                //
+                // checked for named curve parameters..
+                //
+                String name = SSHNamedCurves.GetNameForParameters(ecPublicKey.Parameters);
+
+                if (name == null)
+                {
+                    throw new ArgumentException("unable to derive ssh curve name for " + ecPublicKey.Parameters.Curve.GetType().Name);
+                }
+
+                builder.WriteString(ECDSA + "-sha2-" + name); // Magic
+                builder.WriteString(name);
+                builder.WriteBlock(ecPublicKey.Q.GetEncoded(false)); //Uncompressed
+                return builder.GetBytes();
+            }
+            else if (cipherParameters is DsaPublicKeyParameters dsaPubKey)
+            {
+                DsaParameters dsaParams = dsaPubKey.Parameters;
+
+                SSHBuilder builder = new SSHBuilder();
+                builder.WriteString(DSS);
+                builder.WriteBigNum(dsaParams.P);
+                builder.WriteBigNum(dsaParams.Q);
+                builder.WriteBigNum(dsaParams.G);
+                builder.WriteBigNum(dsaPubKey.Y);
+                return builder.GetBytes();
+            }
+            else if (cipherParameters is Ed25519PublicKeyParameters ed25519PublicKey)
+            {
+                SSHBuilder builder = new SSHBuilder();
+                builder.WriteString(ED_25519);
+                builder.WriteBlock(ed25519PublicKey.GetEncoded());
+                return builder.GetBytes();
+            }
+
+            throw new ArgumentException("unable to convert " + cipherParameters.GetType().Name + " to private key");
+        }
+
+        /**
+         * Parse a public key from an SSHBuffer instance.
+         *
+         * @param buffer containing the SSH public key.
+         * @return A CipherParameters instance.
+         */
+        public static AsymmetricKeyParameter ParsePublicKey(SSHBuffer buffer)
+        {
+            AsymmetricKeyParameter result = null;
+
+            string magic = buffer.ReadString();
+            if (RSA.Equals(magic))
+            {
+                BigInteger e = buffer.ReadBigNumPositive();
+                BigInteger n = buffer.ReadBigNumPositive();
+                result = new RsaKeyParameters(false, n, e);
+            }
+            else if (DSS.Equals(magic))
+            {
+                BigInteger p = buffer.ReadBigNumPositive();
+                BigInteger q = buffer.ReadBigNumPositive();
+                BigInteger g = buffer.ReadBigNumPositive();
+                BigInteger pubKey = buffer.ReadBigNumPositive();
+
+                result = new DsaPublicKeyParameters(pubKey, new DsaParameters(p, q, g));
+            }
+            else if (magic.StartsWith(ECDSA))
+            {
+                String curveName = buffer.ReadString();
+                DerObjectIdentifier oid = SSHNamedCurves.GetByName(curveName);
+                X9ECParameters x9ECParameters = SSHNamedCurves.GetParameters(oid) ?? 
+                    throw new InvalidOperationException("unable to find curve for " + magic + " using curve name " + curveName);
+                var curve = x9ECParameters.Curve;
+                byte[] pointRaw = buffer.ReadBlock();
+
+                result = new ECPublicKeyParameters(
+                    curve.DecodePoint(pointRaw),
+                    new ECNamedDomainParameters(oid, x9ECParameters));
+            }
+            else if (ED_25519.Equals(magic))
+            {
+                byte[] pubKeyBytes = buffer.ReadBlock();
+                if (pubKeyBytes.Length != Ed25519PublicKeyParameters.KeySize)
+                {
+                    throw new InvalidOperationException("public key value of wrong length");
+                }
+
+                result = new Ed25519PublicKeyParameters(pubKeyBytes, 0);
+            }
+
+            if (result == null)
+            {
+                throw new ArgumentException("unable to parse key");
+            }
+
+            if (buffer.HasRemaining())
+            {
+                throw new ArgumentException("decoded key has trailing data");
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/crypto/src/util/ssh/SSHBuffer.cs b/crypto/src/util/ssh/SSHBuffer.cs
new file mode 100644
index 000000000..8d3c3f977
--- /dev/null
+++ b/crypto/src/util/ssh/SSHBuffer.cs
@@ -0,0 +1,150 @@
+using System;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Utilities.SSH
+{
+    public class SSHBuffer
+    {
+        private readonly byte[] buffer;
+        private int pos = 0;
+
+        public SSHBuffer(byte[] magic, byte[] buffer)
+        {
+            this.buffer = buffer;
+            for (int i = 0; i != magic.Length; i++)
+            {
+                if (magic[i] != buffer[i])
+                {
+                    throw new ArgumentException("magic-number incorrect");
+                }
+            }
+
+            pos += magic.Length;
+        }
+
+        public SSHBuffer(byte[] buffer)
+        {
+            this.buffer = buffer;
+        }
+
+        public int ReadU32()
+        {
+            if (pos > (buffer.Length - 4))
+            {
+                throw new ArgumentOutOfRangeException("4 bytes for U32 exceeds buffer.");
+            }
+
+            int i = (buffer[pos++] & 0xFF) << 24;
+            i |= (buffer[pos++] & 0xFF) << 16;
+            i |= (buffer[pos++] & 0xFF) << 8;
+            i |= (buffer[pos++] & 0xFF);
+
+            return i;
+        }
+
+        public String ReadString()
+        {
+            return Strings.FromByteArray(ReadBlock());
+        }
+
+        public byte[] ReadBlock()
+        {
+            int len = ReadU32();
+            if (len == 0)
+            {
+                return new byte[0];
+            }
+
+            if (pos > (buffer.Length - len))
+            {
+                throw new ArgumentException("not enough data for block");
+            }
+
+            int start = pos; pos += len;
+            return Arrays.CopyOfRange(buffer, start, pos);
+        }
+
+        public void SkipBlock()
+        {
+            int len = ReadU32();
+            if (pos > (buffer.Length - len))
+            {
+                throw new ArgumentException("not enough data for block");
+            }
+
+            pos += len;
+        }
+
+        public byte[] ReadPaddedBlock()
+        {
+            return ReadPaddedBlock(8);
+        }
+
+        public byte[] ReadPaddedBlock(int blockSize)
+        {
+            int len = ReadU32();
+            if (len == 0)
+            {
+                return new byte[0];
+            }
+
+            if (pos > (buffer.Length - len))
+            {
+                throw new ArgumentException("not enough data for block");
+            }
+
+            int align = len % blockSize;
+            if (0 != align)
+            {
+                throw new ArgumentException("missing padding");
+            }
+
+            int start = pos; pos += len;
+            int end = pos;
+
+            if (len > 0)
+            {
+                // TODO If encryption is supported, should be constant-time
+                int lastByte = buffer[pos - 1] & 0xFF;
+                if (0 < lastByte && lastByte < blockSize)
+                {
+                    int padCount = lastByte;
+                    end -= padCount;
+
+                    for (int i = 1, padPos = end; i <= padCount; ++i, ++padPos)
+                    {
+                        if (i != (buffer[padPos] & 0xFF))
+                        {
+                            throw new ArgumentException("incorrect padding");
+                        }
+                    }
+                }
+            }
+
+            return Arrays.CopyOfRange(buffer, start, end);
+        }
+
+        public BigInteger ReadBigNumPositive()
+        {
+            int len = ReadU32();
+            if (pos + len > buffer.Length)
+            {
+                throw new ArgumentException("not enough data for big num");
+            }
+
+            int start = pos; pos += len;
+            byte[] d = Arrays.CopyOfRange(buffer, start, pos);
+            return new BigInteger(1, d);
+        }
+
+        public byte[] GetBuffer()
+        {
+            return Arrays.Clone(buffer);
+        }
+
+        public Boolean HasRemaining()
+        {
+            return pos < buffer.Length;
+        }
+    }
+}
diff --git a/crypto/src/util/ssh/SSHBuilder.cs b/crypto/src/util/ssh/SSHBuilder.cs
new file mode 100644
index 000000000..5fa92de4b
--- /dev/null
+++ b/crypto/src/util/ssh/SSHBuilder.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Utilities.SSH
+{
+    public class SSHBuilder
+    {
+        private readonly MemoryStream bos = new MemoryStream();
+
+        [CLSCompliant(false)]
+        public void U32(uint value)
+        {
+            bos.WriteByte(Convert.ToByte((value >> 24) & 0xFF));
+            bos.WriteByte(Convert.ToByte((value >> 16) & 0xFF));
+            bos.WriteByte(Convert.ToByte((value >> 8) & 0xFF));
+            bos.WriteByte(Convert.ToByte(value & 0xFF));
+        }
+
+        public void WriteBigNum(BigInteger n)
+        {
+            WriteBlock(n.ToByteArray());
+        }
+
+        public void WriteBlock(byte[] value)
+        {
+            U32((uint)value.Length);
+            try
+            {
+                bos.Write(value, 0, value.Length);
+            }
+            catch (IOException e)
+            {
+                throw new InvalidOperationException(e.Message, e);
+            }
+        }
+
+        public void WriteBytes(byte[] value)
+        {
+            try
+            {
+                bos.Write(value, 0, value.Length);
+            }
+            catch (IOException e)
+            {
+                throw new InvalidOperationException(e.Message, e);
+            }
+        }
+
+        public void WriteString(String str)
+        {
+            WriteBlock(Strings.ToByteArray(str));
+        }
+
+        public byte[] GetBytes()
+        {
+            return bos.ToArray();
+        }
+
+        public byte[] GetPaddedBytes()
+        {
+            return GetPaddedBytes(8);
+        }
+
+        public byte[] GetPaddedBytes(int blockSize)
+        {
+            int align = (int)bos.Length % blockSize;
+            if (0 != align)
+            {
+                int padCount = blockSize - align;
+                for (int i = 1; i <= padCount; ++i)
+                {
+                    bos.WriteByte(Convert.ToByte(i));
+                }
+            }
+            return bos.ToArray();
+        }
+    }
+}
diff --git a/crypto/src/util/ssh/SSHNamedCurves.cs b/crypto/src/util/ssh/SSHNamedCurves.cs
new file mode 100644
index 000000000..31c350128
--- /dev/null
+++ b/crypto/src/util/ssh/SSHNamedCurves.cs
@@ -0,0 +1,96 @@
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.EC;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Utilities.SSH
+{
+    public class SSHNamedCurves
+    {
+        private static readonly Dictionary<string, DerObjectIdentifier> OidMap =
+            new Dictionary<string, DerObjectIdentifier>
+            {
+                { "nistp256", SecObjectIdentifiers.SecP256r1 },
+                { "nistp384", SecObjectIdentifiers.SecP384r1 },
+                { "nistp521", SecObjectIdentifiers.SecP521r1 },
+                { "nistk163", SecObjectIdentifiers.SecT163k1 },
+                { "nistp192", SecObjectIdentifiers.SecP192r1 },
+                { "nistp224", SecObjectIdentifiers.SecP224r1 },
+                { "nistk233", SecObjectIdentifiers.SecT233k1 },
+                { "nistb233", SecObjectIdentifiers.SecT233r1 },
+                { "nistk283", SecObjectIdentifiers.SecT283k1 },
+                { "nistk409", SecObjectIdentifiers.SecT409k1 },
+                { "nistb409", SecObjectIdentifiers.SecT409r1 },
+                { "nistt571", SecObjectIdentifiers.SecT571k1 }
+            };
+
+
+        private static readonly Dictionary<string, string> CurveNameToSSHName = 
+            new Dictionary<string, string>
+            {
+                {"secp256r1", "nistp256"},
+                {"secp384r1", "nistp384"},
+                {"secp521r1", "nistp521"},
+                {"sect163k1", "nistk163"},
+                {"secp192r1", "nistp192"},
+                {"secp224r1", "nistp224"},
+                {"sect233k1", "nistk233"},
+                {"sect233r1", "nistb233"},
+                {"sect283k1", "nistk283"},
+                {"sect409k1", "nistk409"},
+                {"sect409r1", "nistb409"},
+                {"sect571k1", "nistt571"}
+            };
+
+        private static readonly Dictionary<ECCurve, string> CurveMap = 
+            CustomNamedCurves.Names.ToDictionary(k => CustomNamedCurves.GetByNameLazy(k).Curve, v => v);
+
+        private static readonly Dictionary<DerObjectIdentifier, string> OidToName =
+            OidMap.ToDictionary(k => k.Value, v => v.Key);
+
+
+        public static DerObjectIdentifier GetByName(string sshName)
+        {
+            return OidMap[sshName];
+        }
+
+        public static X9ECParameters GetParameters(string sshName)
+        {
+            return NistNamedCurves.GetByOid(OidMap[sshName.ToLower()]);
+        }
+
+        public static X9ECParameters GetParameters(DerObjectIdentifier oid)
+        {
+            return NistNamedCurves.GetByOid(oid);
+        }
+
+        public static string GetName(DerObjectIdentifier oid)
+        {
+            return OidToName[oid];
+        }
+
+        public static string GetNameForParameters(ECDomainParameters parameters)
+        {
+            if (parameters is ECNamedDomainParameters)
+            {
+                return GetName(((ECNamedDomainParameters)parameters).Name);
+            }
+
+            return GetNameForParameters(parameters.Curve);
+        }
+
+        public static string GetNameForParameters(ECCurve curve)
+        {
+            return CurveNameToSSHName[CurveMap[curve]];
+        }
+    }
+}