Skip to content

Port over OpenSSH key support from Java API #439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions crypto/src/crypto/ec/CustomNamedCurves.cs
Original file line number Diff line number Diff line change
@@ -782,14 +782,16 @@ protected override X9ECParameters CreateParameters()
new Dictionary<string, DerObjectIdentifier>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<DerObjectIdentifier, X9ECParametersHolder> curves =
new Dictionary<DerObjectIdentifier, X9ECParametersHolder>();
private static readonly Dictionary<DerObjectIdentifier, string> names =
private static readonly Dictionary<DerObjectIdentifier, string> objIdToName =
new Dictionary<DerObjectIdentifier, string>();
private static readonly List<string> names = new List<string>();

private static void DefineCurve(string name, DerObjectIdentifier oid, X9ECParametersHolder holder)
{
objIds.Add(name, oid);
names.Add(oid, name);
objIdToName.Add(oid, name);
curves.Add(oid, holder);
names.Add(name);
}

private static void DefineCurveAlias(string name, DerObjectIdentifier oid)
@@ -902,7 +904,7 @@ public static X9ECParametersHolder GetByOidLazy(DerObjectIdentifier oid)
/// <param name="oid">The <see cref="DerObjectIdentifier">OID</see> for the curve.</param>
public static string GetName(DerObjectIdentifier oid)
{
return CollectionUtilities.GetValueOrNull(names, oid);
return CollectionUtilities.GetValueOrNull(objIdToName, oid);
}

/// <summary>Look up the <see cref="DerObjectIdentifier">OID</see> of the curve with the given name.</summary>
@@ -915,7 +917,7 @@ public static DerObjectIdentifier GetOid(string name)
/// <summary>Enumerate the available curve names in this registry.</summary>
public static IEnumerable<string> Names
{
get { return CollectionUtilities.Proxy(objIds.Keys); }
get { return CollectionUtilities.Proxy(names); }
}
}
}
10 changes: 5 additions & 5 deletions crypto/src/pkcs/PrivateKeyInfoFactory.cs
Original file line number Diff line number Diff line change
@@ -164,15 +164,15 @@ public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter private
else
{
X962Parameters x962;
if (priv.PublicKeyParamSet == null)
if (dp is ECNamedDomainParameters _dp)
{
X9ECParameters ecP = new X9ECParameters(dp.Curve, new X9ECPoint(dp.G, false), dp.N, dp.H,
dp.GetSeed());
x962 = new X962Parameters(ecP);
x962 = new X962Parameters(_dp.Name);
}
else
{
x962 = new X962Parameters(priv.PublicKeyParamSet);
X9ECParameters ecP = new X9ECParameters(dp.Curve, new X9ECPoint(dp.G, false), dp.N, dp.H,
dp.GetSeed());
x962 = new X962Parameters(ecP);
}

ec = new ECPrivateKeyStructure(orderBitLength, priv.D, publicKey, x962);
314 changes: 314 additions & 0 deletions crypto/src/util/ssh/OpenSSHPrivateKeyUtil.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
178 changes: 178 additions & 0 deletions crypto/src/util/ssh/OpenSSHPublicKeyUtil.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
150 changes: 150 additions & 0 deletions crypto/src/util/ssh/SSHBuffer.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
83 changes: 83 additions & 0 deletions crypto/src/util/ssh/SSHBuilder.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
96 changes: 96 additions & 0 deletions crypto/src/util/ssh/SSHNamedCurves.cs
Original file line number Diff line number Diff line change
@@ -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]];
}
}
}
521 changes: 521 additions & 0 deletions crypto/test/src/crypto/test/OpenSSHKeyParsingTests.cs

Large diffs are not rendered by default.