From 97ab16e013298c8624c170c6658cbd492da421cc Mon Sep 17 00:00:00 2001 From: olegkap_cp Date: Mon, 16 Aug 2010 18:12:21 +0000 Subject: [PATCH] Repalce PrivateKey and Signature classes with CryptoKey classes --- Renci.SshClient/Renci.SshClient/KeyFile.cs | 17 +- .../Renci.SshClient/Renci.SshClient.csproj | 13 +- .../Renci.SshClient/Security/CryptoKey.cs | 15 ++ .../{PrivateKey.cs => CryptoPrivateKey.cs} | 16 +- .../Security/CryptoPrivateKeyDss.cs | 141 +++++++++++++ ...rivateKeyRsa.cs => CryptoPrivateKeyRsa.cs} | 175 +++++++--------- .../Security/CryptoPublicKey.cs | 6 + ...{SignatureDss.cs => CryptoPublicKeyDss.cs} | 60 ++++-- ...{SignatureRsa.cs => CryptoPublicKeyRsa.cs} | 67 +++++- .../Renci.SshClient/Security/KeyExchange.cs | 6 +- .../Renci.SshClient/Security/PrivateKeyDsa.cs | 192 ------------------ .../Renci.SshClient/Security/Signature.cs | 16 -- Renci.SshClient/Renci.SshClient/Settings.cs | 9 +- 13 files changed, 360 insertions(+), 373 deletions(-) create mode 100644 Renci.SshClient/Renci.SshClient/Security/CryptoKey.cs rename Renci.SshClient/Renci.SshClient/Security/{PrivateKey.cs => CryptoPrivateKey.cs} (62%) create mode 100644 Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs rename Renci.SshClient/Renci.SshClient/Security/{PrivateKeyRsa.cs => CryptoPrivateKeyRsa.cs} (60%) create mode 100644 Renci.SshClient/Renci.SshClient/Security/CryptoPublicKey.cs rename Renci.SshClient/Renci.SshClient/Security/{SignatureDss.cs => CryptoPublicKeyDss.cs} (52%) rename Renci.SshClient/Renci.SshClient/Security/{SignatureRsa.cs => CryptoPublicKeyRsa.cs} (50%) delete mode 100644 Renci.SshClient/Renci.SshClient/Security/PrivateKeyDsa.cs delete mode 100644 Renci.SshClient/Renci.SshClient/Security/Signature.cs diff --git a/Renci.SshClient/Renci.SshClient/KeyFile.cs b/Renci.SshClient/Renci.SshClient/KeyFile.cs index 3fc98b8a..c2001ea3 100644 --- a/Renci.SshClient/Renci.SshClient/KeyFile.cs +++ b/Renci.SshClient/Renci.SshClient/KeyFile.cs @@ -14,13 +14,15 @@ namespace Renci.SshClient private Regex _headerLineContinue = new Regex(@"(?[^:]+(?\\)?)"); private Regex _endKeyLine = new Regex(@"----[ ]*END (?.+) PRIVATE KEY[ ]*----"); - private PrivateKey _key; + //private PrivateKey _key; + private CryptoPrivateKey _key; public string AlgorithmName { get { - return this._key.AlgorithmName; + return this._key.Name; + //return this._key.AlgorithmName; } } @@ -28,7 +30,8 @@ namespace Renci.SshClient { get { - return this._key.PublicKey; + return this._key.GetPublicKey().GetBytes(); + //return this._key.PublicKey; } } @@ -119,15 +122,19 @@ namespace Renci.SshClient switch (keyName) { case "RSA": - this._key = new PrivateKeyRsa(System.Convert.FromBase64String(data.ToString())); + //this._key = new PrivateKeyRsa(System.Convert.FromBase64String(data.ToString())); + this._key = new CryptoPrivateKeyRsa(); + break; case "DSA": - this._key = new PrivateKeyDsa(System.Convert.FromBase64String(data.ToString())); + //this._key = new PrivateKeyDsa(System.Convert.FromBase64String(data.ToString())); + this._key = new CryptoPrivateKeyDss(); break; default: throw new NotSupportedException(string.Format("Key '{0}' is not supported.", keyName)); } + this._key.Load(System.Convert.FromBase64String(data.ToString())); } } diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj index 12c38092..780b505e 100644 --- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj +++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj @@ -106,17 +106,18 @@ - + + + + + + + - - - - - diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoKey.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoKey.cs new file mode 100644 index 00000000..13c64b39 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoKey.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Renci.SshClient.Security +{ + public abstract class CryptoKey + { + public abstract string Name { get; } + + public abstract void Load(IEnumerable data); + + public abstract bool VerifySignature(IEnumerable hash, IEnumerable signature); + + public abstract IEnumerable GetBytes(); + } +} diff --git a/Renci.SshClient/Renci.SshClient/Security/PrivateKey.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs similarity index 62% rename from Renci.SshClient/Renci.SshClient/Security/PrivateKey.cs rename to Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs index 82b7ec40..5f6aa919 100644 --- a/Renci.SshClient/Renci.SshClient/Security/PrivateKey.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs @@ -3,20 +3,11 @@ using Renci.SshClient.Common; namespace Renci.SshClient.Security { - internal abstract class PrivateKey + public abstract class CryptoPrivateKey : CryptoKey { - public abstract string AlgorithmName { get; } + public abstract CryptoPublicKey GetPublicKey(); - protected IEnumerable Data { get; private set; } - - public abstract IEnumerable PublicKey { get; } - - public PrivateKey(IEnumerable data) - { - this.Data = data; - } - - public abstract IEnumerable GetSignature(IEnumerable sessionId); + public abstract IEnumerable GetSignature(IEnumerable key); protected class SignatureKeyData : SshData { @@ -34,6 +25,5 @@ namespace Renci.SshClient.Security this.Write(this.Signature.GetSshString()); } } - } } diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs new file mode 100644 index 00000000..8a57ace0 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; + +namespace Renci.SshClient.Security +{ + public class CryptoPrivateKeyDss : CryptoPrivateKey + { + private byte[] _p; + private byte[] _q; + private byte[] _g; + private byte[] _x; + private byte[] _publicKey; + + public override string Name + { + get { return "ssh-dss"; } + } + + public override void Load(IEnumerable data) + { + using (var 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 InvalidOperationException("Not valid DSS Key."); + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + throw new NotSupportedException("Not supported DSS Key version."); + bt = binr.ReadByte(); + if (bt != 0x00) + throw new InvalidOperationException("Not valid DSS Key."); + + //------ 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._x = binr.ReadBytes(elems); + } + } + + public override CryptoPublicKey GetPublicKey() + { + return new CryptoPublicKeyDss(); + } + + public override IEnumerable GetSignature(IEnumerable key) + { + var data = key.ToArray(); + using (var sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider()) + using (var cs = new System.Security.Cryptography.CryptoStream(System.IO.Stream.Null, sha1, System.Security.Cryptography.CryptoStreamMode.Write)) + { + DSAParameters DSAKeyInfo = new DSAParameters(); + + DSAKeyInfo.X = this._x.TrimLeadinZero().ToArray(); + DSAKeyInfo.P = this._p.TrimLeadinZero().ToArray(); + DSAKeyInfo.Q = this._q.TrimLeadinZero().ToArray(); + DSAKeyInfo.G = this._g.TrimLeadinZero().ToArray(); + + cs.Write(data, 0, data.Length); + + cs.Close(); + + var DSA = new System.Security.Cryptography.DSACryptoServiceProvider(); + DSA.ImportParameters(DSAKeyInfo); + var DSAFormatter = new RSAPKCS1SignatureFormatter(DSA); + DSAFormatter.SetHashAlgorithm("SHA1"); + + var signature = DSAFormatter.CreateSignature(sha1); + + return new SignatureKeyData + { + AlgorithmName = this.Name, + Signature = signature, + }.GetBytes(); + } + } + + public override bool VerifySignature(IEnumerable hash, IEnumerable signature) + { + throw new NotImplementedException(); + } + + public override IEnumerable 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 = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; // we already have the data size + } + + return count; + } + + } +} diff --git a/Renci.SshClient/Renci.SshClient/Security/PrivateKeyRsa.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs similarity index 60% rename from Renci.SshClient/Renci.SshClient/Security/PrivateKeyRsa.cs rename to Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs index 7b8c3bcb..22c2c413 100644 --- a/Renci.SshClient/Renci.SshClient/Security/PrivateKeyRsa.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs @@ -3,74 +3,99 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; -using Renci.SshClient.Common; namespace Renci.SshClient.Security { - internal class PrivateKeyRsa : PrivateKey + public class CryptoPrivateKeyRsa : CryptoPrivateKey { private byte[] _modulus; - private byte[] _eValue; + private byte[] _exponent; private byte[] _dValue; private byte[] _pValue; private byte[] _qValue; private byte[] _dpValue; private byte[] _dqValue; - private byte[] _iqValue; + private byte[] _inverseQ; - private IEnumerable _publicKey; - /// - /// Gets the public key. - /// - /// The public key. - public override IEnumerable PublicKey - { - get - { - if (this._publicKey == null) - { - this._publicKey = new RsaPublicKeyData - { - E = this._eValue, - Modulus = this._modulus, - }.GetBytes(); - - } - return this._publicKey; - } - } - - public override string AlgorithmName + public override string Name { get { return "ssh-rsa"; } } - - public PrivateKeyRsa(IEnumerable data) - : base(data) + public override void Load(IEnumerable data) { - if (!this.ParseRSAPrivateKey()) + // --------- Set up stream to decode the asn.1 encoded RSA private key ------ + using (var ms = new MemoryStream(data.ToArray())) + using (var binr = new BinaryReader(ms)) //wrap Memory Stream with BinaryReader for easy reading { - throw new InvalidDataException("RSA Key is not valid"); + 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 InvalidOperationException("Not valid RSA Key."); + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + throw new NotSupportedException("Not supported RSA Key version."); + bt = binr.ReadByte(); + if (bt != 0x00) + throw new InvalidOperationException("Not valid RSA Key."); + + + //------ 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); } } - public override IEnumerable GetSignature(IEnumerable sessionId) + public override CryptoPublicKey GetPublicKey() { - var data = sessionId.ToArray(); + return new CryptoPublicKeyRsa(this._modulus, this._exponent); + } + + public override IEnumerable GetSignature(IEnumerable key) + { + var data = key.ToArray(); using (var sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider()) using (var cs = new System.Security.Cryptography.CryptoStream(System.IO.Stream.Null, sha1, System.Security.Cryptography.CryptoStreamMode.Write)) { RSAParameters RSAKeyInfo = new RSAParameters(); - RSAKeyInfo.Exponent = _eValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.Exponent = _exponent.TrimLeadinZero().ToArray(); RSAKeyInfo.D = _dValue.TrimLeadinZero().ToArray(); RSAKeyInfo.Modulus = _modulus.TrimLeadinZero().ToArray(); RSAKeyInfo.P = _pValue.TrimLeadinZero().ToArray(); RSAKeyInfo.Q = _qValue.TrimLeadinZero().ToArray(); RSAKeyInfo.DP = _dpValue.TrimLeadinZero().ToArray(); RSAKeyInfo.DQ = _dqValue.TrimLeadinZero().ToArray(); - RSAKeyInfo.InverseQ = _iqValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.InverseQ = _inverseQ.TrimLeadinZero().ToArray(); cs.Write(data, 0, data.Length); @@ -85,66 +110,20 @@ namespace Renci.SshClient.Security return new SignatureKeyData { - AlgorithmName = this.AlgorithmName, + AlgorithmName = this.Name, Signature = signature, }.GetBytes(); } - } - private bool ParseRSAPrivateKey() + public override bool VerifySignature(IEnumerable hash, IEnumerable signature) { - // --------- Set up stream to decode the asn.1 encoded RSA private key ------ - using (var ms = new MemoryStream(this.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; + throw new NotImplementedException(); + } - 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 - return false; - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number - return false; - bt = binr.ReadByte(); - if (bt != 0x00) - return false; - - - //------ all private key components are Integer sequences ---- - elems = GetIntegerSize(binr); - this._modulus = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._eValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._dValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._pValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._qValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._dpValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._dqValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._iqValue = binr.ReadBytes(elems); - - return true; - } + public override IEnumerable GetBytes() + { + throw new NotImplementedException(); } private static int GetIntegerSize(BinaryReader binr) @@ -175,23 +154,5 @@ namespace Renci.SshClient.Security return count; } - - private class RsaPublicKeyData : SshData - { - public IEnumerable Modulus { get; set; } - - public IEnumerable E { get; set; } - - protected override void LoadData() - { - } - - protected override void SaveData() - { - this.Write("ssh-rsa"); - this.Write(this.E.GetSshString()); - this.Write(this.Modulus.GetSshString()); - } - } } } diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKey.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKey.cs new file mode 100644 index 00000000..85fc51b3 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKey.cs @@ -0,0 +1,6 @@ +namespace Renci.SshClient.Security +{ + public abstract class CryptoPublicKey : CryptoKey + { + } +} diff --git a/Renci.SshClient/Renci.SshClient/Security/SignatureDss.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs similarity index 52% rename from Renci.SshClient/Renci.SshClient/Security/SignatureDss.cs rename to Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs index 46c2610c..2d0edca3 100644 --- a/Renci.SshClient/Renci.SshClient/Security/SignatureDss.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs @@ -1,41 +1,62 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Security.Cryptography; namespace Renci.SshClient.Security { - internal class SignatureDss : Signature + public class CryptoPublicKeyDss : CryptoPublicKey { + private IEnumerable _p; + private IEnumerable _q; + private IEnumerable _g; + private IEnumerable _x; + public override string Name { - get { return "ssh-dss"; } + get { throw new NotImplementedException(); } } - public SignatureDss(IEnumerable data) - : base(data) + public CryptoPublicKeyDss() { } - public override bool ValidateSignature(IEnumerable hash, IEnumerable signature) + public CryptoPublicKeyDss(IEnumerable p, IEnumerable q, IEnumerable g, IEnumerable x) { - var pLength = BitConverter.ToUInt32(this.Data.Take(4).Reverse().ToArray(), 0); + this._p = p; + this._q = q; + this._g = g; + this._x = x; + } - var pData = this.Data.Skip(4).Take((int)pLength).ToArray(); + public override void Load(IEnumerable data) + { + using (var ms = new MemoryStream(data.ToArray())) + using (var br = new BinaryReader(ms)) + { - var qLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)pLength).Take(4).Reverse().ToArray(), 0); + var pl = BitConverter.ToUInt32(br.ReadBytes(4).Reverse().ToArray(), 0); - var qData = this.Data.Skip(4 + (int)pLength + 4).Take((int)qLength).ToArray(); + _p = br.ReadBytes((int)pl); - var gLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)pLength + 4 + (int)qLength).Take(4).Reverse().ToArray(), 0); + var ql = BitConverter.ToUInt32(br.ReadBytes(4).Reverse().ToArray(), 0); - var gData = this.Data.Skip(4 + (int)pLength + 4 + (int)qLength + 4).Take((int)gLength).ToArray(); + _q = br.ReadBytes((int)ql); - var xLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)pLength + 4 + (int)qLength + 4 + (int)gLength).Take(4).Reverse().ToArray(), 0); + var gl = BitConverter.ToUInt32(br.ReadBytes(4).Reverse().ToArray(), 0); - var xData = this.Data.Skip(4 + (int)pLength + 4 + (int)qLength + 4 + (int)xLength + 4).Take((int)xLength).ToArray(); + _g = br.ReadBytes((int)gl); + var xl = BitConverter.ToUInt32(br.ReadBytes(4).Reverse().ToArray(), 0); + + _x = br.ReadBytes((int)xl); + } + } + + public override bool VerifySignature(IEnumerable hash, IEnumerable signature) + { using (var sha1 = new SHA1CryptoServiceProvider()) { using (var cs = new CryptoStream(System.IO.Stream.Null, sha1, CryptoStreamMode.Write)) @@ -49,10 +70,10 @@ namespace Renci.SshClient.Security { dsa.ImportParameters(new DSAParameters { - X = xData.TrimLeadinZero().ToArray(), - P = pData.TrimLeadinZero().ToArray(), - Q = qData.TrimLeadinZero().ToArray(), - G = gData.TrimLeadinZero().ToArray(), + X = _x.TrimLeadinZero().ToArray(), + P = _p.TrimLeadinZero().ToArray(), + Q = _q.TrimLeadinZero().ToArray(), + G = _g.TrimLeadinZero().ToArray(), }); var dsaDeformatter = new DSASignatureDeformatter(dsa); dsaDeformatter.SetHashAlgorithm("SHA1"); @@ -87,5 +108,10 @@ namespace Renci.SshClient.Security } } } + + public override IEnumerable GetBytes() + { + throw new NotImplementedException(); + } } } diff --git a/Renci.SshClient/Renci.SshClient/Security/SignatureRsa.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyRsa.cs similarity index 50% rename from Renci.SshClient/Renci.SshClient/Security/SignatureRsa.cs rename to Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyRsa.cs index e2700a1a..e79a1e35 100644 --- a/Renci.SshClient/Renci.SshClient/Security/SignatureRsa.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyRsa.cs @@ -1,33 +1,51 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Security.Cryptography; +using Renci.SshClient.Common; namespace Renci.SshClient.Security { - internal class SignatureRsa : Signature + public class CryptoPublicKeyRsa : CryptoPublicKey { + private IEnumerable _modulus; + private IEnumerable _exponent; + public override string Name { get { return "ssh-rsa"; } } - public SignatureRsa(IEnumerable data) - : base(data) + public CryptoPublicKeyRsa() { } - public override bool ValidateSignature(IEnumerable hash, IEnumerable signature) + internal CryptoPublicKeyRsa(IEnumerable modulus, IEnumerable exponent) { - var exponentLength = BitConverter.ToUInt32(this.Data.Take(4).Reverse().ToArray(), 0); + this._modulus = modulus; + this._exponent = exponent; + } - var exponentData = this.Data.Skip(4).Take((int)exponentLength).ToArray(); + public override void Load(IEnumerable data) + { + using (var ms = new MemoryStream(data.ToArray())) + using (var br = new BinaryReader(ms)) + { - var modulusLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)exponentLength).Take(4).Reverse().ToArray(), 0); + var el = BitConverter.ToUInt32(br.ReadBytes(4).Reverse().ToArray(), 0); - var modulusData = this.Data.Skip(4 + (int)exponentLength + 4).Take((int)modulusLength).ToArray(); + this._exponent = br.ReadBytes((int)el); + var ml = BitConverter.ToUInt32(br.ReadBytes(4).Reverse().ToArray(), 0); + + this._modulus = br.ReadBytes((int)ml); + } + } + + public override bool VerifySignature(IEnumerable hash, IEnumerable signature) + { using (var sha1 = new SHA1CryptoServiceProvider()) { using (var cs = new CryptoStream(System.IO.Stream.Null, sha1, CryptoStreamMode.Write)) @@ -41,9 +59,10 @@ namespace Renci.SshClient.Security { rsa.ImportParameters(new RSAParameters { - Exponent = exponentData, - Modulus = modulusData.TrimLeadinZero().ToArray(), + Exponent = this._exponent.TrimLeadinZero().ToArray(), + Modulus = this._modulus.TrimLeadinZero().ToArray(), }); + var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa); rsaDeformatter.SetHashAlgorithm("SHA1"); @@ -77,5 +96,33 @@ namespace Renci.SshClient.Security } } } + + public override IEnumerable GetBytes() + { + return new RsaPublicKeyData + { + E = this._exponent, + Modulus = this._modulus, + }.GetBytes(); + } + + private class RsaPublicKeyData : SshData + { + public IEnumerable Modulus { get; set; } + + public IEnumerable E { get; set; } + + protected override void LoadData() + { + } + + protected override void SaveData() + { + this.Write("ssh-rsa"); + this.Write(this.E.GetSshString()); + this.Write(this.Modulus.GetSshString()); + } + } + } } diff --git a/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs b/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs index c9c2fd3d..86cb0f64 100644 --- a/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs +++ b/Renci.SshClient/Renci.SshClient/Security/KeyExchange.cs @@ -296,9 +296,11 @@ namespace Renci.SshClient.Security var data = bytes.Skip(4 + algorithmName.Length); - var signature = Settings.HostKeyAlgorithms[algorithmName](data); + CryptoPublicKey key = Settings.HostKeyAlgorithms[algorithmName](); - return signature.ValidateSignature(this.ExchangeHash, this.Signature.GetSshBytes()); + key.Load(data); + + return key.VerifySignature(this.ExchangeHash, this.Signature.GetSshBytes()); } protected void SendMessage(Message message) diff --git a/Renci.SshClient/Renci.SshClient/Security/PrivateKeyDsa.cs b/Renci.SshClient/Renci.SshClient/Security/PrivateKeyDsa.cs deleted file mode 100644 index f8dd8a08..00000000 --- a/Renci.SshClient/Renci.SshClient/Security/PrivateKeyDsa.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshClient.Common; - -namespace Renci.SshClient.Security -{ - internal class PrivateKeyDsa : PrivateKey - { - private byte[] _pValue; - - private byte[] _qValue; - - private byte[] _gValue; - - private byte[] _publicKeyValue; - - private byte[] _privateKeyValue; - - private IEnumerable _publicKey; - /// - /// Gets the public key. - /// - /// The public key. - public override IEnumerable PublicKey - { - get - { - if (this._publicKey == null) - { - this._publicKey = new DsaPublicKeyData - { - P = this._pValue, - Q = this._qValue, - G = this._gValue, - Public = this._publicKeyValue, - }.GetBytes(); - - } - return this._publicKey; - } - } - - public override string AlgorithmName - { - get { return "ssh-dss"; } - } - - - public PrivateKeyDsa(IEnumerable data) - : base(data) - { - if (!this.ParseDSAPrivateKey()) - { - throw new InvalidDataException("DSA Key is not valid"); - } - } - - public override IEnumerable GetSignature(IEnumerable sessionId) - { - var data = sessionId.ToArray(); - using (var sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider()) - using (var cs = new System.Security.Cryptography.CryptoStream(System.IO.Stream.Null, sha1, System.Security.Cryptography.CryptoStreamMode.Write)) - { - DSAParameters DSAKeyInfo = new DSAParameters(); - - DSAKeyInfo.X = this._privateKeyValue.TrimLeadinZero().ToArray(); - DSAKeyInfo.P = this._pValue.TrimLeadinZero().ToArray(); - DSAKeyInfo.Q = this._qValue.TrimLeadinZero().ToArray(); - DSAKeyInfo.G = this._gValue.TrimLeadinZero().ToArray(); - - cs.Write(data, 0, data.Length); - - cs.Close(); - - var DSA = new System.Security.Cryptography.DSACryptoServiceProvider(); - DSA.ImportParameters(DSAKeyInfo); - var DSAFormatter = new RSAPKCS1SignatureFormatter(DSA); - DSAFormatter.SetHashAlgorithm("SHA1"); - - var signature = DSAFormatter.CreateSignature(sha1); - - return new SignatureKeyData - { - AlgorithmName = this.AlgorithmName, - Signature = signature, - }.GetBytes(); - } - - } - - private bool ParseDSAPrivateKey() - { - // --------- Set up stream to decode the asn.1 encoded RSA private key ------ - using (var ms = new MemoryStream(this.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 - return false; - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number - return false; - bt = binr.ReadByte(); - if (bt != 0x00) - return false; - - //------ all private key components are Integer sequences ---- - elems = GetIntegerSize(binr); - this._pValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._qValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._gValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._publicKeyValue = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - this._privateKeyValue = binr.ReadBytes(elems); - } - - return true; - } - - 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 = BitConverter.ToInt32(modint, 0); - } - else - { - count = bt; // we already have the data size - } - - return count; - } - - private class DsaPublicKeyData : SshData - { - public IEnumerable P { get; set; } - - public IEnumerable Q { get; set; } - - public IEnumerable G { get; set; } - - public IEnumerable Public { get; set; } - - protected override void LoadData() - { - } - - protected override void SaveData() - { - this.Write("ssh-dss"); - this.Write(this.P.GetSshString()); - this.Write(this.Q.GetSshString()); - this.Write(this.G.GetSshString()); - this.Write(this.Public.GetSshString()); - } - } - } -} diff --git a/Renci.SshClient/Renci.SshClient/Security/Signature.cs b/Renci.SshClient/Renci.SshClient/Security/Signature.cs deleted file mode 100644 index 0b95987c..00000000 --- a/Renci.SshClient/Renci.SshClient/Security/Signature.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace Renci.SshClient.Security -{ - internal abstract class Signature : Algorithm - { - protected IEnumerable Data { get; private set; } - - public Signature(IEnumerable data) - { - this.Data = data; - } - - public abstract bool ValidateSignature(IEnumerable hash, IEnumerable signature); - } -} diff --git a/Renci.SshClient/Renci.SshClient/Settings.cs b/Renci.SshClient/Renci.SshClient/Settings.cs index bf939f2d..5cd5b6cd 100644 --- a/Renci.SshClient/Renci.SshClient/Settings.cs +++ b/Renci.SshClient/Renci.SshClient/Settings.cs @@ -14,8 +14,7 @@ namespace Renci.SshClient public static IDictionary, HMAC>> HmacAlgorithms { get; private set; } - public static IDictionary, Signature>> HostKeyAlgorithms { get; private set; } - + public static IDictionary> HostKeyAlgorithms { get; private set; } static Settings() { @@ -39,10 +38,10 @@ namespace Renci.SshClient {"hmac-sha1", (key) => { return new System.Security.Cryptography.HMACSHA1(key.Take(20).ToArray());}}, }; - Settings.HostKeyAlgorithms = new Dictionary, Signature>>() + Settings.HostKeyAlgorithms = new Dictionary>() { - {"ssh-rsa", (hostKeyData) => { return new SignatureRsa(hostKeyData);}}, - {"ssh-dsa", (hostKeyData) => { return new SignatureDss(hostKeyData);;}}, // TODO: Need to be tested + {"ssh-rsa", () => { return new CryptoPublicKeyRsa();}}, + {"ssh-dsa", () => { return new CryptoPublicKeyDss();}}, // TODO: Need to be tested }; } }