Wednesday 1 August 2012

Load assembly from file and enforce strong name verification

Loading an external assembly in your application context may represent an important security hazard. An assembly loaded from  Assembly.LoadFrom will not check for a strong name.

During the development process , you probably don't want everyone to have your SN key pair and it's a lot easier to delay sign your assembly.

Here is a way to check your public key token with an expected byte array representing a known public key token.


        public static bool CheckToken(string assembly, byte[] expectedToken)
        {
            if (assembly == null)
                throw new ArgumentNullException("assembly");
            if (expectedToken == null)
                throw new ArgumentNullException("expectedToken");

            try
            {
                // Get the public key token of the given assembly
                Assembly asm = Assembly.LoadFrom(assembly);
                byte[] asmToken = asm.GetName().GetPublicKeyToken();

                // Compare it to the given token
                if (asmToken.Length != expectedToken.Length)
                    return false;

                for (int i = 0; i < asmToken.Length; i++)
                    if (asmToken[i] != expectedToken[i])
                        return false;

                return true;
            }
            catch (System.IO.FileNotFoundException)
            {
                // couldn't find the assembly
                return false;
            }
            catch (BadImageFormatException)
            {
                // the given file couldn't get through the loader
                return false;
            }
        }
This is fine but it will not allow you to check if you have a strong name signed, and you cant make sure you are not delay signed.

A solution would be to import external funcion from mscoree.dll that will make this check for you. This is a good approach to make sure you dont rely on the standard .net check that could be easily bypassed (http://msdn.microsoft.com/en-us/library/cc713694.aspx)

[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerification);

        public static bool CheckStrongName(string assembly)
        {
            // check the signature first
            bool notForced = false;
            bool verified = StrongNameSignatureVerificationEx(assembly, false, ref notForced);

            byte[] your_key = null;

     //Check different key for debug / release ..
            #if DEBUG
            your_key = new byte[] { 0x11, 0x11, 0x11, 0x38, 0x12, 0x11, 0x61, 0xF4 };
            #else
            your_key = new byte[] { 0x11, 0x11, 0x11, 0x38, 0x12, 0x11, 0x61, 0xF4 };
            #endif

            bool isSecureAsm = CheckToken(assembly, your_key);

            if (isSecureAsm  && verified && notForced)
                return true; //signed assembly
            else if (isSecureAsm && verified && !notForced)
                throw new InvalidOperationException("Delay signed assembly");
            else if (isSecureAsm && !verified)
                throw new InvalidOperationException("Assembly modified since signing");
            else
                throw new InvalidOperationException("Not a valid assembly"); 
        }
By setting the notForced parameter to false, you will check for a correct assembly no matter what has been set as a bypass.

No comments:

Post a Comment