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 被直接在界面绘制多次,造成效率低下和闪烁。

  • 相关阅读:
    Java实现 LeetCode 56 合并区间
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
  • 原文地址:https://www.cnblogs.com/cinlap/p/4904490.html
Copyright © 2011-2022 走看看