Add RsaKey and DsaKey classes

Add HostAlgorithm class for host key authentication and possible for certificate authentication in the future
Refactor public/private key logic
This commit is contained in:
olegkap_cp
2011-08-16 18:48:16 +00:00
parent eb8a5086ad
commit 745bc9e2bc
28 changed files with 808 additions and 1023 deletions
@@ -55,7 +55,6 @@
<ItemGroup>
<Compile Include="ConnectionTest.cs" />
<Compile Include="Security\TestCipher.cs" />
<Compile Include="Security\TestCryptoKey.cs" />
<Compile Include="Security\TestHMac.cs" />
<Compile Include="Security\TestHostKey.cs" />
<Compile Include="Security\TestKeyExchange.cs" />
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Security;
using Renci.SshNet.Tests.Properties;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Tests.Security
{
@@ -16,8 +17,8 @@ namespace Renci.SshNet.Tests.Security
{
var connectionInfo = new PasswordConnectionInfo(Resources.HOST, 22, Resources.USERNAME, Resources.PASSWORD);
connectionInfo.HostKeyAlgorithms.Clear();
connectionInfo.HostKeyAlgorithms.Add("ssh-rsa", typeof(CryptoPublicKeyRsa));
connectionInfo.HostKeyAlgorithms.Add("ssh-rsa", (data) => { return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); });
using (var client = new SshClient(connectionInfo))
{
client.Connect();
@@ -30,7 +31,7 @@ namespace Renci.SshNet.Tests.Security
{
var connectionInfo = new PasswordConnectionInfo(Resources.HOST, 22, Resources.USERNAME, Resources.PASSWORD);
connectionInfo.HostKeyAlgorithms.Clear();
connectionInfo.HostKeyAlgorithms.Add("ssh-dss", typeof(CryptoPublicKeyDss));
connectionInfo.HostKeyAlgorithms.Add("ssh-dss", (data) => { return new KeyHostAlgorithm("ssh-dss", new DsaKey(), data); });
using (var client = new SshClient(connectionInfo))
{
+82 -33
View File
@@ -8,7 +8,7 @@ namespace Renci.SshNet.Common
/// <summary>
/// Base class for DER encoded data.
/// </summary>
public abstract class DerData
public class DerData
{
private const byte CONSTRUCTED = 0x20;
@@ -44,13 +44,48 @@ namespace Renci.SshNet.Common
private List<byte> _data;
private int _readerIndex = 0;
private int _lastIndex;
public byte[] Encode()
/// <summary>
/// Gets a value indicating whether end of data is reached.
/// </summary>
/// <value>
/// <c>true</c> if end of data is reached; otherwise, <c>false</c>.
/// </value>
public bool IsEndOfData
{
get
{
return this._readerIndex >= this._lastIndex;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DerData"/> class.
/// </summary>
public DerData()
{
this._data = new List<byte>();
}
this.SaveData();
/// <summary>
/// Initializes a new instance of the <see cref="DerData"/> class.
/// </summary>
/// <param name="data">DER encoded data.</param>
public DerData(byte[] data)
{
this._data = new List<byte>(data);
var dataType = this.ReadByte();
var length = this.ReadLength();
this._lastIndex = this._readerIndex + length;
}
/// <summary>
/// Encodes written data as DER byte array.
/// </summary>
/// <returns></returns>
public byte[] Encode()
{
var length = this._data.Count();
var lengthBytes = this.GetLength(length);
@@ -60,31 +95,11 @@ namespace Renci.SshNet.Common
return this._data.ToArray();
}
public void Decode(byte[] data)
{
this._data = new List<byte>(data);
this._readerIndex = 0;
var dataType = this.ReadByte();
var length = this.ReadLength();
this.LoadData();
}
/// <summary>
/// Called when type specific data need to be loaded.
/// </summary>
protected abstract void LoadData();
/// <summary>
/// Called when type specific data need to be saved.
/// </summary>
protected abstract void SaveData();
/// <summary>
/// Reads next mpint data type from internal buffer.
/// </summary>
/// <returns>mpint read.</returns>
protected BigInteger ReadBigInt()
public BigInteger ReadBigInteger()
{
var type = this.ReadByte();
if (type != INTEGER)
@@ -98,10 +113,40 @@ namespace Renci.SshNet.Common
}
/// <summary>
/// Writes uint32 data into internal buffer.
/// Reads next int data type from internal buffer.
/// </summary>
/// <param name="data">uint32 data to write.</param>
protected void Write(UInt32 data)
/// <returns>int read.</returns>
public int ReadInteger()
{
var type = this.ReadByte();
if (type != INTEGER)
throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected.");
var length = this.ReadLength();
var data = this.ReadBytes(length);
if (length > 4)
throw new InvalidOperationException("Integer type cannot occupy more then 4 bytes");
var result = 0;
var shift = (length - 1) * 8;
for (int i = 0; i < length; i++)
{
result |= data[i] << shift;
shift -= 8;
}
//return (int)(data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]);
return result;
}
/// <summary>
/// Writes UInt32 data into internal buffer.
/// </summary>
/// <param name="data">UInt32 data to write.</param>
public void Write(UInt32 data)
{
var bytes = data.GetBytes();
this._data.Add(INTEGER);
@@ -110,7 +155,11 @@ namespace Renci.SshNet.Common
this.WriteBytes(bytes);
}
protected void Write(BigInteger data)
/// <summary>
/// Writes BigInteger data into internal buffer.
/// </summary>
/// <param name="data">BigInteger data to write.</param>
public void Write(BigInteger data)
{
var bytes = data.ToByteArray().Reverse().ToList();
this._data.Add(INTEGER);
@@ -119,7 +168,11 @@ namespace Renci.SshNet.Common
this.WriteBytes(bytes);
}
protected void Write(DerData data)
/// <summary>
/// Writes DerData data into internal buffer.
/// </summary>
/// <param name="data">DerData data to write.</param>
public void Write(DerData data)
{
throw new NotImplementedException();
}
@@ -208,9 +261,5 @@ namespace Renci.SshNet.Common
this._readerIndex += length;
return result;
}
}
}
+10 -4
View File
@@ -62,7 +62,7 @@ namespace Renci.SshNet
/// <summary>
/// Gets supported host key algorithms for this connection.
/// </summary>
public IDictionary<string, Type> HostKeyAlgorithms { get; private set; }
public IDictionary<string, Func<byte[], HostAlgorithm>> HostKeyAlgorithms { get; private set; }
/// <summary>
/// Gets supported authentication methods for this connection.
@@ -178,10 +178,16 @@ namespace Renci.SshNet
//{"none", typeof(...)},
};
this.HostKeyAlgorithms = new Dictionary<string, Type>()
this.HostKeyAlgorithms = new Dictionary<string, Func<byte[], HostAlgorithm>>()
{
{"ssh-rsa", typeof(CryptoPublicKeyRsa)},
{"ssh-dss", typeof(CryptoPublicKeyDss)},
{"ssh-rsa", (data) => { return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); }},
{"ssh-dss", (data) => { return new KeyHostAlgorithm("ssh-dss", new DsaKey(), data); }},
//{"x509v3-sign-rsa", () => { ... },
//{"x509v3-sign-dss", () => { ... },
//{"spki-sign-rsa", () => { ... },
//{"spki-sign-dss", () => { ... },
//{"pgp-sign-rsa", () => { ... },
//{"pgp-sign-dss", () => { ... },
};
this.AuthenticationMethods = new Dictionary<string, Type>()
@@ -5,6 +5,9 @@ using System.Text;
namespace Renci.SshNet.Messages.Connection
{
/// <summary>
/// Represents "keepalive@openssh.com" type channel request information
/// </summary>
public class KeepAliveRequestInfo : RequestInfo
{
/// <summary>
@@ -75,14 +75,14 @@ namespace Renci.SshNet
this._publicKeyRequestMessageResponseWaitHandle.Reset();
this._isSignatureRequired = false;
var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.AlgorithmName, keyFile.PublicKey);
var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data);
if (this.KeyFiles.Count < 2)
{
// If only one key file provided then send signature for very first request
var signatureData = new SignatureData(message, this.Session.SessionId).GetBytes();
message.Signature = keyFile.GetSignature(signatureData);
message.Signature = keyFile.HostKey.Sign(signatureData);
}
// Send public key authentication request
@@ -94,11 +94,11 @@ namespace Renci.SshNet
{
this._publicKeyRequestMessageResponseWaitHandle.Reset();
var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.AlgorithmName, keyFile.PublicKey);
var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data);
var signatureData = new SignatureData(message, this.Session.SessionId).GetBytes();
signatureMessage.Signature = keyFile.GetSignature(signatureData);
signatureMessage.Signature = keyFile.HostKey.Sign(signatureData);
// Send public key authentication request with signature
this.SendMessage(signatureMessage);
+8 -39
View File
@@ -27,42 +27,10 @@ namespace Renci.SshNet
private static Regex _privateKeyRegex = new Regex(@"^-----BEGIN (?<keyName>\w+) PRIVATE KEY-----\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]{16})\r?\n\r?\n)?(?<data>([a-zA-Z0-9/+=]{1,64}\r?\n)+)-----END \k<keyName> PRIVATE KEY-----.*", RegexOptions.Compiled | RegexOptions.Multiline);
#endif
private CryptoPrivateKey _key;
/// <summary>
/// Gets the name of private key algorithm.
/// Gets the host key.
/// </summary>
/// <value>
/// The name of the algorithm.
/// </value>
public string AlgorithmName
{
get
{
return this._key.Name;
}
}
/// <summary>
/// Gets the public key.
/// </summary>
public byte[] PublicKey
{
get
{
return this._key.GetPublicKey().GetBytes().ToArray();
}
}
/// <summary>
/// Gets the signature.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <returns>Signature data</returns>
public byte[] GetSignature(IEnumerable<byte> sessionId)
{
return this._key.GetSignature(sessionId);
}
public HostAlgorithm HostKey;
/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
@@ -199,16 +167,14 @@ namespace Renci.SshNet
switch (keyName)
{
case "RSA":
this._key = new CryptoPrivateKeyRsa();
this.HostKey = new KeyHostAlgorithm("ssh-rsa", new RsaKey(decryptedData.ToArray()));
break;
case "DSA":
this._key = new CryptoPrivateKeyDss();
this.HostKey = new KeyHostAlgorithm("ssh-dss", new DsaKey(decryptedData.ToArray()));
break;
default:
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Key '{0}' is not supported.", keyName));
}
this._key.Load(decryptedData);
}
/// <summary>
@@ -224,10 +190,13 @@ namespace Renci.SshNet
{
if (cipherInfo == null)
throw new ArgumentNullException("cipherInfo");
if (cipherData == null)
throw new ArgumentNullException("cipherData");
if (binarySalt == null)
throw new ArgumentNullException("binarySalt");
List<byte> cipherKey = new List<byte>();
using (var md5 = new MD5Hash())
@@ -110,6 +110,7 @@
<Compile Include="Security\Algorithm.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CertificateHostAlgorithm.cs" />
<Compile Include="Security\Cryptography\CipherDigitalSignature.cs" />
<Compile Include="Security\Cryptography\Ciphers\AesCipher.cs" />
<Compile Include="Security\Cryptography\Ciphers\Arc4Cipher.cs" />
@@ -129,6 +130,15 @@
<Compile Include="Security\Cryptography\Ciphers\RsaCipher.cs" />
<Compile Include="Security\Cryptography\Ciphers\SerpentCipher.cs" />
<Compile Include="Security\Cryptography\DsaDigitalSignature.cs" />
<Compile Include="Security\Cryptography\DsaKey.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\Cryptography\Key.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\Cryptography\RsaKey.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\Cryptography\StreamCipher.cs" />
<Compile Include="Security\Cryptography\SymmetricCipher.cs" />
<Compile Include="Security\Cryptography\Ciphers\TripleDesCipher.cs" />
@@ -151,27 +161,7 @@
<Compile Include="Security\Cryptography\Hashes\SHA256Hash.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoKey.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoPrivateKey.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoPrivateKeyDss.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoPrivateKeyRsa.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoPublicKey.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoPublicKeyDss.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\CryptoPublicKeyRsa.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\HostAlgorithm.cs" />
<Compile Include="Security\KeyExchange.cs">
<SubType>Code</SubType>
</Compile>
@@ -190,6 +180,7 @@
<Compile Include="Security\KeyExchangeDiffieHellmanGroupExchangeSha256.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Security\KeyHostAlgorithm.cs" />
<Compile Include="Shell.NET40.cs" />
<Compile Include="SftpClient.NET40.cs" />
<Compile Include="PasswordConnectionInfo.NET40.cs" />
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Renci.SshNet.Security
{
/// <summary>
/// Implements certificate support for host algorithm.
/// </summary>
public class CertificateHostAlgorithm : HostAlgorithm
{
/// <summary>
/// Gets the host key data.
/// </summary>
public override byte[] Data
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// Initializes a new instance of the <see cref="CertificateHostAlgorithm"/> class.
/// </summary>
/// <param name="name">The host key name.</param>
public CertificateHostAlgorithm(string name)
: base(name)
{
}
/// <summary>
/// Signs the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public override byte[] Sign(byte[] data)
{
throw new NotImplementedException();
}
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="signature">The signature.</param>
/// <returns></returns>
public override bool VerifySignature(byte[] data, byte[] signature)
{
throw new NotImplementedException();
}
}
}
@@ -1,27 +0,0 @@
using System.Collections.Generic;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents base class for public and private keys
/// </summary>
public abstract class CryptoKey
{
/// <summary>
/// Gets key name.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Loads key specific data.
/// </summary>
/// <param name="data">The data.</param>
public abstract void Load(IEnumerable<byte> data);
/// <summary>
/// Gets key data byte array.
/// </summary>
/// <returns>The data byte array.</returns>
public abstract IEnumerable<byte> GetBytes();
}
}
@@ -1,62 +0,0 @@
using System.Collections.Generic;
using Renci.SshNet.Common;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents base class for private keys
/// </summary>
public abstract class CryptoPrivateKey : CryptoKey
{
/// <summary>
/// Gets the public key.
/// </summary>
/// <returns></returns>
public abstract CryptoPublicKey GetPublicKey();
/// <summary>
/// Gets the signature.
/// </summary>
/// <param name="key">The key data bytes.</param>
/// <returns></returns>
public abstract byte[] GetSignature(IEnumerable<byte> key);
/// <summary>
/// Represents signature key data structure
/// </summary>
protected class SignatureKeyData : SshData
{
/// <summary>
/// Gets or sets the name of the algorithm.
/// </summary>
/// <value>
/// The name of the algorithm.
/// </value>
public string AlgorithmName { get; set; }
/// <summary>
/// Gets or sets the signature.
/// </summary>
/// <value>
/// The signature.
/// </value>
public byte[] Signature { get; set; }
/// <summary>
/// Called when type specific data need to be loaded.
/// </summary>
protected override void LoadData()
{
}
/// <summary>
/// Called when type specific data need to be saved.
/// </summary>
protected override void SaveData()
{
this.Write(this.AlgorithmName);
this.WriteBinaryString(this.Signature);
}
}
}
}
@@ -1,155 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents DSS private key
/// </summary>
internal class CryptoPrivateKeyDss : CryptoPrivateKey
{
private byte[] _p;
private byte[] _q;
private byte[] _g;
private byte[] _publicKey;
private byte[] _privateKey;
/// <summary>
/// Gets key name.
/// </summary>
public override string Name
{
get { return "ssh-dss"; }
}
/// <summary>
/// Loads key specific data.
/// </summary>
/// <param name="data">The data.</param>
public override void Load(IEnumerable<byte> data)
{
MemoryStream ms = null;
try
{
ms = new MemoryStream(data.ToArray());
using (var binr = new BinaryReader(ms)) //wrap Memory Stream with BinaryReader for easy reading
{
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
throw new SshException("DSS key is not valid for use in specified state");
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
throw new SshException("DSS key version is not supported.");
bt = binr.ReadByte();
if (bt != 0x00)
throw new SshException("DSS key is not valid for use in specified state");
//------ all private key components are Integer sequences ----
elems = CryptoPrivateKeyDss.GetIntegerSize(binr);
this._p = binr.ReadBytes(elems);
elems = CryptoPrivateKeyDss.GetIntegerSize(binr);
this._q = binr.ReadBytes(elems);
elems = CryptoPrivateKeyDss.GetIntegerSize(binr);
this._g = binr.ReadBytes(elems);
elems = CryptoPrivateKeyDss.GetIntegerSize(binr);
this._publicKey = binr.ReadBytes(elems);
elems = CryptoPrivateKeyDss.GetIntegerSize(binr);
this._privateKey = binr.ReadBytes(elems);
}
}
finally
{
if (ms != null)
{
ms.Dispose();
ms = null;
}
}
}
/// <summary>
/// Gets the public key.
/// </summary>
/// <returns></returns>
public override CryptoPublicKey GetPublicKey()
{
return new CryptoPublicKeyDss(this._p, this._q, this._g, this._publicKey);
}
/// <summary>
/// Gets the signature.
/// </summary>
/// <param name="key">The key data bytes.</param>
/// <returns></returns>
public override byte[] GetSignature(IEnumerable<byte> key)
{
var ss1 = new DsaDigitalSignature(this._p, this._q, this._g, this._privateKey, null);
var signature = ss1.CreateSignature(key.ToArray());
return new SignatureKeyData
{
AlgorithmName = this.Name,
Signature = signature,
}.GetBytes().ToArray();
}
/// <summary>
/// Gets key data byte array.
/// </summary>
/// <returns>
/// The data byte array.
/// </returns>
public override IEnumerable<byte> GetBytes()
{
throw new NotImplementedException();
}
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = (int)(modint[3] << 24 | modint[2] << 16 | modint[1] << 8 | modint[0]);
}
else
{
count = bt; // we already have the data size
}
return count;
}
}
}
@@ -1,166 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents RSA private key
/// </summary>
internal class CryptoPrivateKeyRsa : CryptoPrivateKey
{
private byte[] _modulus;
private byte[] _exponent;
private byte[] _dValue;
private byte[] _pValue;
private byte[] _qValue;
private byte[] _dpValue;
private byte[] _dqValue;
private byte[] _inverseQ;
/// <summary>
/// Gets key name.
/// </summary>
public override string Name
{
get { return "ssh-rsa"; }
}
/// <summary>
/// Loads key specific data.
/// </summary>
/// <param name="data">The data.</param>
public override void Load(IEnumerable<byte> data)
{
MemoryStream ms = null;
try
{
ms = new MemoryStream(data.ToArray());
using (var binr = new BinaryReader(ms))
{
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
throw new SshException("RSA key is not valid for use in specified state");
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
throw new SshException("RSA key version is not supported.");
bt = binr.ReadByte();
if (bt != 0x00)
throw new SshException("RSA key is not valid for use in specified state");
//------ all private key components are Integer sequences ----
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._modulus = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._exponent = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._dValue = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._pValue = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._qValue = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._dpValue = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._dqValue = binr.ReadBytes(elems);
elems = CryptoPrivateKeyRsa.GetIntegerSize(binr);
this._inverseQ = binr.ReadBytes(elems);
}
}
finally
{
if (ms != null)
{
ms.Dispose();
ms = null;
}
}
}
/// <summary>
/// Gets the public key.
/// </summary>
/// <returns></returns>
public override CryptoPublicKey GetPublicKey()
{
return new CryptoPublicKeyRsa(this._modulus, this._exponent);
}
/// <summary>
/// Gets the signature.
/// </summary>
/// <param name="key">The key data bytes.</param>
/// <returns></returns>
public override byte[] GetSignature(IEnumerable<byte> key)
{
var signature = new RsaDigitalSignature(this._exponent, this._modulus, this._dValue, this._dpValue, this._dqValue, this._inverseQ, this._pValue, this._qValue);
return new SignatureKeyData
{
AlgorithmName = this.Name,
Signature = signature.CreateSignature(key.ToArray()),
}.GetBytes().ToArray();
}
/// <summary>
/// Gets key data byte array.
/// </summary>
/// <returns>
/// The data byte array.
/// </returns>
public override IEnumerable<byte> GetBytes()
{
throw new NotImplementedException();
}
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = (int)(modint[3] << 24 | modint[2] << 16 | modint[1] << 8 | modint[0]);
}
else
{
count = bt; // we already have the data size
}
return count;
}
}
}
@@ -1,18 +0,0 @@
using System.Collections.Generic;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents base class for public keys
/// </summary>
public abstract class CryptoPublicKey : CryptoKey
{
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="hash">The hash.</param>
/// <param name="signature">The signature.</param>
/// <returns>true if signature verified; otherwise false.</returns>
public abstract bool VerifySignature(IEnumerable<byte> hash, IEnumerable<byte> signature);
}
}
@@ -1,173 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents DSS public key
/// </summary>
public class CryptoPublicKeyDss : CryptoPublicKey
{
private byte[] _p;
private byte[] _q;
private byte[] _g;
private byte[] _publicKey;
/// <summary>
/// Gets key name.
/// </summary>
public override string Name
{
get { return "ssh-dss"; }
}
/// <summary>
/// Initializes a new instance of the <see cref="CryptoPublicKeyDss"/> class.
/// </summary>
public CryptoPublicKeyDss()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CryptoPublicKeyDss"/> class.
/// </summary>
/// <param name="p">The p value.</param>
/// <param name="q">The q value.</param>
/// <param name="g">The g value.</param>
/// <param name="publicKey">The public key value.</param>
public CryptoPublicKeyDss(byte[] p, byte[] q, byte[] g, byte[] publicKey)
{
this._p = p;
this._q = q;
this._g = g;
this._publicKey = publicKey;
}
/// <summary>
/// Loads key specific data.
/// </summary>
/// <param name="data">The data.</param>
public override void Load(IEnumerable<byte> data)
{
MemoryStream ms = null;
try
{
ms = new MemoryStream(data.ToArray());
using (var br = new BinaryReader(ms))
{
var pl = (uint)(br.ReadByte() << 24 | br.ReadByte() << 16 | br.ReadByte() << 8 | br.ReadByte());
_p = br.ReadBytes((int)pl);
var ql = (uint)(br.ReadByte() << 24 | br.ReadByte() << 16 | br.ReadByte() << 8 | br.ReadByte());
_q = br.ReadBytes((int)ql);
var gl = (uint)(br.ReadByte() << 24 | br.ReadByte() << 16 | br.ReadByte() << 8 | br.ReadByte());
_g = br.ReadBytes((int)gl);
var xl = (uint)(br.ReadByte() << 24 | br.ReadByte() << 16 | br.ReadByte() << 8 | br.ReadByte());
_publicKey = br.ReadBytes((int)xl);
}
}
finally
{
if (ms != null)
{
ms.Dispose();
ms = null;
}
}
}
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="hash">The hash.</param>
/// <param name="signature">The signature.</param>
/// <returns>
/// true if signature verified; otherwise false.
/// </returns>
public override bool VerifySignature(IEnumerable<byte> hash, IEnumerable<byte> signature)
{
long i = 0;
long j = 0;
byte[] tmp;
var sig = signature.ToArray();
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0)
{
long i1 = (sig[i++] << 24) & 0xff000000;
long i2 = (sig[i++] << 16) & 0x00ff0000;
long i3 = (sig[i++] << 8) & 0x0000ff00;
long i4 = (sig[i++]) & 0x000000ff;
j = i1 | i2 | i3 | i4;
i += j;
i1 = (sig[i++] << 24) & 0xff000000;
i2 = (sig[i++] << 16) & 0x00ff0000;
i3 = (sig[i++] << 8) & 0x0000ff00;
i4 = (sig[i++]) & 0x000000ff;
j = i1 | i2 | i3 | i4;
tmp = new byte[j];
Array.Copy(sig, (int)i, tmp, 0, (int)j);
sig = tmp;
}
var sig1 = new DsaDigitalSignature(_p, _q, _g, null, _publicKey);
return sig1.VerifySignature(hash.ToArray(), sig);
}
/// <summary>
/// Gets key data byte array.
/// </summary>
/// <returns>
/// The data byte array.
/// </returns>
public override IEnumerable<byte> GetBytes()
{
return new DsaPublicKeyData
{
P = this._p,
Q = this._q,
G = this._g,
Public = this._publicKey,
}.GetBytes();
}
private class DsaPublicKeyData : SshData
{
public byte[] P { get; set; }
public byte[] Q { get; set; }
public byte[] G { get; set; }
public byte[] Public { get; set; }
protected override void LoadData()
{
}
protected override void SaveData()
{
this.Write("ssh-dss");
this.WriteBinaryString(this.P);
this.WriteBinaryString(this.Q);
this.WriteBinaryString(this.G);
this.WriteBinaryString(this.Public);
}
}
}
}
@@ -1,155 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
namespace Renci.SshNet.Security
{
/// <summary>
/// Represents RSA public key
/// </summary>
public class CryptoPublicKeyRsa : CryptoPublicKey
{
private byte[] _modulus;
private byte[] _exponent;
/// <summary>
/// Gets key name.
/// </summary>
public override string Name
{
get { return "ssh-rsa"; }
}
/// <summary>
/// Initializes a new instance of the <see cref="CryptoPublicKeyRsa"/> class.
/// </summary>
public CryptoPublicKeyRsa()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CryptoPublicKeyRsa"/> class.
/// </summary>
/// <param name="modulus">The modulus.</param>
/// <param name="exponent">The exponent.</param>
internal CryptoPublicKeyRsa(byte[] modulus, byte[] exponent)
{
this._modulus = modulus;
this._exponent = exponent;
}
/// <summary>
/// Loads key specific data.
/// </summary>
/// <param name="data">The data.</param>
public override void Load(IEnumerable<byte> data)
{
MemoryStream ms = null;
try
{
ms = new MemoryStream(data.ToArray());
using (var br = new BinaryReader(ms))
{
var el = (uint)(br.ReadByte() << 24 | br.ReadByte() << 16 | br.ReadByte() << 8 | br.ReadByte());
this._exponent = br.ReadBytes((int)el);
var ml = (uint)(br.ReadByte() << 24 | br.ReadByte() << 16 | br.ReadByte() << 8 | br.ReadByte());
this._modulus = br.ReadBytes((int)ml);
}
}
finally
{
if (ms != null)
{
ms.Dispose();
ms = null;
}
}
}
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="hash">The hash.</param>
/// <param name="signature">The signature.</param>
/// <returns>
/// true if signature verified; otherwise false.
/// </returns>
public override bool VerifySignature(IEnumerable<byte> hash, IEnumerable<byte> signature)
{
long i = 0;
long j = 0;
byte[] tmp;
var sig = signature.ToArray();
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0)
{
long i1 = (sig[i++] << 24) & 0xff000000;
long i2 = (sig[i++] << 16) & 0x00ff0000;
long i3 = (sig[i++] << 8) & 0x0000ff00;
long i4 = (sig[i++]) & 0x000000ff;
j = i1 | i2 | i3 | i4;
i += j;
i1 = (sig[i++] << 24) & 0xff000000;
i2 = (sig[i++] << 16) & 0x00ff0000;
i3 = (sig[i++] << 8) & 0x0000ff00;
i4 = (sig[i++]) & 0x000000ff;
j = i1 | i2 | i3 | i4;
tmp = new byte[j];
Array.Copy(sig, (int)i, tmp, 0, (int)j);
sig = tmp;
}
var sig1 = new RsaDigitalSignature(this._exponent, this._modulus);
return sig1.VerifySignature(hash.ToArray(), sig);
}
/// <summary>
/// Gets key data byte array.
/// </summary>
/// <returns>
/// The data byte array.
/// </returns>
public override IEnumerable<byte> GetBytes()
{
return new RsaPublicKeyData
{
E = this._exponent,
Modulus = this._modulus,
}.GetBytes();
}
private class RsaPublicKeyData : SshData
{
public byte[] Modulus { get; set; }
public byte[] E { get; set; }
protected override void LoadData()
{
}
protected override void SaveData()
{
this.Write("ssh-rsa");
this.WriteBinaryString(this.E);
this.WriteBinaryString(this.Modulus);
}
}
}
}
@@ -22,6 +22,12 @@ namespace Renci.SshNet.Security.Cryptography
/// <param name="cipher">The cipher.</param>
public CipherDigitalSignature(HashAlgorithm hash, AsymmetricCipher cipher)
{
if (hash == null)
throw new ArgumentNullException("hash");
if (cipher == null)
throw new ArgumentNullException("cipher");
this._hash = hash;
this._cipher = cipher;
}
@@ -32,7 +38,7 @@ namespace Renci.SshNet.Security.Cryptography
/// <param name="input">The input.</param>
/// <param name="signature">The signature.</param>
/// <returns></returns>
public override bool VerifySignature(byte[] input, byte[] signature)
public override bool Verify(byte[] input, byte[] signature)
{
var sig = this._cipher.Decrypt(signature);
@@ -68,7 +74,7 @@ namespace Renci.SshNet.Security.Cryptography
/// </summary>
/// <param name="input">The input.</param>
/// <returns></returns>
public override byte[] CreateSignature(byte[] input)
public override byte[] Sign(byte[] input)
{
// Calculate hash value
var hashData = this.Hash(input);
@@ -103,6 +109,7 @@ namespace Renci.SshNet.Security.Cryptography
protected static List<byte> DerEncode(byte[] hashData)
{
// TODO: Replace with DER Encoding
// TODO: Replace with algorithm code
var algorithm = new byte[] { 6, 5, 43, 14, 3, 2, 26 };
var algorithmParams = new byte[] { 5, 0 };
@@ -16,56 +16,20 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
private bool _isPrivate;
private BigInteger _exponent;
private BigInteger _modulus;
private BigInteger _d;
private BigInteger _dp;
private BigInteger _dq;
private BigInteger _inverseQ;
private BigInteger _p;
private BigInteger _q;
private RsaKey _key;
/// <summary>
/// Initializes a new instance of the <see cref="RsaCipher"/> class.
/// </summary>
/// <param name="exponent">The exponent.</param>
/// <param name="modulus">The modulus.</param>
public RsaCipher(BigInteger exponent, BigInteger modulus)
/// <param name="key">The RSA key.</param>
public RsaCipher(RsaKey key)
{
//if (key == null)
// throw new ArgumentNullException("key");
//this._publicKey = key;
this._exponent = exponent;
this._modulus = modulus;
this._isPrivate = false;
}
if (key == null)
throw new ArgumentNullException("key");
/// <summary>
/// Initializes a new instance of the <see cref="RsaCipher"/> class.
/// </summary>
/// <param name="exponent">The exponent.</param>
/// <param name="modulus">The modulus.</param>
/// <param name="d">The d.</param>
/// <param name="dp">The dp.</param>
/// <param name="dq">The dq.</param>
/// <param name="inverseQ">The inverse Q.</param>
/// <param name="p">The p.</param>
/// <param name="q">The q.</param>
public RsaCipher(BigInteger exponent, BigInteger modulus, BigInteger d, BigInteger dp, BigInteger dq, BigInteger inverseQ, BigInteger p, BigInteger q)
{
//if (key == null)
// throw new ArgumentNullException("key");
//this._privateKey = key;
this._exponent = exponent;
this._modulus = modulus;
this._d = d;
this._dp = dp;
this._dq = dq;
this._inverseQ = inverseQ;
this._p = p;
this._q = q;
this._isPrivate = true;
this._key = key;
this._isPrivate = !this._key.D.IsZero;
}
/// <summary>
@@ -101,7 +65,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
{
BigInteger random = BigInteger.One;
var max = this._modulus - 1;
var max = this._key.Modulus - 1;
while (random <= BigInteger.One || random >= max)
{
@@ -112,29 +76,28 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
random = new BigInteger(bytesArray.Reverse().ToArray());
}
BigInteger blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, this._exponent, this._modulus) * input), this._modulus);
BigInteger blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, this._key.Exponent, this._key.Modulus) * input), this._key.Modulus);
// mP = ((input Mod p) ^ dP)) Mod p
var mP = BigInteger.ModPow((blindedInput % this._p), this._dp, this._p);
var mP = BigInteger.ModPow((blindedInput % this._key.P), this._key.DP, this._key.P);
// mQ = ((input Mod q) ^ dQ)) Mod q
var mQ = BigInteger.ModPow((blindedInput % this._q), this._dq, this._q);
var mQ = BigInteger.ModPow((blindedInput % this._key.Q), this._key.DQ, this._key.Q);
var h = BigInteger.PositiveMod(((mP - mQ) * this._inverseQ), this._p);
var h = BigInteger.PositiveMod(((mP - mQ) * this._key.InverseQ), this._key.P);
var m = h * this._q + mQ;
var m = h * this._key.Q + mQ;
BigInteger rInv = BigInteger.ModInverse(random, this._modulus);
BigInteger rInv = BigInteger.ModInverse(random, this._key.Modulus);
result = BigInteger.PositiveMod((m * rInv), this._modulus);
result = BigInteger.PositiveMod((m * rInv), this._key.Modulus);
}
else
{
result = BigInteger.ModPow(input, this._exponent, this._modulus);
result = BigInteger.ModPow(input, this._key.Exponent, this._key.Modulus);
}
return result.ToByteArray().Reverse().ToArray();
}
}
}
@@ -17,13 +17,13 @@ namespace Renci.SshNet.Security.Cryptography
/// <param name="input">The input.</param>
/// <param name="signature">The signature.</param>
/// <returns></returns>
public abstract bool VerifySignature(byte[] input, byte[] signature);
public abstract bool Verify(byte[] input, byte[] signature);
/// <summary>
/// Creates the signature.
/// </summary>
/// <param name="input">The input.</param>
/// <returns></returns>
public abstract byte[] CreateSignature(byte[] input);
public abstract byte[] Sign(byte[] input);
}
}
@@ -13,30 +13,23 @@ namespace Renci.SshNet.Security.Cryptography
/// </summary>
public class DsaDigitalSignature : DigitalSignature
{
private BigInteger _p;
private BigInteger _q;
private BigInteger _g;
private BigInteger _privateKey;
private BigInteger _publicKey;
private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider();
private HashAlgorithm _hash;
private DsaKey _key;
/// <summary>
/// Initializes a new instance of the <see cref="DsaDigitalSignature"/> class.
/// </summary>
/// <param name="p">The p.</param>
/// <param name="q">The q.</param>
/// <param name="g">The g.</param>
/// <param name="privateKey">The private key.</param>
/// <param name="publicKey">The public key.</param>
public DsaDigitalSignature(byte[] p, byte[] q, byte[] g, byte[] privateKey, byte[] publicKey)
/// <param name="key">The DSA key.</param>
public DsaDigitalSignature(DsaKey key)
{
this._p = new BigInteger(p.Reverse().ToArray());
this._q = new BigInteger(q.Reverse().ToArray());
this._g = new BigInteger(g.Reverse().ToArray());
if (privateKey != null)
this._privateKey = new BigInteger(privateKey.Reverse().ToArray());
if (publicKey != null)
this._publicKey = new BigInteger(publicKey.Reverse().ToArray());
if (key == null)
throw new ArgumentNullException("key");
this._key = key;
this._hash = new SHA1Hash();
}
@@ -46,7 +39,7 @@ namespace Renci.SshNet.Security.Cryptography
/// <param name="input">The input.</param>
/// <param name="signature">The signature.</param>
/// <returns></returns>
public override bool VerifySignature(byte[] input, byte[] signature)
public override bool Verify(byte[] input, byte[] signature)
{
var hashInput = this._hash.ComputeHash(input);
@@ -69,26 +62,26 @@ namespace Renci.SshNet.Security.Cryptography
BigInteger s = new BigInteger(sBytes);
// Reject the signature if 0 < r < q or 0 < s < q is not satisfied.
if (r <= 0 || r >= this._q)
if (r <= 0 || r >= this._key.Q)
return false;
if (s <= 0 || s >= this._q)
if (s <= 0 || s >= this._key.Q)
return false;
// Calculate w = s1 mod q
BigInteger w = BigInteger.ModInverse(s, this._q);
BigInteger w = BigInteger.ModInverse(s, this._key.Q);
// Calculate u1 = H(m)·w mod q
BigInteger u1 = hm * w % this._q;
BigInteger u1 = hm * w % this._key.Q;
// Calculate u2 = r * w mod q
BigInteger u2 = r * w % this._q;
BigInteger u2 = r * w % this._key.Q;
u1 = BigInteger.ModPow(this._g, u1, this._p);
u2 = BigInteger.ModPow(this._publicKey, u2, this._p);
u1 = BigInteger.ModPow(this._key.G, u1, this._key.P);
u2 = BigInteger.ModPow(this._key.Y, u2, this._key.P);
// Calculate v = ((g pow u1 * y pow u2) mod p) mod q
BigInteger v = ((u1 * u2) % this._p) % this._q;
BigInteger v = ((u1 * u2) % this._key.P) % this._key.Q;
// The signature is valid if v = r
return v == r;
@@ -99,7 +92,7 @@ namespace Renci.SshNet.Security.Cryptography
/// </summary>
/// <param name="input">The input.</param>
/// <returns></returns>
public override byte[] CreateSignature(byte[] input)
public override byte[] Sign(byte[] input)
{
var hashInput = this._hash.ComputeHash(input);
@@ -114,26 +107,30 @@ namespace Renci.SshNet.Security.Cryptography
do
{
// TODO: Take random function to base class
// TODO: Take random function to BigInteger
// Generate a random per-message value k where 0 < k < q
do
{
//k = new BigInteger(q.BitLength, random);
k = BigInteger.Parse("980263959677973875983479554308083464979482795347", System.Globalization.NumberStyles.None, CultureInfo.InvariantCulture);
var bytesArray = new byte[20];
_randomizer.GetBytes(bytesArray);
bytesArray[bytesArray.Length - 1] = (byte)(bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value
k = new BigInteger(bytesArray.Reverse().ToArray());
}
while (k <= 0 || k >= this._q);
while (k <= 0 || k >= this._key.Q);
// Calculate r = ((g pow k) mod p) mod q
r = BigInteger.ModPow(this._g, k, this._p) % this._q;
r = BigInteger.ModPow(this._key.G, k, this._key.P) % this._key.Q;
// In the unlikely case that r = 0, start again with a different random k
} while (r.IsZero);
// Calculate s = ((k pow 1)(H(m) + x*r)) mod q
k = (BigInteger.ModInverse(k, this._q) * (m + this._privateKey * r));
k = (BigInteger.ModInverse(k, this._key.Q) * (m + this._key.X * r));
s = k % this._q;
s = k % this._key.Q;
// In the unlikely case that s = 0, start again with a different random k
} while (s.IsZero);
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Contains DSA private and public key
/// </summary>
public class DsaKey : Key
{
/// <summary>
/// Gets public key Y.
/// </summary>
public BigInteger Y { get; private set; }
/// <summary>
/// Gets private key X.
/// </summary>
public BigInteger X { get; private set; }
/// <summary>
/// Gets the G.
/// </summary>
public BigInteger G { get; private set; }
/// <summary>
/// Gets the Q.
/// </summary>
public BigInteger Q { get; private set; }
/// <summary>
/// Gets the P.
/// </summary>
public BigInteger P { get; private set; }
private DigitalSignature _digitalSignature;
/// <summary>
/// Gets the digital signature.
/// </summary>
protected override DigitalSignature DigitalSignature
{
get
{
if (this._digitalSignature == null)
{
this._digitalSignature = new DsaDigitalSignature(this);
}
return this._digitalSignature;
}
}
/// <summary>
/// Gets or sets the public.
/// </summary>
/// <value>
/// The public.
/// </value>
public override BigInteger[] Public
{
get
{
return new BigInteger[] { this.P, this.Q, this.G, this.Y };
}
set
{
if (value.Length != 4)
throw new InvalidOperationException("Invalid public key.");
this.P = value[0];
this.Q = value[1];
this.G = value[2];
this.Y = value[3];
}
}
/// <summary>
/// Gets or sets the private.
/// </summary>
/// <value>
/// The private.
/// </value>
protected override BigInteger[] Private
{
get
{
//return new BigInteger[] { this.P, this.Q, this.G, this.Y, this.X };
return new BigInteger[] { this.P, this.Q, this.G, this.X };
}
set
{
if (value.Length != 5)
throw new InvalidOperationException("Invalid private key.");
this.P = value[0];
this.Q = value[1];
this.G = value[2];
this.Y = value[3];
this.X = value[4];
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DsaKey"/> class.
/// </summary>
public DsaKey()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DsaKey"/> class.
/// </summary>
/// <param name="data">DER encoded private key data.</param>
public DsaKey(byte[] data)
: base(data)
{
}
}
}
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Base class for asymmetric cipher algorithms
/// </summary>
public abstract class Key
{
/// <summary>
/// Gets the key specific digital signature.
/// </summary>
protected abstract DigitalSignature DigitalSignature { get; }
/// <summary>
/// Gets or sets the public key.
/// </summary>
/// <value>
/// The public.
/// </value>
public abstract BigInteger[] Public { get; set; }
/// <summary>
/// Gets or sets the private key.
/// </summary>
/// <value>
/// The private.
/// </value>
protected abstract BigInteger[] Private { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Key"/> class.
/// </summary>
/// <param name="data">DER encoded private key data.</param>
public Key(byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
var der = new DerData(data);
var version = der.ReadBigInteger();
var keys = new List<BigInteger>();
while (!der.IsEndOfData)
{
keys.Add(der.ReadBigInteger());
}
this.Private = keys.ToArray();
}
/// <summary>
/// Initializes a new instance of the <see cref="Key"/> class.
/// </summary>
public Key()
: base()
{
}
/// <summary>
/// Signs the specified data with the key.
/// </summary>
/// <param name="data">The data to sign.</param>
/// <returns>Signed data.</returns>
public byte[] Sign(byte[] data)
{
return this.DigitalSignature.Sign(data);
}
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="data">The data to verify.</param>
/// <param name="signature">The signature to verify against.</param>
/// <returns></returns>
public bool VerifySignature(byte[] data, byte[] signature)
{
return this.DigitalSignature.Verify(data, signature);
}
}
}
@@ -16,26 +16,9 @@ namespace Renci.SshNet.Security.Cryptography
/// <summary>
/// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class.
/// </summary>
/// <param name="exponent">The exponent.</param>
/// <param name="modulus">The modulus.</param>
/// <param name="d">The D value.</param>
/// <param name="dp">The DP value.</param>
/// <param name="dq">The DQ value.</param>
/// <param name="inverseQ">The InverseQ value.</param>
/// <param name="p">The P value.</param>
/// <param name="q">The Q value.</param>
public RsaDigitalSignature(byte[] exponent, byte[] modulus, byte[] d, byte[] dp, byte[] dq, byte[] inverseQ, byte[] p, byte[] q)
: base(new SHA1Hash(), new RsaCipher(new BigInteger(exponent.Reverse().ToArray()), new BigInteger(modulus.Reverse().ToArray()), new BigInteger(d.Reverse().ToArray()), new BigInteger(dp.Reverse().ToArray()), new BigInteger(dq.Reverse().ToArray()), new BigInteger(inverseQ.Reverse().ToArray()), new BigInteger(p.Reverse().ToArray()), new BigInteger(q.Reverse().ToArray())))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class.
/// </summary>
/// <param name="exponent">The exponent.</param>
/// <param name="modulus">The modulus.</param>
public RsaDigitalSignature(byte[] exponent, byte[] modulus)
: base(new SHA1Hash(), new RsaCipher(new BigInteger(exponent.Reverse().ToArray()), new BigInteger(modulus.Reverse().ToArray())))
/// <param name="rsaKey">The RSA key.</param>
public RsaDigitalSignature(RsaKey rsaKey)
: base(new SHA1Hash(), new RsaCipher(rsaKey))
{
}
}
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Contains RSA private and public key
/// </summary>
public class RsaKey : Key
{
/// <summary>
/// Gets the modulus.
/// </summary>
public BigInteger Modulus { get; private set; }
/// <summary>
/// Gets the exponent.
/// </summary>
public BigInteger Exponent { get; private set; }
/// <summary>
/// Gets the D.
/// </summary>
public BigInteger D { get; private set; }
/// <summary>
/// Gets the P.
/// </summary>
public BigInteger P { get; private set; }
/// <summary>
/// Gets the Q.
/// </summary>
public BigInteger Q { get; private set; }
/// <summary>
/// Gets the DP.
/// </summary>
public BigInteger DP { get; private set; }
/// <summary>
/// Gets the DQ.
/// </summary>
public BigInteger DQ { get; private set; }
/// <summary>
/// Gets the inverse Q.
/// </summary>
public BigInteger InverseQ { get; private set; }
private DigitalSignature _digitalSignature;
/// <summary>
/// Gets the digital signature.
/// </summary>
protected override DigitalSignature DigitalSignature
{
get
{
if (this._digitalSignature == null)
{
this._digitalSignature = new RsaDigitalSignature(this);
}
return this._digitalSignature;
}
}
/// <summary>
/// Gets or sets the public.
/// </summary>
/// <value>
/// The public.
/// </value>
public override BigInteger[] Public
{
get
{
return new BigInteger[] { this.Exponent, this.Modulus };
}
set
{
if (value.Length != 2)
throw new InvalidOperationException("Invalid private key.");
this.Exponent = value[0];
this.Modulus = value[1];
}
}
/// <summary>
/// Gets or sets the private.
/// </summary>
/// <value>
/// The private.
/// </value>
protected override BigInteger[] Private
{
get
{
return new BigInteger[] { this.Modulus, this.Exponent, this.D, this.P, this.Q, this.DP, this.DQ, this.InverseQ };
}
set
{
if (value.Length != 8)
throw new InvalidOperationException("Invalid private key.");
this.Modulus = value[0];
this.Exponent = value[1];
this.D = value[2];
this.P = value[3];
this.Q = value[4];
this.DP = value[5];
this.DQ = value[6];
this.InverseQ = value[7];
}
}
/// <summary>
/// Initializes a new instance of the <see cref="RsaKey"/> class.
/// </summary>
public RsaKey()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RsaKey"/> class.
/// </summary>
/// <param name="data">DER encoded private key data.</param>
public RsaKey(byte[] data)
: base(data)
{
}
}
}
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Base class for SSH host algorithms.
/// </summary>
public abstract class HostAlgorithm
{
/// <summary>
/// Gets the host key name.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Gets the host key data.
/// </summary>
public abstract byte[] Data { get; }
/// <summary>
/// Initializes a new instance of the <see cref="HostAlgorithm"/> class.
/// </summary>
/// <param name="name">The host key name.</param>
public HostAlgorithm(string name)
{
this.Name = name;
}
/// <summary>
/// Signs the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public abstract byte[] Sign(byte[] data);
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="signature">The signature.</param>
/// <returns></returns>
public abstract bool VerifySignature(byte[] data, byte[] signature);
}
}
@@ -7,6 +7,7 @@ using Renci.SshNet.Messages.Transport;
using System.Diagnostics;
using Renci.SshNet.Messages;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
@@ -72,6 +73,8 @@ namespace Renci.SshNet.Security
{
var exchangeHash = this.CalculateHash();
// TODO: See how to improve this area
var bytes = this._hostKey;
var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]);
@@ -80,9 +83,7 @@ namespace Renci.SshNet.Security
var data = bytes.Skip(4 + algorithmName.Length);
CryptoPublicKey key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName].CreateInstance<CryptoPublicKey>();
key.Load(data);
var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey);
return key.VerifySignature(exchangeHash, this._signature);
}
@@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
/// <summary>
/// Implements key support for host algorithm.
/// </summary>
public class KeyHostAlgorithm : HostAlgorithm
{
private Key _key;
/// <summary>
/// Gets the public key data.
/// </summary>
public override byte[] Data
{
get
{
return new SshKeyData(this.Name, this._key.Public).GetBytes();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class.
/// </summary>
/// <param name="name">Host key name.</param>
/// <param name="key">Host key.</param>
public KeyHostAlgorithm(string name, Key key)
: base(name)
{
this._key = key;
}
/// <summary>
/// Initializes a new instance of the <see cref="HostAlgorithm"/> class.
/// </summary>
/// <param name="name">Host key name.</param>
/// <param name="key">Host key.</param>
/// <param name="data">Host key encoded data.</param>
public KeyHostAlgorithm(string name, Key key, byte[] data)
: base(name)
{
this._key = key;
var sshKey = new SshKeyData();
sshKey.Load(data);
this._key.Public = sshKey.Keys;
}
/// <summary>
/// Signs the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public override byte[] Sign(byte[] data)
{
return new SignatureKeyData(this.Name, this._key.Sign(data)).GetBytes().ToArray();
}
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="signature">The signature.</param>
/// <returns></returns>
public override bool VerifySignature(byte[] data, byte[] signature)
{
var signatureData = new SignatureKeyData();
signatureData.Load(signature);
return this._key.VerifySignature(data, signatureData.Signature);
}
private class SshKeyData : SshData
{
public BigInteger[] Keys { get; private set; }
public string Name { get; private set; }
public SshKeyData()
{
}
public SshKeyData(string name, params BigInteger[] keys)
{
this.Name = name;
this.Keys = keys;
}
protected override void LoadData()
{
this.Name = this.ReadString();
var keys = new List<BigInteger>();
while (!this.IsEndOfData)
{
keys.Add(this.ReadBigInt());
}
this.Keys = keys.ToArray();
}
protected override void SaveData()
{
this.Write(this.Name);
foreach (var key in this.Keys)
{
this.Write(key);
}
}
}
private class SignatureKeyData : SshData
{
/// <summary>
/// Gets or sets the name of the algorithm.
/// </summary>
/// <value>
/// The name of the algorithm.
/// </value>
public string AlgorithmName { get; private set; }
/// <summary>
/// Gets or sets the signature.
/// </summary>
/// <value>
/// The signature.
/// </value>
public byte[] Signature { get; private set; }
public SignatureKeyData()
{
}
public SignatureKeyData(string name, byte[] signature)
{
this.AlgorithmName = name;
this.Signature = signature;
}
/// <summary>
/// Called when type specific data need to be loaded.
/// </summary>
protected override void LoadData()
{
this.AlgorithmName = this.ReadString();
this.Signature = this.ReadBinaryString();
}
/// <summary>
/// Called when type specific data need to be saved.
/// </summary>
protected override void SaveData()
{
this.Write(this.AlgorithmName);
this.WriteBinaryString(this.Signature);
}
}
}
}