/// <summary>
        /// Creates a counter signature, just like
        /// <c>sn -a IdentityPubKey.snk IdentityKey.snk SignaturePubKey.snk</c> can do.
        /// The public key <c>sn</c> prints is <paramref name="signaturePubKey"/>'s value.
        /// </summary>
        /// <param name="identityPubKey">Identity public key</param>
        /// <param name="identityKey">Identity strong name key pair</param>
        /// <param name="signaturePubKey">Signature public key</param>
        /// <returns>The counter signature</returns>
        public static byte[] CreateCounterSignature(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey)
        {
            var hash = AssemblyHash.Hash(signaturePubKey.CreatePublicKey(), identityPubKey.HashAlgorithm);

            using (var rsa = identityKey.CreateRSA()) {
                var    rsaFmt   = new RSAPKCS1SignatureFormatter(rsa);
                string hashName = identityPubKey.HashAlgorithm.GetName();
                rsaFmt.SetHashAlgorithm(hashName);
                var snSig = rsaFmt.CreateSignature(hash);
                Array.Reverse(snSig);
                return(snSig);
            }
        }
        /// <summary>
        /// Strong name hashes the .NET file
        /// </summary>
        /// <param name="hashAlg">Hash algorithm</param>
        /// <param name="snSigOffset">Strong name sig offset (relative to start of .NET PE file)</param>
        /// <param name="snSigSize">Size of strong name signature</param>
        /// <returns>The strong name hash of the .NET file</returns>
        byte[] StrongNameHashData(AssemblyHashAlgorithm hashAlg, long snSigOffset, uint snSigSize)
        {
            var reader = new BinaryReader(stream);

            snSigOffset += baseOffset;
            long snSigOffsetEnd = snSigOffset + snSigSize;

            using (var hasher = new AssemblyHash(hashAlg)) {
                var buffer = new byte[0x8000];

                // Hash the DOS header. It's defined to be all data from the start of
                // the file up to the NT headers.
                stream.Position = baseOffset + 0x3C;
                uint ntHeadersOffs = reader.ReadUInt32();
                stream.Position = baseOffset;
                hasher.Hash(stream, ntHeadersOffs, buffer);

                // Hash NT headers, but hash authenticode + checksum as 0s
                stream.Position += 6;
                int numSections = reader.ReadUInt16();
                stream.Position -= 8;
                hasher.Hash(stream, 0x18, buffer);                      // magic + FileHeader

                bool is32bit = reader.ReadUInt16() == 0x010B;
                stream.Position -= 2;
                int optHeaderSize = is32bit ? 0x60 : 0x70;
                if (stream.Read(buffer, 0, optHeaderSize) != optHeaderSize)
                {
                    throw new IOException("Could not read data");
                }
                // Clear checksum
                for (int i = 0; i < 4; i++)
                {
                    buffer[0x40 + i] = 0;
                }
                hasher.Hash(buffer, 0, optHeaderSize);

                const int imageDirsSize = 16 * 8;
                if (stream.Read(buffer, 0, imageDirsSize) != imageDirsSize)
                {
                    throw new IOException("Could not read data");
                }
                // Clear authenticode data dir
                for (int i = 0; i < 8; i++)
                {
                    buffer[4 * 8 + i] = 0;
                }
                hasher.Hash(buffer, 0, imageDirsSize);

                // Hash section headers
                long sectHeadersOffs = stream.Position;
                hasher.Hash(stream, (uint)numSections * 0x28, buffer);

                // Hash all raw section data but make sure we don't hash the location
                // where the strong name signature will be stored.
                for (int i = 0; i < numSections; i++)
                {
                    stream.Position = sectHeadersOffs + i * 0x28 + 0x10;
                    uint sizeOfRawData    = reader.ReadUInt32();
                    uint pointerToRawData = reader.ReadUInt32();

                    stream.Position = baseOffset + pointerToRawData;
                    while (sizeOfRawData > 0)
                    {
                        var pos = stream.Position;

                        if (snSigOffset <= pos && pos < snSigOffsetEnd)
                        {
                            uint skipSize = (uint)(snSigOffsetEnd - pos);
                            if (skipSize >= sizeOfRawData)
                            {
                                break;
                            }
                            sizeOfRawData   -= skipSize;
                            stream.Position += skipSize;
                            continue;
                        }

                        if (pos >= snSigOffsetEnd)
                        {
                            hasher.Hash(stream, sizeOfRawData, buffer);
                            break;
                        }

                        uint maxLen = (uint)Math.Min(snSigOffset - pos, sizeOfRawData);
                        hasher.Hash(stream, maxLen, buffer);
                        sizeOfRawData -= maxLen;
                    }
                }

                return(hasher.ComputeHash());
            }
        }