65.9K
CodeProject 正在变化。 阅读更多。
Home

校验和验证

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2011 年 9 月 19 日

CPOL

3分钟阅读

viewsIcon

55061

downloadIcon

1477

循环遍历文件夹/子文件夹中的所有文件,并运行存储在数据库中的校验和。 通过电子邮件发送差异和结果。

引言

我们越来越看到存储、存档和保存数字资产的重要性,可能需要几十年甚至几个世纪。数字资产管理系统主要侧重于摄取和用户访问,但资产本身呢?人们花费大量时间创建视频、图像和其他媒体。因此,我们应该定期验证数字资产的数据完整性并在出现差异时发出警报,这是有道理的。下面的程序使用 SHA-256 校验和来执行此操作。这是一个为 .NET 4.0 编写的控制台应用程序。

背景

必备组件

  • MySQL 服务器(和 GUI 工具)* 可在 www.mysql.com 上获得
  • MySQL Connector/NET
  • *数据库平台是 MySQL,但它可以轻松地与 SQL Server 一起使用。我喜欢 MySQL Connector/NET 的一点是,使用数据库的语法几乎与 MS SQL Server 完全相同(即,SqlCommand 变为 MySqlCommand)。

  • .NET 4.0/Visual Studio 2010。
  • 用于通过 SMTP 验证和发送电子邮件的 Gmail 帐户或其他帐户。

用例

核心用例是检查文件夹/子文件夹中的所有文件,并验证文件的校验和值是否已更改。 如果已更改,则应通过电子邮件通知技术人员或企业所有者。 下面的程序执行此操作; 它还报告文件总数、未更改的文件数和新文件数。

该程序将摘要报告写入电子邮件正文,并附加两个包含详细信息的电子表格 (.csv)。

程序的某些部分可以通过 app.config 文件进行配置,即

  • 要验证的文件夹/子文件夹
  • 电子邮件发送地址
  • 电子邮件发件人地址
  • 文件附件的名称和位置
  • 日志文件的名称和位置

该示例使用的文件夹结构如下所示

folders.jpg

使用代码

首先我们需要一个数据库。 附加的文件名为 DRMC.sql,可以将其还原到 MySQL,或者可以在 Notepad++ 中打开该文件以查看 1 个表、1 个视图和 5 个存储过程。 该程序假定数据库名为“drmc”。

整个程序都在下载文件中。 以下是一些有用的区域需要指出

通过存储过程连接和使用 MySQL 数据库并非总是那么简单。 存储过程包含一些 OUT 参数,以便获得总计数。

下面的代码演示了如何枚举文件夹和子文件夹中的所有文件,运行校验和并将结果插入到表中

// Connect to database
string m_conn = 
  ConfigurationManager.ConnectionStrings["MySqlConnectionString"].ConnectionString;
MySqlConnection conn = new MySqlConnection(m_conn);
conn.Open();

try
{
    // Get the checksum and path for all files in directory.
    // Use stored proc to insert data. Log results and errors.
    MySqlCommand cmd = new MySqlCommand("drmc.proc_checksum", conn);
    cmd.CommandType = CommandType.StoredProcedure;

    string[] m_files = Directory.GetFiles(m_path, "*.*", 
                                 SearchOption.AllDirectories);

    foreach (string m_file in m_files)
    {
        m_filename = Path.GetFileName(m_file);
        cmd.Parameters.Clear();
        cmd.Parameters.Add(new MySqlParameter(@"M_FILEPATH", 
            MySqlDbType.VarChar) { Value = m_file.Replace("\\", "\\\\") });
        cmd.Parameters.Add(new MySqlParameter(@"M_FILENAME", 
            MySqlDbType.VarChar) { Value = m_filename });
        cmd.Parameters.Add(new MySqlParameter(@"M_SHA256", 
            MySqlDbType.VarChar) { Value = GetChecksum(m_file).ToString() });
        cmd.ExecuteNonQuery();

        using (StreamWriter sw = File.AppendText(m_results))
        {
            Logger.LogMessage("File " + m_file + 
                   " inserted in database.", sw);
            sw.Close();
        }
    }
  ...

了解如何使用 MySqlDataAdapter 以及如何从 ExecuteNonQuery 获取 OUT 参数也很有用。 下面的代码片段演示了如何执行此操作。

// Get number and listing of new files
MySqlCommand cmd_newfiles = new MySqlCommand("drmc.proc_newfiles", conn);
cmd_newfiles.CommandType = CommandType.StoredProcedure;

cmd_newfiles.Parameters.AddWithValue("@M_NEWCOUNT", MySqlDbType.Int32);
cmd_newfiles.Parameters["@M_NEWCOUNT"].Direction = ParameterDirection.Output;
cmd_newfiles.ExecuteNonQuery();

Console.WriteLine("New files: " + cmd_newfiles.Parameters["@M_NEWCOUNT"].Value);
Console.WriteLine("\r\n");

string str_newfiles = cmd_newfiles.Parameters["@M_NEWCOUNT"].Value.ToString();

MySqlDataAdapter sda_newfiles = new MySqlDataAdapter(cmd_newfiles);
DataSet ds_newfiles = new DataSet();
ds_newfiles.DataSetName = "New Files";
sda_newfiles.Fill(ds_newfiles);
sda_newfiles.Dispose();

最后,我们开始使用结果构建电子邮件并发送它,包括使用 FileGenerator 类创建的两个附件。

// Begin creating email content and attachments
DataTable dt_newfiles = ds_newfiles.Tables[0];
DataTable dt_changedfiles = ds_changedfiles.Tables[0];
DataTable dt_changedfiles1 = ds_changedfiles.Tables[1];

FileGenerator.CreateFile(dt_changedfiles, m_changedfiles).ToString();

string m_emailbody = "This e-mail is a summary of checksum file integrity " + 
                     "for files located here: \r\n\r\n" + m_path + "\r\n\r\n";
m_emailbody = m_emailbody + "There are a total of " + 
              str_totalfiles + " files. \r\n\r\n";
m_emailbody = m_emailbody + 
              "The file location, file name, and checksum are the same for " + 
              str_samefiles + " files. \r\n\r\n";
m_emailbody = m_emailbody + "There are " + str_newfiles + 
              " new files. These are listed below, if any.  " + 
              "Detailed information is in the attached " + 
              "file checksum_new_files.csv\r\n\r\n";
m_emailbody = m_emailbody + "There are " + str_changedfiles + 
              " files where the CHECKSUM HAS BEEN CHANGED. " + 
              "The integrity of the file is in doubt, " + 
              "or it has been changed by a user.  " + 
              "The files are listed below, if any.  Detailed information " + 
              "is in the attached file checksum_changed_files.csv\r\n\r\n";

string m_emailnewbody = "New Files: \r\n" + 
       FileGenerator.CreateFile(dt_newfiles, m_newfiles).ToString();
string m_emailchangedbody = "Changed Files: \r\n" + 
       FileGenerator.CreateFile(dt_changedfiles1).ToString();

m_emailbody = m_emailbody + m_emailnewbody + "\r\n" + m_emailchangedbody;

#endregion

#region Email configuration and send

var client = new SmtpClient("smtp.gmail.com", 587)
{
	Credentials = new NetworkCredential("gmailuserhere", 
                      "gmailpasswordhere"),
	EnableSsl = true
};

MailMessage m_message = new MailMessage(
m_notification_to,
m_notification_from,
m_notification_title + DateTime.Today.ToShortDateString(),
m_emailbody);

//...see downloads for code on creating full attchments

m_message.Attachments.Add(m_new_attachment);
m_message.Attachments.Add(m_changed_attachment);

client.Send(m_message);

代码中的实际工作由下面的函数执行。 这段代码几乎逐字来自 Jeff Barnes, MS MVP, 和他的博客文章: http://jeffbarnes.net/blog/post/2007/01/12/File-Checksum-using-NET.aspx

private static string GetChecksum(string m_fileinput)
{
    try
    {
        string m_checksum;
        using (FileStream stream = File.OpenRead(m_fileinput))
        {
            SHA256Managed sha = new SHA256Managed();
            byte[] checksum = sha.ComputeHash(stream);
            m_checksum = 
              BitConverter.ToString(checksum).Replace("-", String.Empty);
        }

        return m_checksum;

    }
    catch (Exception ex)
    {
        using (StreamWriter swerr = File.AppendText(m_errors))
        {
            Logger.LogMessage(ex.Message.ToString(), swerr);
            swerr.Close();
        }

        return "unable to retrieve checksum";

    }
}

您可能遇到的 SHA-256 问题之一是,由于其扩展的算法,创建校验和所需的时间比 MD5 长。 MD5 将在不到一半的时间内运行,如果您有 TB 级的数据需要扫描并且您更关心校验和值而不是加密,这一点非常重要。 下面的代码片段演示了如何改用 MD5

MD5 m_md5 = new MD5CryptoServiceProvider();
//SHA256Managed sha = new SHA256Managed();
byte[] checksum = m_md5.ComputeHash(stream);
m_checksum = BitConverter.ToString(checksum).Replace("-", String.Empty);

历史

  • 2011 年 9 月 23 日 - 添加了 MD5 注释,以便更快地扫描文件。
© . All rights reserved.