zoukankan      html  css  js  c++  java
  • 【C#】聊聊不需要记密码的密码管理补充帖 —— 具体实现

    开篇第一句话,就是“小白继续,有实际经验的兄弟们可以洗洗睡了”,因为这个 Lite 版是个实验性的实现,也由于水平原因源码不忍直视,所以如果你坚持看完了,请留下宝贵意见。

    以下,干货:


    基本模式:

      程序使用“钥匙”对用户需要加密的内容进行自动加密,加密完成后,导出数据和“钥匙”为文件,用户自行保管文件即可。解密时,告诉程序要用哪个钥匙,程序无需用户任何输入即可自动完成解密,解密失败也不会影响用户数据。

    此模式好处:

      用户无需关心数据如何保存,更不必管“钥匙”具体什么样(无需指定密码),只要在使用时告诉程序“来,用这个钥匙,给我解这个数据”,就像插入钥匙打开锁头一样,至于钥匙长什么样,管它呢!

    不能被破解吗?

      理论上哪有不能被破解的东西,但是这种方式也很安全,因为你需要满足下面几种方式才能被破解:

      1. 暴力算法,纯技术流,黑客拿到你的数据文件,看到一串乱码,但由于黑客经验极其丰富一看就知道大概是什么算法算出来的,好,他就开始暴力破解....唉,我不好说用多长时间,但是平民小白被牛B黑客这么死心塌地破解的概率恐怕比中国男足进世界杯还小吧。

      2. 黑客必须同时得到你的数据文件和“钥匙”文件 & 还得知道你的A钥匙就是开A数据的 & 还得知道怎么使用钥匙开你的数据文件(程序逻辑),这概率...也够小的吧。


    数据逻辑:

      1. 程序里,我设定了四个基础数据,来组成一个“隐私内容项”,分别是标题、关键字、加密内容和校验码;

    #region Statements
    
    /// <summary>
    /// 标题
    /// </summary>
    private string _title;
    /// <summary>
    /// 关键内容,比如登录账号
    /// </summary>
    private string _keyword;
    /// <summary>
    /// 加密后的密文
    /// </summary>
    private string _ciphertext;
    /// <summary>
    /// 校验值
    /// </summary>
    private string _checkcode;
    
    #endregion
    隐私内容类

      2. 隐私内容类是承载用户信息的基本单元,本身没有功能,所以设计一个管理它使用它的类,暂时叫做“内容管理类”吧。内容管理类的功能是接收界面反馈,添加、移除“隐私内容项”,使用“加密服务”来实现加密解密功能等等;

      3. 内容管理类自己没有加解密功能,是要调用实现加解密功能的类,暂时成为“密钥服务”吧,密钥服务要实现密钥新生、密钥导入、密钥导出、加密、解密、创建Hash、验证等功能;

    public class CryptogramService
    {
       public byte[] Encrypt(byte[] data){ }
       public byte[] Decrypt(byte[] data){ }
       public CryptogramService LoadKey(string path){ }
       public byte[] GetKey(){ }
    }
    密钥服务

      4. 内容管理类可以将所有“隐私内容项”导出成文件;

      5. 密钥服务类可以将密钥信息保存成文件;

      以上代码仅供参考,实际应该被实现成接口,和调用方实现松耦合,我在程序里暂时写死了 ^^,至此 iPassword Lite 主要功能所使用到的数据都描述了,总结起来就是“内容管理”接收用户输入,创建一个“隐私内容”,调用“密钥服务”加密“隐私内容”


    密钥服务:

      1. 创建新钥匙:程序启动时,默认创建新的“钥匙”,钥匙的齿什么样用户不用管,其实程序也不知道;

      2. 导入钥匙:程序可以导入用户指定的“钥匙”,导入钥匙的作用是让用户使用自选的钥匙来开锁;

      3. 导出钥匙:程序可以保存当前使用的钥匙为文件;


    界面实现:

      在 iPassword Lite 中使用了 ListBox 自绘的方式呈现,这恐怕也是整个程序中唯一可以给其他朋友借鉴一二的地方,倒不是多高明,是因为其它的内容实在没什么展示代码的意义。一步步说吧

      1. 设置 ListBox,其 DrawMode 设置成 OwnerDrawVariable 就可以自绘了;

      2. 绘制成什么样,我现在的做法是 头像 + 标题 + 描述文字(描述文字暂时是关键字内容)的现实方式,左侧头像,右侧由标题和描述文字上下左对齐排列;

      3. 测量项目,在绘制前要设定好 ListBox 的每个 Item 的大小,宽度好办,我就设置 ListBox 的 ClientSize 就可以了,高度是个问题,我的方式是上空白 + 头像高度 + 下空白的方式,但是我的头像高度又由标题文字和描述文字及留白组成,所以实际计算方式是【上留白 + 标题 + 描述文字 + 下留白】来组成,下面是实际代码

    private void profileListBox_MeasureItem(object sender, MeasureItemEventArgs e)
            {
                // 高度 = 上下通用间隔 + 标题和描述所有文本间隔
                this.MeasureTitleHeight = TextRenderer.MeasureText("", this.FontForTitle).Height;
                this.MeasureDescriptionHeight = TextRenderer.MeasureText("", this.FontForDescription).Height;
                var allTextHeight = this.MeasureTitleHeight + this.MeasureDescriptionHeight + this.TextPadding * 4;
    
                e.ItemHeight = this.ItemPadding * 2 + allTextHeight;
                e.ItemWidth = profileListBox.ClientRectangle.Width;
    
                // 设置头像尺寸
                this.HeadshotSize = new Size(allTextHeight, allTextHeight);
            }
    测量 Item

      4. 绘制。绘制比较麻烦,一开始我是所有元素分别绘制的,发现闪烁明显,后来采用绘制到内存图像,再把图像绘制到界面的方式,即双缓冲的思路。具体方式是这样:

        1). 创建内容图像

    // 准备图形
    Bitmap itemImage = new Bitmap(e.Bounds.Width, e.Bounds.Height, e.Graphics);
    var itemGraphics = Graphics.FromImage(itemImage);
    itemGraphics.CompositingQuality = CompositingQuality.HighQuality;
    itemGraphics.SmoothingMode = SmoothingMode.AntiAlias;
    var itemRect = new Rectangle(0, 0, e.Bounds.Width, e.Bounds.Height);
    内存图像

        2). 绘制背景

    // 绘制背景
    var bgBrush = new SolidBrush(Color.White);
    itemGraphics.FillRectangle(bgBrush, itemRect);
    bgBrush.Dispose();
    绘制背景

        3). 绘制头像,获取标题第一个字,将其绘制到头像圆形中

    // 绘制头像圆形
    var headImageBrush = new SolidBrush(Color.DarkTurquoise);
    if (isSelected)
    {
        headImageBrush.Color = Color.Tomato;
    } 
    var headImageRect = new Rectangle(new Point(itemRect.X + this.ItemPadding, itemRect.Y + this.ItemPadding), this.HeadshotSize);
    itemGraphics.FillEllipse(headImageBrush, headImageRect);
    headImageBrush.Dispose();
    
    // 绘制头像中的字符
    string hsLetter = title.Substring(0, 1);  // 取标题第一个字符作为头像字显示
    var letterBrush = new SolidBrush(Color.White);
    var letterSF = new StringFormat(StringFormatFlags.NoWrap);
    letterSF.Alignment = StringAlignment.Center;
    letterSF.LineAlignment = StringAlignment.Center;
    var letterRect = headImageRect;
    itemGraphics.DrawString(hsLetter, this.FontForHeadshot, letterBrush, letterRect, letterSF);
    
    letterBrush.Dispose();
    letterSF.Dispose();
    绘制头像和文字

        4). 绘制标题和描述文字绘制描述文字

    //draw title
    var titleString = title;
    var titleBrush = new SolidBrush(Color.FromKnownColor(KnownColor.WindowText));
    var titleRect = new Rectangle(itemRect.X + this.ItemPadding + this.HeadshotSize.Width + this.ItemPadding,
                                    itemRect.Y + this.ItemPadding,
                                    itemRect.Width - (itemRect.X + this.ItemPadding + this.HeadshotSize.Width + this.ItemPadding),
                                    this.TextPadding + this.MeasureTitleHeight + this.TextPadding);
    var titleSF = new StringFormat(StringFormatFlags.NoWrap);
    titleSF.Alignment = StringAlignment.Near;
    titleSF.LineAlignment = StringAlignment.Center;
    
    itemGraphics.DrawString(titleString, this.FontForTitle, titleBrush, titleRect, titleSF);
    titleBrush.Dispose();
    titleSF.Dispose();
    
    //draw keyword string
    var kwString = keyword;
    var kwBrush = new SolidBrush(Color.FromKnownColor(KnownColor.GrayText));
    var kwRect = new Rectangle(itemRect.X + this.ItemPadding + this.HeadshotSize.Width + this.ItemPadding,
                                itemRect.Y + this.ItemPadding + this.TextPadding * 2 + this.MeasureTitleHeight,
                                itemRect.Width - (itemRect.X + this.ItemPadding + this.HeadshotSize.Width + this.ItemPadding),
                                this.TextPadding * 2 + this.MeasureDescriptionHeight);
    var kwSF = new StringFormat(StringFormatFlags.NoWrap);
    kwSF.Alignment = StringAlignment.Near;
    kwSF.LineAlignment = StringAlignment.Center;
    
    itemGraphics.DrawString(kwString, this.FontForDescription, kwBrush, kwRect, kwSF);
    kwBrush.Dispose();
    kwSF.Dispose();
    绘制头像和文字

        5). 把图像绘制到界面

    var image = OwnDrawItemToImage(e);
    if(image !=null)
    {
        e.Graphics.DrawImageUnscaled(image, e.Bounds);
        image.Dispose();
    }
    将图像绘制到界面

    至此,界面绘制完成,这种方式可以避免一个 Item 被直接在界面绘制多次,造成效率低下和闪烁。

  • 相关阅读:
    (转载)linux 常用命令
    视图view
    Mysql增删改查
    mysql最基础命令
    mysql的基本操作
    (转载)RHEL7(RedHat 7)本地源的配置
    (转载)Linux之虚拟机 rehl7的ip
    js 基本
    java Servlet
    java Tttp协议和Tomcat
  • 原文地址:https://www.cnblogs.com/cinlap/p/4904490.html
Copyright © 2011-2022 走看看