Skip to content

Commit

Permalink
Add Support for ECDSA Host- and Private-Keys
Browse files Browse the repository at this point in the history
Also known as:
- ecdsa-sha2-nistp256
- ecdsa-sha2-nistp384
- ecdsa-sha2-nistp521

Basically it translate between SSH-Data and Microsoft Crypto API.
  • Loading branch information
darinkes committed Dec 3, 2018
1 parent 38167e3 commit a6108d8
Show file tree
Hide file tree
Showing 16 changed files with 788 additions and 16 deletions.
10 changes: 8 additions & 2 deletions src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII</DefineConstants>
<DefineConstants>TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand All @@ -29,7 +29,7 @@
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII</DefineConstants>
<DefineConstants>TRACE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Renci.SshNet.xml</DocumentationFile>
Expand Down Expand Up @@ -695,6 +695,12 @@
<Compile Include="..\Renci.SshNet\Security\Cryptography\Key.cs">
<Link>Security\Cryptography\Key.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Security\Cryptography\EcdsaDigitalSignature.cs">
<Link>Security\Cryptography\EcdsaDigitalSignature.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Security\Cryptography\EcdsaKey.cs">
<Link>Security\Cryptography\EcdsaKey.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Security\Cryptography\RsaDigitalSignature.cs">
<Link>Security\Cryptography\RsaDigitalSignature.cs</Link>
</Compile>
Expand Down
20 changes: 20 additions & 0 deletions src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,26 @@
<Link>Data\Key.SSH2.RSA.txt</Link>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.ECDSA.txt">
<Link>Data\Key.ECDSA.txt</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.ECDSA384.txt">
<Link>Data\Key.ECDSA384.txt</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.ECDSA521.txt">
<Link>Data\Key.ECDSA521.txt</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.ECDSA.Encrypted.txt">
<Link>Data\Key.ECDSA.Encrypted.txt</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.ECDSA384.Encrypted.txt">
<Link>Data\Key.ECDSA384.Encrypted.txt</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.ECDSA521.Encrypted.txt">
<Link>Data\Key.ECDSA521.Encrypted.txt</Link>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
Expand Down
66 changes: 66 additions & 0 deletions src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,72 @@ public void Test_PrivateKey_RSA_DES_EDE3_CFB()
}
}

[TestMethod]
[Owner("darinkes")]
[TestCategory("PrivateKey")]
public void Test_PrivateKey_ECDSA()
{
using (var stream = GetData("Key.ECDSA.txt"))
{
new PrivateKeyFile(stream);
}
}

[TestMethod]
[Owner("darinkes")]
[TestCategory("PrivateKey")]
public void Test_PrivateKey_ECDSA384()
{
using (var stream = GetData("Key.ECDSA384.txt"))
{
new PrivateKeyFile(stream);
}
}

[TestMethod]
[Owner("darinkes")]
[TestCategory("PrivateKey")]
public void Test_PrivateKey_ECDSA521()
{
using (var stream = GetData("Key.ECDSA521.txt"))
{
new PrivateKeyFile(stream);
}
}

[TestMethod]
[Owner("darinkes")]
[TestCategory("PrivateKey")]
public void Test_PrivateKey_ECDSA_Encrypted()
{
using (var stream = GetData("Key.ECDSA.Encrypted.txt"))
{
new PrivateKeyFile(stream, "12345");
}
}

[TestMethod]
[Owner("darinkes")]
[TestCategory("PrivateKey")]
public void Test_PrivateKey_ECDSA384_Encrypted()
{
using (var stream = GetData("Key.ECDSA384.Encrypted.txt"))
{
new PrivateKeyFile(stream, "12345");
}
}

[TestMethod]
[Owner("darinkes")]
[TestCategory("PrivateKey")]
public void Test_PrivateKey_ECDSA521_Encrypted()
{
using (var stream = GetData("Key.ECDSA521.Encrypted.txt"))
{
new PrivateKeyFile(stream, "12345");
}
}

/// <summary>
///A test for Dispose
///</summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,54D46F498C989115AAE14FEA21E3AF11

IQdFnndcbzz10d7YQIgEE1TzuzJrm7uYJr4Hvdfz/FshVxMRqxqaqtEgo2vAHHik
BOcPkm+84ERlTNPslcJqLSkKzCdxb7Rz5hfwHuN3Y6Lf01qGakDlzAUEjEyDor+4
zQtAne+f+gRUJnBvLLoVhH4xdeQFC55GECNUFQpEmos=
-----END EC PRIVATE KEY-----
5 changes: 5 additions & 0 deletions src/Renci.SshNet.Tests/Data/Key.ECDSA.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEdqaFKgJBIibVjyUh1v7Y35LwIQJrocdTaYFLwl7iB0oAoGCCqGSM49
AwEHoUQDQgAEQD5MO/n9yqSDTszwzVpApLx5SQFecE5ZfDkgxqVdHQecm1BAPozZ
4eKGNhKn72hT79mLlp9HXX+oNEcuVT83Hw==
-----END EC PRIVATE KEY-----
9 changes: 9 additions & 0 deletions src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,1D64653C5E18C2AACB0B17E3FE43C219

lCtRmcvKSeIACwqTtsf/ei1brtCZ386rsk/j7bSXdkZBpvzcmzbeo6w6CYm206Km
hV9TMl2dIO/I1/ov5/2VIR3ZkaElyDOJD/+Be0e3aus4EZj1H1YM/Dv+4QJId+is
Cw4ycWjfudYPPejGdiyjzt5qjaIJwrrEvGtMg7sWVAqDpjcAjS9KuaCu5nOgdItL
s7oHuz+DTGdJQNfUHAlUnz1JaMRWzpP0MwtxdcaRY+w=
-----END EC PRIVATE KEY-----
6 changes: 6 additions & 0 deletions src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCQawHdHLR7NvKa2vPV0sVkbzOE8c0enp95iEysGcGV66RXE1EH//nh
gu5UzeTR4KigBwYFK4EEACKhZANiAAQUk4rVvoOPI1hQzWpNx09Uo6qG+srGcbvB
q15eFK0GnK/T0UBKxdbZ2+//KAYI6SeDHM9t3ORF1aX5EpjTEBI4d7ZY/lV9jX6M
nJ4XuGteJselM2iMmy+p9ZYw83BYB1Y=
-----END EC PRIVATE KEY-----
10 changes: 10 additions & 0 deletions src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,F995028237EBD79C928530CC6C3E957F

wT+iajbte4MnpCipVy/7W9t2I8OgwbMjNBw9PB5xmXR1NQX+yWa81DXMTgjHi8++
6tp+Vlftkr7mY1yvZCVo1Sy4VgcvZeMhtpVKtvYdMCmHJC6gaDOTYX3yee8DJ4FL
fG+IQz0wFyZZ26NFrHiwbufW9z6pXhGNCQZK0KLbFxI9iKwVA0llc7uzTEcmBBpn
0/Snp0CVvX+i6AP9Xj0bBdrFCsvcoT+ZHzS8YWJUfu3m6cpAJksCAy0PXR3ifvus
edTfDpkMxd4/b+DtPB6SMekIAjnQyzbyaTwJCujm8iU=
-----END EC PRIVATE KEY-----
7 changes: 7 additions & 0 deletions src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBn2DAme7AU8sCA+/sd6s3c2FNW26IiPvulGd3FC8k5q+fjBZ5LUWR
iJMGrsf2rJLO8hXMGJYoF9tjZEGaabQ8KVagBwYFK4EEACOhgYkDgYYABABrpVjs
ANqcvqMUo1wo0I1uVCXQ6xrauy4iU86FiOwFmkYRrle4w3oYdRJwniC3TwGMuBuM
PMIoCTXr0UtUzn1vkQESNR/J/jAxVseLlVe+KDfZHKvsvk2+O4XaSa1qMfLwN3sp
wlj08+ylKjlO6V3g0hbz4ZaSVwuiRS7Xsv8W2MV6rg==
-----END EC PRIVATE KEY-----
8 changes: 8 additions & 0 deletions src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,14 @@
<Name>Renci.SshNet</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Data\Key.ECDSA.txt" />
<EmbeddedResource Include="Data\Key.ECDSA384.txt" />
<EmbeddedResource Include="Data\Key.ECDSA521.txt" />
<EmbeddedResource Include="Data\Key.ECDSA.Encrypted.txt" />
<EmbeddedResource Include="Data\Key.ECDSA384.Encrypted.txt" />
<EmbeddedResource Include="Data\Key.ECDSA521.Encrypted.txt" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
85 changes: 77 additions & 8 deletions src/Renci.SshNet/Common/DerData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class DerData

private const byte Boolean = 0x01;
private const byte Integer = 0x02;
//private const byte BITSTRING = 0x03;
private const byte BITSTRING = 0x03;
private const byte Octetstring = 0x04;
private const byte Null = 0x05;
private const byte Objectidentifier = 0x06;
Expand Down Expand Up @@ -101,7 +101,7 @@ public BigInteger ReadBigInteger()
{
var type = ReadByte();
if (type != Integer)
throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected.");
throw new InvalidOperationException(string.Format("Invalid data type, INTEGER(02) is expected, got {0}", type));

var length = ReadLength();

Expand All @@ -118,7 +118,7 @@ public int ReadInteger()
{
var type = ReadByte();
if (type != Integer)
throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected.");
throw new InvalidOperationException(string.Format("Invalid data type, INTEGER(02) is expected, got {0}", type));

var length = ReadLength();

Expand All @@ -140,6 +140,36 @@ public int ReadInteger()
return result;
}

/// <summary>
/// Reads next octetstring data type from internal buffer.
/// </summary>
/// <returns>data read.</returns>
public byte[] ReadOctetString()
{
var type = ReadByte();
if (type != Octetstring)
throw new InvalidOperationException(string.Format("Invalid data type, OCTETSTRING(04) is expected, got {0}", type));

var length = ReadLength();
var data = ReadBytes(length);
return data;
}

/// <summary>
/// Reads next object data type from internal buffer.
/// </summary>
/// <returns>data read.</returns>
public byte[] ReadObject()
{
var type = ReadByte();
if (type != Objectidentifier)
throw new InvalidOperationException(string.Format("Invalid data type, OBJECT(06) is expected, got {0}", type));

var length = ReadLength();
var data = ReadBytes(length);
return data;
}

/// <summary>
/// Writes BOOLEAN data into internal buffer.
/// </summary>
Expand Down Expand Up @@ -189,6 +219,18 @@ public void Write(byte[] data)
WriteBytes(data);
}

/// <summary>
/// Writes BITSTRING data into internal buffer.
/// </summary>
/// <param name="data">The data.</param>
public void WriteBitstring(byte[] data)
{
_data.Add(BITSTRING);
var length = GetLength(data.Length);
WriteBytes(length);
WriteBytes(data);
}

/// <summary>
/// Writes OBJECTIDENTIFIER data into internal buffer.
/// </summary>
Expand Down Expand Up @@ -229,6 +271,18 @@ public void Write(ObjectIdentifier identifier)
WriteBytes(bytes);
}

/// <summary>
/// Writes OBJECTIDENTIFIER data into internal buffer.
/// </summary>
/// <param name="bytes">The bytes.</param>
public void WriteObjectIdentifier(byte[] bytes)
{
_data.Add(Objectidentifier);
var length = GetLength(bytes.Length);
WriteBytes(length);
WriteBytes(bytes);
}

/// <summary>
/// Writes NULL data into internal buffer.
/// </summary>
Expand Down Expand Up @@ -270,8 +324,11 @@ private static IEnumerable<byte> GetLength(int length)
}
return new[] {(byte) length};
}

private int ReadLength()
/// <summary>
/// Gets Data Length
/// </summary>
/// <returns>length</returns>
public int ReadLength()
{
int length = ReadByte();

Expand Down Expand Up @@ -306,20 +363,32 @@ private int ReadLength()
return length;
}

private void WriteBytes(IEnumerable<byte> data)
/// <summary>
/// Write Byte data into internal buffer.
/// </summary>
public void WriteBytes(IEnumerable<byte> data)
{
_data.AddRange(data);
}

private byte ReadByte()
/// <summary>
/// Reads Byte data into internal buffer.
/// </summary>
/// <returns>data read</returns>
public byte ReadByte()
{
if (_readerIndex > _data.Count)
throw new InvalidOperationException("Read out of boundaries.");

return _data[_readerIndex++];
}

private byte[] ReadBytes(int length)
/// <summary>
/// Reads lengths Bytes data into internal buffer.
/// </summary>
/// <returns>data read</returns>
/// <param name="length">amount of data to read.</param>
public byte[] ReadBytes(int length)
{
if (_readerIndex + length > _data.Count)
throw new InvalidOperationException("Read out of boundaries.");
Expand Down
6 changes: 5 additions & 1 deletion src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,13 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy

HostKeyAlgorithms = new Dictionary<string, Func<byte[], KeyHostAlgorithm>>
{
#if FEATURE_ECDSA
{"ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data)},
{"ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data)},
{"ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data)},
#endif
{"ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data)},
{"ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data)},
//{"ecdsa-sha2-nistp256 "}
//{"x509v3-sign-rsa", () => { ... },
//{"x509v3-sign-dss", () => { ... },
//{"spki-sign-rsa", () => { ... },
Expand Down
Loading

0 comments on commit a6108d8

Please sign in to comment.