Jakub Florczyk - Blog o programowaniu .NET

Programista praktyczny

HMACSHA1 (HMAC-SHA1) w Compact Framework

Ostatnio w wolnych chwilach pracuje nad protokołem OAuth dla Pocket Blip-a wraz z Filipem Tepperem z Blip.pl, który był tak miły i udostępnił mi jedno konto testowe. Problemy pojawiły się już na początku bo w bibliotekach Compact Framework brakuje implementacji HMAC-SHA1. Chciałem sprawę rozwiązać przez użycie OPENNETCF, które mają ten algorytm zaimplementowany, ale niestety pojawiały się błędy których nijak nie mogłem rozwiązać, bo sypały je biblioteki CF-a.

Dlatego jak w poprzednim artykule o PointF rozwiązałem problem używając Reflector-a i dzięki drobnym poprawkom klasa działa lepiej i się nie sypie jak wersja z OPENNETCF:

namespace System.Security.Cryptography
{
    public abstract class KeyedHashAlgorithm : HashAlgorithm
    {
        // Fields
        protected byte[] KeyValue;

        // Methods
        protected KeyedHashAlgorithm()
        {
        }

        public static KeyedHashAlgorithm Create()
        {
            return Create("System.Security.Cryptography.KeyedHashAlgorithm");
        }

        public static KeyedHashAlgorithm Create(string algName)
        {
            return (KeyedHashAlgorithm)CryptoConfig.CreateFromName(algName);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.KeyValue != null)
                {
                    Array.Clear(this.KeyValue, 0, this.KeyValue.Length);
                }
                this.KeyValue = null;
            }
            base.Dispose(disposing);
        }

        // Properties
        public virtual byte[] Key
        {
            get
            {
                return (byte[])this.KeyValue.Clone();
            }
            set
            {
                if (base.State != 0)
                {
                    throw new CryptographicException("Cryptography_HashKeySet");
                }
                this.KeyValue = (byte[])value.Clone();
            }
        }
    }

    public abstract class HMAC : KeyedHashAlgorithm
    {
        // Fields
        private int blockSizeValue = 0x40;
        internal HashAlgorithm m_hash1;
        internal HashAlgorithm m_hash2;
        private bool m_hashing;
        internal string m_hashName;
        private byte[] m_inner;
        private byte[] m_outer;

        // Methods
        protected HMAC()
        {
        }

        public static HMAC Create()
        {
            return Create("System.Security.Cryptography.HMAC");
        }

        public static HMAC Create(string algorithmName)
        {
            return (HMAC)CryptoConfig.CreateFromName(algorithmName);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.m_hash1 != null)
                {
                    this.m_hash1.Clear();
                }
                if (this.m_hash2 != null)
                {
                    this.m_hash2.Clear();
                }
                if (this.m_inner != null)
                {
                    Array.Clear(this.m_inner, 0, this.m_inner.Length);
                }
                if (this.m_outer != null)
                {
                    Array.Clear(this.m_outer, 0, this.m_outer.Length);
                }
            }
            base.Dispose(disposing);
        }

        protected override void HashCore(byte[] rgb, int ib, int cb)
        {
            if (!this.m_hashing)
            {
                this.m_hash1.TransformBlock(this.m_inner, 0, this.m_inner.Length, this.m_inner, 0);
                this.m_hashing = true;
            }
            this.m_hash1.TransformBlock(rgb, ib, cb, rgb, ib);
        }

        protected override byte[] HashFinal()
        {
            if (!this.m_hashing)
            {
                this.m_hash1.TransformBlock(this.m_inner, 0, this.m_inner.Length, this.m_inner, 0);
                this.m_hashing = true;
            }
            this.m_hash1.TransformFinalBlock(new byte[0], 0, 0);
            byte[] hashValue = this.m_hash1.Hash;
            this.m_hash2.TransformBlock(this.m_outer, 0, this.m_outer.Length, this.m_outer, 0);
            this.m_hash2.TransformBlock(hashValue, 0, hashValue.Length, hashValue, 0);
            this.m_hashing = false;
            this.m_hash2.TransformFinalBlock(new byte[0], 0, 0);
            return this.m_hash2.Hash;
        }

        public override void Initialize()
        {
            this.m_hash1.Initialize();
            this.m_hash2.Initialize();
            this.m_hashing = false;
        }

        internal void InitializeKey(byte[] key)
        {
            this.m_inner = null;
            this.m_outer = null;
            if (key.Length > this.BlockSizeValue)
            {
                base.KeyValue = this.m_hash1.ComputeHash(key);
            }
            else
            {
                base.KeyValue = (byte[])key.Clone();
            }
            this.UpdateIOPadBuffers();
        }

        private void UpdateIOPadBuffers()
        {
            int num;
            if (this.m_inner == null)
            {
                this.m_inner = new byte[this.BlockSizeValue];
            }
            if (this.m_outer == null)
            {
                this.m_outer = new byte[this.BlockSizeValue];
            }
            for (num = 0; num < this.BlockSizeValue; num++)
            {
                this.m_inner[num] = 0x36;
                this.m_outer[num] = 0x5c;
            }
            for (num = 0; num < base.KeyValue.Length; num++)
            {
                this.m_inner[num] = (byte)(this.m_inner[num] ^ base.KeyValue[num]);
                this.m_outer[num] = (byte)(this.m_outer[num] ^ base.KeyValue[num]);
            }
        }

        // Properties
        protected int BlockSizeValue
        {
            get
            {
                return this.blockSizeValue;
            }
            set
            {
                this.blockSizeValue = value;
            }
        }

        public string HashName
        {
            get
            {
                return this.m_hashName;
            }
            set
            {
                if (this.m_hashing)
                {
                    throw new CryptographicException("Cryptography_HashNameSet");
                }
                this.m_hashName = value;
                this.m_hash1 = HashAlgorithm.Create(this.m_hashName);
                this.m_hash2 = HashAlgorithm.Create(this.m_hashName);
            }
        }

        public override byte[] Key
        {
            get
            {
                return (byte[])base.KeyValue.Clone();
            }
            set
            {
                if (this.m_hashing)
                {
                    throw new CryptographicException("Cryptography_HashKeySet");
                }
                this.InitializeKey(value);
            }
        }
    }

    public class HMACSHA1 : HMAC
    {
        public HMACSHA1(byte[] key)
            : this(key, false)
        {
        }

        public HMACSHA1(byte[] key, bool useManagedSha1)
        {
            base.m_hashName = "SHA1";
            if (useManagedSha1)
            {
                base.m_hash1 = new SHA1Managed();
                base.m_hash2 = new SHA1Managed();
            }
            else
            {
                base.m_hash1 = new SHA1CryptoServiceProvider();
                base.m_hash2 = new SHA1CryptoServiceProvider();
            }
            base.HashSizeValue = 160;
            base.InitializeKey(key);
        }
    }

}

Dla zainteresowanych klasę zmieniłem z miejscach gdzie pobierana jest wartość HashAlgorithm-u (this.m_hash1.Hash) oraz wszędzie tam gdzie znajdują się opisy błedów (za leniwy jestem aby je wyciągać z Resources :) .

2 ResponsesLeave one →

  1. Ian

     /  2010-03-29

    Thank you very much – this was really helpful

Leave a Reply