Example #1
0
        public void VerifyStrongName(SignatureVerificationResult svr, PortableExecutableHeader portableExecutableHeader)
        {
            if (portableExecutableHeader.IsManagedCode)
            {
                svr.IsNativeImage = !portableExecutableHeader.IsILImage;
                // NGEN/CrossGen don't preserve StrongName signatures.
                if (!svr.IsNativeImage)
                {
                    bool wasVerified = false;
                    int  hresult     = StrongName.ClrStrongName.StrongNameSignatureVerificationEx(svr.FullPath, fForceVerification: true, pfWasVerified: out wasVerified);
                    svr.IsStrongNameSigned = hresult == StrongName.S_OK;
                    svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailSignedStrongName, svr.IsStrongNameSigned);

                    if (hresult != StrongName.S_OK)
                    {
                        svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailHResult, hresult);
                    }
                    else
                    {
                        string publicToken;
                        hresult = StrongName.GetStrongNameTokenFromAssembly(svr.FullPath, out publicToken);
                        if (hresult == StrongName.S_OK)
                        {
                            svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailPublicKeyToken, publicToken);
                        }
                    }
                }
                else
                {
                    svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailNativeImage);
                }
            }
        }
Example #2
0
        public override SignatureVerificationResult VerifySignature(string path, string parent, string virtualPath)
        {
            SignatureVerificationResult svr = base.VerifySignature(path, parent, virtualPath);

            if (VerifyRecursive)
            {
                // MSU is just a CAB file really
                Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, svr.TempPath);
                CabInfo cabInfo = new CabInfo(path);
                cabInfo.Unpack(svr.TempPath);

                foreach (string cabFile in Directory.EnumerateFiles(svr.TempPath))
                {
                    string cabFileFullName = Path.GetFullPath(cabFile);
                    SignatureVerificationResult cabEntryResult = VerifyFile(cabFile, svr.Filename, Path.Combine(svr.VirtualPath, cabFile), cabFileFullName);

                    // Tag the full path into the result detail
                    cabEntryResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, cabFileFullName);

                    svr.NestedResults.Add(cabEntryResult);
                }

                DeleteDirectory(svr.TempPath);
            }

            return(svr);
        }
Example #3
0
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            if (VerifyJarSignatures)
            {
                var svr = new SignatureVerificationResult(path, parent);

                try
                {
                    JarError.ClearErrors();
                    var jarFile = new JarFile(path);
                    svr.IsSigned = jarFile.IsSigned();

                    if (!svr.IsSigned && JarError.HasErrors())
                    {
                        svr.AddDetail(DetailKeys.Error, JarError.GetLastError());
                    }
                    else
                    {
                        foreach (Timestamp timestamp in jarFile.Timestamps)
                        {
                            svr.AddDetail(DetailKeys.Misc, SignCheckResources.DetailTimestamp, timestamp.SignedOn, timestamp.SignatureAlgorithm);
                        }

                        IEnumerable <Timestamp> invalidTimestamps = from ts in jarFile.Timestamps
                                                                    where !ts.IsValid
                                                                    select ts;

                        foreach (Timestamp ts in invalidTimestamps)
                        {
                            svr.AddDetail(DetailKeys.Error, SignCheckResources.DetailTimestampOutisdeCertValidity, ts.SignedOn, ts.EffectiveDate, ts.ExpiryDate);
                            svr.IsSigned = false;
                        }
                    }

                    svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned);
                }
                catch (Exception e)
                {
                    svr.AddDetail(DetailKeys.Error, e.Message);
                }

                return(svr);
            }

            return(SignatureVerificationResult.UnsupportedFileTypeResult(path, parent));
        }
Example #4
0
        /// <summary>
        /// Verify the signature of a single file.
        /// </summary>
        /// <param name="path">The path of the file on disk to verify.</param>
        /// <param name="parent">The name of parent container, e.g. an MSI or VSIX. Can be null when there is no parent container.</param>
        /// <param name="containerPath">The path of the file in the container. This may differ from the path on disk as containers are flattened. It's
        /// primarily intended to help with exclusions and report more readable names.</param>
        /// <returns>The verification result.</returns>
        public SignatureVerificationResult VerifyFile(string path, string parent, string virtualPath, string containerPath)
        {
            Log.WriteMessage(LogVerbosity.Detailed, String.Format(SignCheckResources.ProcessingFile, Path.GetFileName(path), String.IsNullOrEmpty(parent) ? SignCheckResources.NA : parent));

            FileVerifier fileVerifier       = GetFileVerifier(path);
            SignatureVerificationResult svr = fileVerifier.VerifySignature(path, parent, virtualPath);

            svr.IsDoNotSign = Exclusions.IsDoNotSign(path, parent, virtualPath, containerPath);

            if ((svr.IsDoNotSign) && (svr.IsSigned))
            {
                // Report errors if a DO-NOT-SIGN file is signed.
                svr.AddDetail(DetailKeys.Error, SignCheckResources.DetailDoNotSignFileSigned, svr.Filename);
            }

            if ((!svr.IsDoNotSign) && (!svr.IsSigned))
            {
                svr.IsExcluded = Exclusions.IsExcluded(path, parent, svr.VirtualPath, containerPath);

                if ((svr.IsExcluded))
                {
                    svr.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded);
                }
            }

            if (GenerateExclusion)
            {
                svr.ExclusionEntry = String.Join(";", String.Join("|", path, containerPath), parent, String.Empty);
                Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, svr.Filename, svr.ExclusionEntry);
            }

            // Include the full path for top-level files
            if (String.IsNullOrEmpty(parent))
            {
                svr.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, svr.FullPath);
            }

            if (!String.IsNullOrEmpty(virtualPath))
            {
                svr.AddDetail(DetailKeys.File, SignCheckResources.DetailVirtualPath, svr.VirtualPath);
            }

            return(svr);
        }
Example #5
0
        protected SignatureVerificationResult VerifyAuthentiCode(string path, string parent)
        {
            var  svr     = new SignatureVerificationResult(path, parent);
            uint hresult = AuthentiCode.IsSigned(path);

            svr.IsAuthentiCodeSigned = hresult == 0;
            svr.IsSigned             = svr.IsAuthentiCodeSigned;

            // Log non-zero HRESULTs
            if (hresult != 0)
            {
                string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
                svr.AddDetail(DetailKeys.Error, String.Format(SignCheckResources.ErrorHResult, hresult, errorMessage));
            }

            // TODO: Should only check if there is a signature, even if it's invalid
            if (VerifyAuthenticodeTimestamps)
            {
                try
                {
                    svr.Timestamps = AuthentiCode.GetTimestamps(path).ToList();

                    foreach (Timestamp timestamp in svr.Timestamps)
                    {
                        svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailTimestamp, timestamp.SignedOn, timestamp.SignatureAlgorithm);
                        svr.IsAuthentiCodeSigned &= timestamp.IsValid;
                    }
                }
                catch
                {
                    svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailTimestampError);
                    svr.IsSigned = false;
                }
            }
            else
            {
                svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailTimestampSkipped);
            }

            svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailSignedAuthentiCode, svr.IsAuthentiCodeSigned);

            return(svr);
        }
Example #6
0
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            SignatureVerificationResult svr = new SignatureVerificationResult(path, parent);
            string fullPath = svr.FullPath;

            svr.IsSigned = IsSigned(fullPath);
            svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned);
            VerifyContent(svr);

            return(svr);
        }
Example #7
0
        /// <summary>
        /// Creates a SignatureVerificationResult for an excluded file type or file extension.
        /// </summary>
        /// <param name="path">The path to the excluded file.</param>
        /// <param name="parent">The parent container of the excluded file</param>
        /// <returns></returns>
        public static SignatureVerificationResult ExcludedFileResult(string path, string parent)
        {
            var signatureVerificationResult = new SignatureVerificationResult(path, parent)
            {
                IsExcluded = true
            };

            signatureVerificationResult.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded);

            return(signatureVerificationResult);
        }
Example #8
0
        /// <summary>
        /// Creates a SignatureVerificationResult for an unsupported file type or file extension.
        /// </summary>
        /// <param name="path">The path to the file that is unsupported</param>
        /// <returns>A SignatureVerificationResult indicating the file is unsupported..</returns>
        public static SignatureVerificationResult UnsupportedFileTypeResult(string path, string parent)
        {
            var signatureVerificationResult = new SignatureVerificationResult(path, parent)
            {
                IsSkipped = true
            };

            signatureVerificationResult.AddDetail(DetailKeys.File, SignCheckResources.DetailSkippedUnsupportedFileType);

            return(signatureVerificationResult);
        }
Example #9
0
        /// <summary>
        /// Verify the contents of a zip-based archive and add the results to the container result.
        /// </summary>
        /// <param name="svr">The container result</param>
        protected void VerifyContent(SignatureVerificationResult svr)
        {
            if (VerifyRecursive)
            {
                using (ZipArchive zipArchive = ZipFile.OpenRead(svr.FullPath))
                {
                    string tempPath = svr.TempPath;
                    CreateDirectory(tempPath);
                    Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, tempPath);
                    Dictionary <string, string> archiveMap = new Dictionary <string, string>();

                    foreach (ZipArchiveEntry archiveEntry in zipArchive.Entries)
                    {
                        // Generate an alias for the actual file that has the same extension. We do this to avoid path too long errors so that
                        // containers can be flattened.
                        string directoryName = Path.GetDirectoryName(archiveEntry.FullName);
                        string hashedPath    = String.IsNullOrEmpty(directoryName) ? Utils.GetHash(@".\", HashAlgorithmName.SHA256.Name) :
                                               Utils.GetHash(directoryName, HashAlgorithmName.SHA256.Name);
                        string extension = Path.GetExtension(archiveEntry.FullName);

                        // CAB files cannot be aliased since they're referred to from the Media table inside the MSI
                        string aliasFileName = String.Equals(extension.ToLowerInvariant(), ".cab") ? Path.GetFileName(archiveEntry.FullName) :
                                               Utils.GetHash(archiveEntry.FullName, HashAlgorithmName.SHA256.Name) + Path.GetExtension(archiveEntry.FullName); // lgtm [cs/zipslip] Archive from trusted source
                        string aliasFullName = Path.Combine(tempPath, hashedPath, aliasFileName);

                        if (File.Exists(aliasFullName))
                        {
                            Log.WriteMessage(LogVerbosity.Normal, SignCheckResources.FileAlreadyExists, aliasFullName);
                        }
                        else
                        {
                            CreateDirectory(Path.GetDirectoryName(aliasFullName));
                            archiveEntry.ExtractToFile(aliasFullName); // lgtm [cs/microsoft/zipslip] Archive from trusted source
                            archiveMap[archiveEntry.FullName] = aliasFullName;
                        }
                    }

                    // We can only verify once everything is extracted. This is mainly because MSIs can have mutliple external CAB files
                    // and we need to ensure they are extracted before we verify the MSIs.
                    foreach (string fullName in archiveMap.Keys)
                    {
                        SignatureVerificationResult result = VerifyFile(archiveMap[fullName], svr.Filename,
                                                                        Path.Combine(svr.VirtualPath, fullName), fullName);

                        // Tag the full path into the result detail
                        result.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, fullName);
                        svr.NestedResults.Add(result);
                    }
                    DeleteDirectory(tempPath);
                }
            }
        }
Example #10
0
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            if (VerifyXmlSignatures)
            {
                X509Certificate2 xmlCertificate;
                var svr = new SignatureVerificationResult(path, parent);
                svr.IsSigned = IsSigned(svr.FullPath, out xmlCertificate);
                svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned);
                return(svr);
            }

            return(SignatureVerificationResult.UnsupportedFileTypeResult(path, parent));
        }
Example #11
0
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            SignatureVerificationResult svr = VerifyAuthentiCode(path, parent);

            if (FinalizeResult)
            {
                // Derived class that need to evaluate additional properties and results must
                // set FinalizeResult = false, otherwise the Signed result can be logged multiple times.
                svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned);
            }

            return(svr);
        }
        /// <summary>
        /// Verify whether the portable executable contains an AuthentiCode signature and optionally check the
        /// StrongName signature if it is enabled and the file represents a managed code executable.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            // Defer to the base implementation to check the AuthentiCode signature.
            SignatureVerificationResult svr = base.VerifySignature(path, parent);

            PEHeader = new PortableExecutableHeader(svr.FullPath);

            if (VerifyStrongNameSignature)
            {
                VerifyStrongName(svr, PEHeader);
            }

            svr.IsSigned = svr.IsAuthentiCodeSigned & ((svr.IsStrongNameSigned) || (!VerifyStrongNameSignature) || svr.IsNativeImage);
            svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned);

            return(svr);
        }
Example #13
0
        /// <summary>
        /// Checks whether the specified file is excluded from verification and updates the SignatureVerificationResult.
        /// If <see cref="GenerateExclusion"/> is true, an exclusion entry is also generated.
        /// </summary>
        /// <param name="result">The SignatureVerificationResult to update.</param>
        /// <param name="path">The path of the file on disk.</param>
        /// <param name="parent">The parent (container) of the file.</param>
        /// <param name="containerPath">The path of the file in the container. May be null if the file is not embedded in a container.</param>
        protected void CheckAndUpdateExclusions(ref SignatureVerificationResult result, string path, string parent, string containerPath)
        {
            if (Exclusions.Count > 0)
            {
                result.IsExcluded = Exclusions.IsParentExcluded(parent) || Exclusions.IsFileExcluded(path) || Exclusions.IsFileExcluded(containerPath);

                if (result.IsExcluded)
                {
                    result.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded);
                }
            }
            if (GenerateExclusion)
            {
                result.ExclusionEntry = String.Join(";", String.Join("|", path, containerPath), parent, String.Empty);
                Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, result.Filename, result.ExclusionEntry);
            }
        }
Example #14
0
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            var    svr      = SignatureVerificationResult.UnsupportedFileTypeResult(path, parent);
            string fullPath = svr.FullPath;

            svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, SignCheckResources.NA);

            if (VerifyRecursive)
            {
                using (ZipArchive zipArchive = ZipFile.OpenRead(fullPath))
                {
                    string tempPath = svr.TempPath;
                    CreateDirectory(tempPath);
                    Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, tempPath);

                    foreach (ZipArchiveEntry archiveEntry in zipArchive.Entries)
                    {
                        // Generate an alias for the actual file that has the same extension. We do this to avoid path too long errors so that
                        // containers can be flattened
                        string aliasFileName = Utils.GetHash(archiveEntry.FullName, HashAlgorithmName.MD5.Name) + Path.GetExtension(archiveEntry.FullName);
                        string aliasFullName = Path.Combine(tempPath, aliasFileName);

                        if (File.Exists(aliasFullName))
                        {
                            Log.WriteMessage(LogVerbosity.Normal, SignCheckResources.FileAlreadyExists, aliasFullName);
                        }
                        else
                        {
                            archiveEntry.ExtractToFile(aliasFullName);
                            SignatureVerificationResult archiveEntryResult = VerifyFile(aliasFullName, svr.Filename, archiveEntry.FullName);

                            // Tag the full path into the result detail
                            archiveEntryResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, archiveEntry.FullName);
                            svr.NestedResults.Add(archiveEntryResult);
                        }
                    }

                    DeleteDirectory(tempPath);
                }
            }

            return(svr);
        }
Example #15
0
        /// <summary>
        /// Verify the signature of a single file.
        /// </summary>
        /// <param name="path">The path of the file on disk to verify.</param>
        /// <param name="parent">The name of parent container, e.g. an MSI or VSIX. Can be null when there is no parent container.</param>
        /// <param name="containerPath">The path of the file in the container. This may differ from the path on disk as containers are flattened. It's
        /// primarily intended to help with exclusions and report more readable names.</param>
        /// <returns>The verification result.</returns>
        public SignatureVerificationResult VerifyFile(string path, string parent, string containerPath)
        {
            Log.WriteMessage(LogVerbosity.Detailed, String.Format(SignCheckResources.ProcessingFile, Path.GetFileName(path), String.IsNullOrEmpty(parent) ? SignCheckResources.NA : parent));

            FileVerifier fileVerifier       = GetFileVerifier(path);
            SignatureVerificationResult svr = fileVerifier.VerifySignature(path, parent);

            svr.IsExcluded = Exclusions.IsExcluded(path, parent, containerPath);

            if (GenerateExclusion)
            {
                svr.ExclusionEntry = String.Join(";", String.Join("|", path, containerPath), parent, String.Empty);
                Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, svr.Filename, svr.ExclusionEntry);
            }

            // Include the full path for top-level files
            if (String.IsNullOrEmpty(parent))
            {
                svr.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, svr.FullPath);
            }

            return(svr);
        }
Example #16
0
        public override SignatureVerificationResult VerifySignature(string path, string parent)
        {
            var    svr      = new SignatureVerificationResult(path, parent);
            string fullPath = svr.FullPath;

            svr.IsSigned = IsSigned(fullPath, svr);
            svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned);

            if (VerifyRecursive)
            {
                using (ZipArchive zipArchive = ZipFile.OpenRead(fullPath))
                {
                    string tempPath = svr.TempPath;
                    CreateDirectory(tempPath);

                    foreach (ZipArchiveEntry archiveEntry in zipArchive.Entries)
                    {
                        // Generate an alias for the actual file, but keep the original extension. This should limit the chances of running
                        // into 'path too long' errors when extracting the files.
                        string aliasFileName = Utils.GetHash(archiveEntry.FullName, HashAlgorithmName.MD5.Name) + Path.GetExtension(archiveEntry.FullName);
                        string aliasFullName = Path.Combine(tempPath, aliasFileName);

                        archiveEntry.ExtractToFile(aliasFullName);
                        SignatureVerificationResult archiveEntryResult = VerifyFile(aliasFullName, svr.Filename, archiveEntry.FullName);

                        // Tag the full path into the result detail
                        archiveEntryResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, archiveEntry.FullName);
                        svr.NestedResults.Add(archiveEntryResult);
                    }

                    DeleteDirectory(tempPath);
                }
            }

            return(svr);
        }
Example #17
0
        private bool IsSigned(string path, SignatureVerificationResult result)
        {
            PackageDigitalSignature packageSignature = null;

            using (var vsixStream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                var vsixPackage      = Package.Open(vsixStream);
                var signatureManager = new PackageDigitalSignatureManager(vsixPackage);

                if (!signatureManager.IsSigned)
                {
                    return(false);
                }

                if (signatureManager.Signatures.Count() != 1)
                {
                    return(false);
                }

                if (signatureManager.Signatures[0].SignedParts.Count != vsixPackage.GetParts().Count() - 1)
                {
                    return(false);
                }

                packageSignature = signatureManager.Signatures[0];

                // Retrieve the timestamp
                Timestamp timestamp;
                if (!TryGetTimestamp(packageSignature, out timestamp))
                {
                    // Timestamp is either invalid or not present
                    result.AddDetail(DetailKeys.Error, SignCheckResources.ErrorInvalidOrMissingTimestamp);
                    return(false);
                }

                // Update the result with the timestamp detail
                result.AddDetail(DetailKeys.Signature, String.Format(SignCheckResources.DetailTimestamp, timestamp.SignedOn, timestamp.SignatureAlgorithm));

                // Verify the certificate chain
                X509Certificate2 certificate = new X509Certificate2(packageSignature.Signer);

                X509Chain certChain = new X509Chain();
                certChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
                certChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;

                // If the certificate has expired, but the VSIX was signed prior to expiration
                // we can ignore invalid time policies.
                bool certExpired = DateTime.Now > certificate.NotAfter;

                if (timestamp.IsValid && certExpired)
                {
                    certChain.ChainPolicy.VerificationFlags |= X509VerificationFlags.IgnoreNotTimeValid;
                }

                if (!certChain.Build(certificate))
                {
                    result.AddDetail(DetailKeys.Error, SignCheckResources.DetailErrorFailedToBuildCertChain);
                    return(false);
                }

                result.AddDetail(DetailKeys.Misc, SignCheckResources.DetailCertChainValid);
            }

            return(true);
        }
Example #18
0
        public override SignatureVerificationResult VerifySignature(string path, string parent, string virtualPath)
        {
            SignatureVerificationResult svr = base.VerifySignature(path, parent, virtualPath);

            if (VerifyRecursive)
            {
                CreateDirectory(svr.TempPath);

                // TODO: Fix for MSIs with external CABs that are not present.
                using (var installPackage = new InstallPackage(svr.FullPath, DatabaseOpenMode.Transact, sourceDir: null, workingDir: svr.TempPath))
                {
                    InstallPathMap files         = installPackage.Files;
                    var            originalFiles = new Dictionary <string, string>();

                    // Flatten the files to avoid path too long errors. We use the File column and extension to create a unique file
                    // and record the original, relative MSI path in the result.
                    foreach (string key in installPackage.Files.Keys)
                    {
                        originalFiles[key] = installPackage.Files[key].TargetPath;
                        string name       = key + Path.GetExtension(installPackage.Files[key].TargetName);
                        string targetPath = Path.Combine(svr.TempPath, name);
                        installPackage.Files[key].TargetName = name;
                        installPackage.Files[key].SourceName = name;
                        installPackage.Files[key].SourcePath = targetPath;
                        installPackage.Files[key].TargetPath = targetPath;
                    }

                    try
                    {
                        Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, svr.TempPath);
                        installPackage.ExtractFiles(installPackage.Files.Keys);

                        foreach (string key in installPackage.Files.Keys)
                        {
                            SignatureVerificationResult packageFileResult = VerifyFile(installPackage.Files[key].TargetPath, svr.Filename, Path.Combine(svr.VirtualPath, originalFiles[key]), containerPath: null);
                            packageFileResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, originalFiles[key]);
                            svr.NestedResults.Add(packageFileResult);
                        }
                    }
                    catch (Exception e)
                    {
                        Log.WriteError(e.Message);
                    }
                }

                // Extract files from the Binary table - this is where items such as custom actions are stored.
                try
                {
                    using (var installDatabase = new Database(svr.FullPath, DatabaseOpenMode.ReadOnly))
                        using (View view = installDatabase.OpenView("SELECT `Name`, `Data` FROM `Binary`"))
                        {
                            view.Execute();

                            foreach (Record record in view)
                            {
                                string binaryFile     = (string)record["Name"];
                                string binaryFilePath = Path.Combine(svr.TempPath, binaryFile);
                                StructuredStorage.SaveStream(record, svr.TempPath);
                                SignatureVerificationResult binaryStreamResult = VerifyFile(binaryFilePath, svr.Filename, Path.Combine(svr.VirtualPath, binaryFile), containerPath: null);
                                binaryStreamResult.AddDetail(DetailKeys.Misc, SignCheckResources.FileExtractedFromBinaryTable);
                                svr.NestedResults.Add(binaryStreamResult);
                                record.Close();
                            }
                        }
                }
                catch (Exception e)
                {
                    Log.WriteError(e.Message);
                }

                DeleteDirectory(svr.TempPath);
            }

            return(svr);
        }