/// <summary>Parse a revocation message.</summary>
        public UserRevocationMessage(Certificate cacert, MemBlock data)
        {
            _data = data;

            int pos    = 0;
            int length = 0;

            Username = AdrConverter.Deserialize(data, pos, out length) as string;
            pos     += length;
            // Random number to reduce likelihood of malicious duplication of messages
            NumberSerializer.ReadInt(data, pos);
            pos += 4;
            // Verify that there is a date contained therein, perhaps we should verify the time
            new DateTime(NumberSerializer.ReadLong(data, pos));
            pos      += 8;
            Signature = new byte[data.Length - pos];
            data.Slice(pos).CopyTo(Signature, 0);

            // hash the data
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();

            Hash = sha1.ComputeHash(data, 0, data.Length - Signature.Length);

            if (!cacert.PublicKey.VerifyHash(Hash,
                                             CryptoConfig.MapNameToOID("SHA1"), Signature))
            {
                throw new Exception("Invalid UserRevocationMessage signature");
            }
        }
        /// <summary>Parses web data and updates the revoked users hashtable if
        /// successful</summary>
        protected void UpdateRl(byte[] data)
        {
            // message is length (4) + date (8) + data (variable) + hash (~20)
            int length = data.Length;

            if (length < 12)
            {
                throw new Exception("No data?  Didn't get enough data...");
            }

            length = NumberSerializer.ReadInt(data, 0);
            DateTime date = new DateTime(NumberSerializer.ReadLong(data, 4));

            // warn the user that this is an old revocation list, maybe there is an attack
            if (date < DateTime.UtcNow.AddHours(-24))
            {
                ProtocolLog.WriteIf(IpopLog.GroupVPN, "Revocation list is over 24 hours old");
            }

            // Weird, data length is longer than the data we got
            if (length > data.Length - 12)
            {
                throw new Exception("Missing data?  Didn't get enough data...");
            }

            // hash the data and verify the signature
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();

            byte[] hash      = sha1.ComputeHash(data, 4, length);
            byte[] signature = new byte[data.Length - 4 - length];
            Array.Copy(data, 4 + length, signature, 0, signature.Length);

            if (!_ca_cert.PublicKey.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature))
            {
                throw new Exception("Invalid signature!");
            }

            // convert the data to an array list as it was sent to us
            MemBlock mem = MemBlock.Reference(data, 12, length - 8);

            ArrayList rl = AdrConverter.Deserialize(mem) as ArrayList;

            if (rl == null)
            {
                throw new Exception("Data wasn't a list...");
            }

            // convert it into a hashtable for O(1) look ups
            Hashtable ht = new Hashtable();

            foreach (string username in rl)
            {
                ht[username] = true;
            }

            Interlocked.Exchange(ref _revoked_users, ht);
        }