浏览代码

Improve DER object serialization and add ObjectIdentifier class to identify algorithm being used during DER serialziation

olegkap_cp 14 年之前
父节点
当前提交
6f20f21d11

+ 101 - 24
Renci.SshClient/Renci.SshNet/Common/DerData.cs

@@ -14,32 +14,32 @@ namespace Renci.SshNet.Common
 
         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;
-        private const byte EXTERNAL = 0x08;
-        private const byte ENUMERATED = 0x0a;
+        //private const byte EXTERNAL = 0x08;
+        //private const byte ENUMERATED = 0x0a;
         private const byte SEQUENCE = 0x10;
-        private const byte SEQUENCEOF = 0x10; // for completeness
-        private const byte SET = 0x11;
-        private const byte SETOF = 0x11; // for completeness
-
-        private const byte NUMERICSTRING = 0x12;
-        private const byte PRINTABLESTRING = 0x13;
-        private const byte T61STRING = 0x14;
-        private const byte VIDEOTEXSTRING = 0x15;
-        private const byte IA5STRING = 0x16;
-        private const byte UTCTIME = 0x17;
-        private const byte GENERALIZEDTIME = 0x18;
-        private const byte GRAPHICSTRING = 0x19;
-        private const byte VISIBLESTRING = 0x1a;
-        private const byte GENERALSTRING = 0x1b;
-        private const byte UNIVERSALSTRING = 0x1c;
-        private const byte BMPSTRING = 0x1e;
-        private const byte UTF8STRING = 0x0c;
-        private const byte APPLICATION = 0x40;
-        private const byte TAGGED = 0x80;
+        //private const byte SEQUENCEOF = 0x10; // for completeness
+        //private const byte SET = 0x11;
+        //private const byte SETOF = 0x11; // for completeness
+
+        //private const byte NUMERICSTRING = 0x12;
+        //private const byte PRINTABLESTRING = 0x13;
+        //private const byte T61STRING = 0x14;
+        //private const byte VIDEOTEXSTRING = 0x15;
+        //private const byte IA5STRING = 0x16;
+        //private const byte UTCTIME = 0x17;
+        //private const byte GENERALIZEDTIME = 0x18;
+        //private const byte GRAPHICSTRING = 0x19;
+        //private const byte VISIBLESTRING = 0x1a;
+        //private const byte GENERALSTRING = 0x1b;
+        //private const byte UNIVERSALSTRING = 0x1c;
+        //private const byte BMPSTRING = 0x1e;
+        //private const byte UTF8STRING = 0x0c;
+        //private const byte APPLICATION = 0x40;
+        //private const byte TAGGED = 0x80;
 
         private List<byte> _data;
 
@@ -142,6 +142,17 @@ namespace Renci.SshNet.Common
             return result;
         }
 
+        /// <summary>
+        /// Writes BOOLEAN data into internal buffer.
+        /// </summary>
+        /// <param name="data">UInt32 data to write.</param>
+        public void Write(bool data)
+        {
+            this._data.Add(BOOLEAN);
+            this._data.Add(1);
+            this._data.Add((byte)(data ? 1 : 0));
+        }
+
         /// <summary>
         /// Writes UInt32 data into internal buffer.
         /// </summary>
@@ -156,7 +167,7 @@ namespace Renci.SshNet.Common
         }
 
         /// <summary>
-        /// Writes BigInteger data into internal buffer.
+        /// Writes INTEGER data into internal buffer.
         /// </summary>
         /// <param name="data">BigInteger data to write.</param>
         public void Write(BigInteger data)
@@ -168,13 +179,79 @@ namespace Renci.SshNet.Common
             this.WriteBytes(bytes);
         }
 
+        /// <summary>
+        /// Writes OCTETSTRING data into internal buffer.
+        /// </summary>
+        /// <param name="data">The data.</param>
+        public void Write(byte[] data)
+        {
+            this._data.Add(OCTETSTRING);
+            var length = this.GetLength(data.Length);
+            this.WriteBytes(length);
+            this.WriteBytes(data);
+        }
+
+        /// <summary>
+        /// Writes OBJECTIDENTIFIER data into internal buffer.
+        /// </summary>
+        /// <param name="identifiers">The identifiers.</param>
+        public void Write(ObjectIdentifier identifier)
+        {
+            var temp = new ulong[identifier.Identifiers.Length - 1];
+            temp[0] = identifier.Identifiers[0] * 40 + identifier.Identifiers[1];
+            Array.Copy(identifier.Identifiers, 2, temp, 1, identifier.Identifiers.Length - 2);
+            var bytes = new List<byte>();
+            foreach (var subidentifier in temp)
+            {
+                var item = subidentifier;
+                var buffer = new byte[8];
+                var bufferIndex = buffer.Length - 1;
+
+                var current = (byte)(item & 0x7F);
+                do
+                {
+                    buffer[bufferIndex] = current;
+                    if (bufferIndex < buffer.Length - 1)
+                        buffer[bufferIndex] |= (byte)0x80;
+                    item >>= 7;
+                    current = (byte)(item & 0x7F);
+                    bufferIndex--;
+                }
+                while (current > 0);
+
+                for (int i = bufferIndex + 1; i < buffer.Length; i++)
+                {
+                    bytes.Add(buffer[i]);
+                }
+            }
+
+
+
+            this._data.Add(OBJECTIDENTIFIER);
+            var length = this.GetLength(bytes.Count);
+            this.WriteBytes(length);
+            this.WriteBytes(bytes);
+        }
+
+
+
+        /// <summary>
+        /// Writes NULL data into internal buffer.
+        /// </summary>
+        public void WriteNull()
+        {
+            this._data.Add(NULL);
+            this._data.Add(0);
+        }
+
         /// <summary>
         /// Writes DerData data into internal buffer.
         /// </summary>
         /// <param name="data">DerData data to write.</param>
         public void Write(DerData data)
         {
-            throw new NotImplementedException();
+            var bytes = data.Encode();
+            this._data.AddRange(bytes);
         }
 
         private byte[] GetLength(int length)

+ 31 - 0
Renci.SshClient/Renci.SshNet/Common/ObjectIdentifier.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Renci.SshNet.Common
+{
+    /// <summary>
+    /// Describes object identifier for DER encoding
+    /// </summary>
+    public struct ObjectIdentifier
+    {
+        /// <summary>
+        /// Gets the object identifier.
+        /// </summary>
+        public ulong[] Identifiers { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ObjectIdentifier"/> class.
+        /// </summary>
+        /// <param name="identifiers">The identifiers.</param>
+        public ObjectIdentifier(params ulong[] identifiers)
+            : this()
+        {
+            if (identifiers.Length < 2)
+                throw new ArgumentException("identifiers");
+
+            this.Identifiers = identifiers;
+        }
+    }
+}

+ 1 - 0
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -83,6 +83,7 @@
     <Compile Include="Common\ChannelOpenFailedEventArgs.cs" />
     <Compile Include="Common\ChannelRequestEventArgs.cs" />
     <Compile Include="Common\DerData.cs" />
+    <Compile Include="Common\ObjectIdentifier.cs" />
     <Compile Include="Common\SemaphoreLight.cs">
       <SubType>Code</SubType>
     </Compile>

+ 26 - 26
Renci.SshClient/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Security.Cryptography;
+using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Security.Cryptography
 {
@@ -15,12 +16,14 @@ namespace Renci.SshNet.Security.Cryptography
 
         private AsymmetricCipher _cipher;
 
+        private ObjectIdentifier _oid;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="CipherDigitalSignature"/> class.
         /// </summary>
         /// <param name="hash">The hash.</param>
         /// <param name="cipher">The cipher.</param>
-        public CipherDigitalSignature(HashAlgorithm hash, AsymmetricCipher cipher)
+        public CipherDigitalSignature(HashAlgorithm hash, ObjectIdentifier oid, AsymmetricCipher cipher)
         {
             if (hash == null)
                 throw new ArgumentNullException("hash");
@@ -30,6 +33,7 @@ namespace Renci.SshNet.Security.Cryptography
 
             this._hash = hash;
             this._cipher = cipher;
+            this._oid = oid;
         }
 
         /// <summary>
@@ -57,10 +61,10 @@ namespace Renci.SshNet.Security.Cryptography
 
             var expected = DerEncode(hashData);
 
-            if (expected.Count != sig1.Length)
+            if (expected.Length != sig1.Length)
                 return false;
 
-            for (int i = 0; i < expected.Count; i++)
+            for (int i = 0; i < expected.Length; i++)
             {
                 if (expected[i] != sig1[i])
                     return false;
@@ -80,19 +84,17 @@ namespace Renci.SshNet.Security.Cryptography
             var hashData = this.Hash(input);
 
             //  Calculate DER string
-
-            //  Resolve algorithm identifier
-            var dd = DerEncode(hashData);
+            var derEncodedHash = DerEncode(hashData);
 
             //  Calculate signature
             var rsaInputBlockSize = new byte[255];
             rsaInputBlockSize[0] = 0x01;
-            for (int i = 1; i < rsaInputBlockSize.Length - dd.Count - 1; i++)
+            for (int i = 1; i < rsaInputBlockSize.Length - derEncodedHash.Length - 1; i++)
             {
                 rsaInputBlockSize[i] = 0xFF;
             }
 
-            Array.Copy(dd.ToArray(), 0, rsaInputBlockSize, rsaInputBlockSize.Length - dd.Count, dd.Count);
+            Array.Copy(derEncodedHash, 0, rsaInputBlockSize, rsaInputBlockSize.Length - derEncodedHash.Length, derEncodedHash.Length);
 
             return this._cipher.Encrypt(rsaInputBlockSize).TrimLeadingZero().ToArray();
         }
@@ -107,25 +109,23 @@ namespace Renci.SshNet.Security.Cryptography
             return this._hash.ComputeHash(input);
         }
 
-        protected static List<byte> DerEncode(byte[] hashData)
+        /// <summary>
+        /// Encodes hash using DER.
+        /// </summary>
+        /// <param name="hashData">The hash data.</param>
+        /// <returns>DER Encoded byte array</returns>
+        protected 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 };
-
-            var dd = new List<byte>(algorithm);
-            dd.AddRange(algorithmParams);
-            dd.Insert(0, (byte)dd.Count);
-            dd.Insert(0, 48);
-
-            dd.Add(4);
-            dd.Add((byte)hashData.Length);
-            dd.AddRange(hashData);
-
-            dd.Insert(0, (byte)dd.Count);
-            dd.Insert(0, 48);
-            return dd;
+            var data = new DerData();
+
+            var alg = new DerData();
+            alg.Write(this._oid);
+            alg.WriteNull();
+
+            data.Write(alg);
+            data.Write(hashData);
+
+            return data.Encode();
         }
     }
 }

+ 1 - 1
Renci.SshClient/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs

@@ -18,7 +18,7 @@ namespace Renci.SshNet.Security.Cryptography
         /// </summary>
         /// <param name="rsaKey">The RSA key.</param>
         public RsaDigitalSignature(RsaKey rsaKey)
-            : base(new SHA1Hash(), new RsaCipher(rsaKey))
+            : base(new SHA1Hash(), new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey))
         {
         }
     }

+ 1 - 2
Renci.SshClient/Renci.SshNet/Sftp/SftpFileStream.cs

@@ -875,8 +875,7 @@ namespace Renci.SshNet.Sftp
                     else
                     {
                         // No: copy the data to the write buffer first.
-                        Array.Copy(buffer, offset, this._buffer,
-                                   this._bufferPosn, tempLen);
+                        Array.Copy(buffer, offset, this._buffer, this._bufferPosn, tempLen);
                         this._bufferPosn += tempLen;
                     }
 

+ 51 - 14
Renci.SshClient/Renci.SshNet/Sftp/SftpSession.cs

@@ -408,24 +408,61 @@ namespace Renci.SshNet.Sftp
         /// <param name="data">The data.</param>
         internal void RequestWrite(byte[] handle, UInt64 offset, byte[] data)
         {
-            using (var wait = new AutoResetEvent(false))
+            var maximumDataSize = 1024 * 32 - 38;
+
+            if (data.Length < maximumDataSize + 1)
             {
-                var request = new SftpWriteRequest(this.NextRequestId, handle, offset, data,
-                    (response) =>
-                    {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
+                using (var wait = new AutoResetEvent(false))
+                {
+                    var request = new SftpWriteRequest(this.NextRequestId, handle, offset, data,
+                        (response) =>
                         {
-                            this.ThrowSftpException(response);
-                        }
-                    });
+                            if (response.StatusCode == StatusCodes.Ok)
+                            {
+                                wait.Set();
+                            }
+                            else
+                            {
+                                this.ThrowSftpException(response);
+                            }
+                        });
+
+                    this.SendRequest(request);
+
+                    this.WaitHandle(wait, this._operationTimeout);
+                }
+            }
+            else 
+            {
+                var block = data.Length / maximumDataSize + 1;
 
-                this.SendRequest(request);
+                for (int i = 0; i < block; i++)
+                {
+                    var blockBufferSize = Math.Min(data.Length - maximumDataSize * i, maximumDataSize);
+                    var blockBuffer = new byte[blockBufferSize];
 
-                this.WaitHandle(wait, this._operationTimeout);
+                    Buffer.BlockCopy(data, i * maximumDataSize, blockBuffer, 0, blockBufferSize);
+
+                    using (var wait = new AutoResetEvent(false))
+                    {
+                        var request = new SftpWriteRequest(this.NextRequestId, handle, offset + (ulong)(i * maximumDataSize), blockBuffer,
+                            (response) =>
+                            {
+                                if (response.StatusCode == StatusCodes.Ok)
+                                {
+                                    wait.Set();
+                                }
+                                else
+                                {
+                                    this.ThrowSftpException(response);
+                                }
+                            });
+
+                        this.SendRequest(request);
+
+                        this.WaitHandle(wait, this._operationTimeout);
+                    }
+                }
             }
         }