diff options
Diffstat (limited to 'crypto/src/util/ssh')
-rw-r--r-- | crypto/src/util/ssh/OpenSSHPrivateKeyUtil.cs | 314 | ||||
-rw-r--r-- | crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs | 178 | ||||
-rw-r--r-- | crypto/src/util/ssh/SSHBuffer.cs | 150 | ||||
-rw-r--r-- | crypto/src/util/ssh/SSHBuilder.cs | 83 | ||||
-rw-r--r-- | crypto/src/util/ssh/SSHNamedCurves.cs | 96 |
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]]; + } + } +} |