diff --git a/crypto/src/crypto/ec/CustomNamedCurves.cs b/crypto/src/crypto/ec/CustomNamedCurves.cs
index d256dba73..2f6fdfeaa 100644
--- a/crypto/src/crypto/ec/CustomNamedCurves.cs
+++ b/crypto/src/crypto/ec/CustomNamedCurves.cs
@@ -782,14 +782,16 @@ namespace Org.BouncyCastle.Crypto.EC
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 @@ namespace Org.BouncyCastle.Crypto.EC
/// <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>
@@ -912,10 +914,10 @@ namespace Org.BouncyCastle.Crypto.EC
return CollectionUtilities.GetValueOrNull(objIds, name);
}
- /// <summary>Enumerate the available curve names in this registry.</summary>
+ /// <summary>Enumerate the available curve objIdToName in this registry.</summary>
public static IEnumerable<string> Names
{
- get { return CollectionUtilities.Proxy(objIds.Keys); }
+ get { return CollectionUtilities.Proxy(names); }
}
}
}
diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
index d56831f35..b180e49f2 100644
--- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs
+++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -164,15 +164,25 @@ namespace Org.BouncyCastle.Pkcs
else
{
X962Parameters x962;
- if (priv.PublicKeyParamSet == null)
+ //if (priv.PublicKeyParamSet == null)
+ //{
+ // X9ECParameters ecP = new X9ECParameters(dp.Curve, new X9ECPoint(dp.G, false), dp.N, dp.H,
+ // dp.GetSeed());
+ // x962 = new X962Parameters(ecP);
+ //}
+ //else
+ //{
+ // x962 = new X962Parameters(priv.PublicKeyParamSet);
+ //}
+ 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);
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]];
+ }
+ }
+}
|