二维码加密/解密(防篡改) 工具实现

发布时间:2020-08-04

概述

此工具实现 二维码图像加密/解密功能,原理是 将二维码图像 转换为0/1压缩矩阵字符串,实现加密功能,显示图像时,将字符串转换为图像即可。 在一些领域和功能上,能够有效防止恶意修改替换二维码图片,将图像直接转换为较少的字符串,和代码融为一体,没有图片资源,十分安全可靠。

详细

前言

  在任何领域,图像资源是很容易被获取甚至恶意替换的,此工具提供一种解决思路,把图像转换为0/1压缩矩阵字符串,然后运行程序时候,解析字符串展示出图像来。图像和代码融为一体,这种情况下,除非破解源代码,否则束手无策。 这种方式,对于二维码图像,尤其适用,转换成字符串也比较小。下面具体讲讲实现原理和过程。


效果展示

此为重要的收款码,别人将无法篡改,最终,只会在程序 中 存储 密钥信息,运行程序进行展示,没有图像资源。

1.png


工具实现过程

  1.准备c# WFM窗体应用程序(作为入口)

        如下图, 创建窗体程序以及相关的控件(输入框、按钮等)

   2.png

3.png


2. 实现拖拽读取图片功能


4.png

 相关代码

 private void Form1_DragEnter(object sender, DragEventArgs e)
        {
            string fileInfo = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
            if (fileInfo.EndsWith(".png") && !textBox1.Text.Equals(fileInfo))
            {
                textBox1.Text = fileInfo;
                Common.DealImgCodeToZeroStepOne(textBox1.Text);
                Common.DealTrimStepTwo();
                string res = Common.DealCodeToStr();
                richTextBox1.Text = res;
            }

        }

3. 图像加密,转换字符串

      图像转换压缩字符串过程 是,第一步,把图像 所有的像素点,规划为0/1两种,黑白两种,也就是 对 图像的位图 处理,处理完之后, 第二步 ,二维码四周 的空白像素 是 不要的,是冗余信息,需要删除,进行处理之后,进行第三步,统计压缩数据,因为遍历是每一行一行进行的,相邻的几行 是一样的,需要 压缩 处理 即,使用 5> 表示,有5行 一样的像素信息,大大降低转换后存储大小。至此,转换完成, 需要特别注意的是, 一般截图二维码,像素点RGB不是完全的0,0,0,可能是有值的,黑白直接还有过度颜色,不可以单纯以二维码  单位块,一块一块的去计算转换,是有问题的。

    下面是相关代码块,有点复杂,请仔细看。转换完成之后,生成比较少量的字符串,此为整理压缩后的数据矩阵。将在 展示 图像中去解析使用此字符串。

       

         //第一步:  二维码处理 0/1 化  转换为 位图

 public static void DealImgCodeToZeroStepOne(String imgDir) 
        {
            Image curImg = Image.FromFile(imgDir);
            int imgWidth = curImg.Width;
            int imgHeight = curImg.Height;

            Bitmap curBitImg = new Bitmap(curImg);
            curDealImg = new Bitmap(imgWidth, imgHeight);
            for (int y = 0; y < imgHeight; y++)
            {
                for (int x = 0; x < imgWidth ; x++)
                {
                    Color pixel = curBitImg.GetPixel(x, y);
                    if (pixel.R <= 2&& pixel.G <= 2 && pixel.B <= 2)
                    {
                        curDealImg.SetPixel(x, y, Color.FromArgb(255, 0, 0, 0));
                    }
                    else
                    {
                        curDealImg.SetPixel(x, y, Color.FromArgb(0, 0, 0, 0));
                    }
                }
            }
        }

          //第二步:  删除边缘 无用 像素

 public static void DealTrimStepTwo()
        {
            int imgHeight = curDealImg.Height;
            int imgWidth = curDealImg.Width;
            int startX = 0, endX = 0, startY = 0, endY = 0;
            int x, y;
            for (y = 0; y < imgHeight; y++)
            {
                if (startY != 0) break;
                for (x = 0; x < imgWidth; x++)
                {
                    if (curDealImg.GetPixel(x, y).A != 0 && startY == 0)
                    {
                        startY = y;
                        break;
                    }
                }
            }

            for (y = imgHeight - 1; y >= 0; y--)
            {
                if (endY != 0) break;
                for (x = imgWidth - 1; x >= 0; x--)
                {
                    if (curDealImg.GetPixel(x, y).A != 0 && endY == 0)
                    {
                        endY = y;
                        break;
                    }
                }
            }

            for (x = 0; x < imgWidth; x++)
            {
                if (startX != 0) break;
                for (y = 0; y < imgHeight; y++)
                {
                    if (curDealImg.GetPixel(x, y).A != 0 && startX == 0)
                    {
                        startX = x;
                        break;
                    }
                }
            }

            for (x = imgWidth - 1; x >= 0; x--)
            {
                if (endX != 0) break;
                for (y = 0; y < imgHeight; y++)
                {
                    if (curDealImg.GetPixel(x, y).A != 0 && endX == 0)
                    {
                        endX = x;
                        break;
                    }
                }
            }

            Color pixel;
            int afterWidth = endX - startX + 1;
            int afterHeight = endY - startY + 1;

            Bitmap resBmImg = new Bitmap(afterWidth, afterHeight);
            int x2, y2;
            for (y2 = 0; y2 < afterHeight; y2++)
            {
                for (x2 = 0; x2 < afterWidth; x2++)
                {
                    pixel = curDealImg.GetPixel(startX + x2, startY + y2);
                    resBmImg.SetPixel(x2, y2, Color.FromArgb(pixel.A, pixel.R, pixel.G, pixel.B));
                }
            }
            curDealImg = null;
            curDealImg = resBmImg;

        }

           //第三步: 二维码处理 转换 压缩数据 字符串   加密完成

          

       public static string DealCodeToStr()
        {
            int imgWidth = curDealImg.Width;
            int imgHeight = curDealImg.Height;

            string res = "";
            for (int y = 0; y < imgHeight; y++)
            {

                int totalNum = 0;
                int oldType = -1;
                string curLineStr = "";
                for (int x = 0; x < imgWidth; x++)
                {
                    Color pixel = curDealImg.GetPixel(x, y);
                    int curType = pixel.A == 255 ? 1 : 0;
                    if (x == 0)
                    {
                        oldType = pixel.A == 255 ? 1 : 0;
                        totalNum = 1;
                        continue;
                    }
                    else if (curType == oldType)
                    {
                        totalNum = totalNum + 1;
                        if (x == imgWidth - 1)
                        {
                            if (y == imgHeight - 1)
                            {
                                curLineStr = curLineStr + totalNum + "," + oldType;

                            }
                            else {
                                curLineStr = curLineStr + totalNum + "," + oldType + "\n";
                            }
                        }
                        continue;
                    }
                    else if (curType != oldType)
                    {
                        curLineStr = curLineStr + totalNum + "," + oldType + " ";
                        totalNum = 1;
                        oldType = curType;
                        if (x == imgWidth - 1){
                            if (y == imgHeight - 1)
                            {
                                curLineStr = curLineStr + totalNum + "," + oldType;

                            }
                            else
                            {
                                curLineStr = curLineStr + totalNum + "," + oldType + "\n";
                            }
                        }
                    }
                }
                if (y == 0)
                {
                    res = "1>" + curLineStr;
                    continue;
                }
                else
                {
                    if (res.EndsWith(curLineStr))
                    {
                        int endfindIndex = res.IndexOf(">" + curLineStr);
                        string repeateNum = "";

                        if (endfindIndex <= 2)
                        {
                            repeateNum = res.Substring(0, endfindIndex);
                        }
                        else {
                            int startIndex = endfindIndex - 5 ;
                            int agofindIndex = res.IndexOf("\n", startIndex);

                            repeateNum = res.Substring(agofindIndex + 1, endfindIndex - agofindIndex - 1); 
                        }
                          int addRepeateNum = Int32.Parse(repeateNum) + 1;
                        res = res.Replace(repeateNum + ">" + curLineStr, addRepeateNum + ">" + curLineStr);
                    }
                    else
                    {
                        res = res + "1>" + curLineStr;
                    }
                    if (!res.EndsWith("\n")) {
                        int a = 41564;

                    }

                }
            }
            resStr = res;
            return res;
        }

4. 图像展示,解密字符串

    在上步中,已经完成了图像转换字符串过程了,程序中 ,只需要使用此字符串,即可展示二维码图像,展示原理, 比如生成 字符串 为  5>49,1 9,0 6,1 16,0 ......, 表示,有5行一样的像素, 49个为1 黑色。9个为0 白色 等等,其他行表示内容与此类似,解析只不过是,算出 总共的像素大小,长和宽度,声明 对应的  位图对象,在位图对象的每一个像素点 x,y 上,设置其应0/1 黑白 颜色 即可。需要创建个新窗体,进行设置。就是很多很多for循环套用 即可。具体代码如下

    新建窗体 showCodeDialog,添加 如下 代码

    //声明所需变量
        private  string resStr;
        private int width;
        private int height;
        private int gap = 50;
    
    //设置原始字符串
    public void setCurStr(string str)
        {
            resStr = str;
        }
     // 计算出 二维码图像的长宽
        public void dealSize()
        {
            string orgPayCodeStr;
            if (resStr.IndexOf("\r\n") >= 0) {
                orgPayCodeStr = resStr.Replace("\r\n", "*");
            } else {
                orgPayCodeStr = resStr.Replace("\n", "*");
            }
            string[] listOneLine = orgPayCodeStr.Split('*');

            string firstStr = listOneLine[1];
            int tempEndIndex = firstStr.IndexOf(">");
            string[] lineInfolist = firstStr.Substring(tempEndIndex + 1).Split(' ');
            // 计算 宽度
            width = -1;
            for (int k = 0; k < lineInfolist.Length; k++) { 
                {
                    string[] oneInfo = lineInfolist[k].Split(',');
                    int repeatOnePixel = Int32.Parse(oneInfo[0]);
                    width = width + repeatOnePixel;
                }
           }
            height = -1;
            for (int i = 0; i < listOneLine.Length; i++)
            {
                string lineStr = listOneLine[i];

                int endIndex = lineStr.IndexOf(">");
                string a = lineStr.Substring(0, endIndex);
                int repeatLines = Int32.Parse(a);
                for (int j = 0; j < repeatLines; j++)
                {
                    height = height + 1;
                }
            }
            width = width + 1;
            height = height + 1;

            this.Size = new Size(width + gap * 2, height + gap *2);  //设置窗体大小
        }   
        
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            var g = e.Graphics;
            dealSize();
            Bitmap resBitmap = new Bitmap(width, height);

            string orgPayCodeStr;
            if (resStr.IndexOf("\r\n") >= 0)
            {
                orgPayCodeStr = resStr.Replace("\r\n", "*");
            }
            else
            {
                orgPayCodeStr = resStr.Replace("\n", "*");
            }


            string[] listOneLine = orgPayCodeStr.Split('*');

            int curLineIndex = -1;
            for (int i = 0; i < listOneLine.Length; i++)
            {
                string lineStr = listOneLine[i];

                int endIndex = lineStr.IndexOf(">");

                string a = lineStr.Substring(0, endIndex);
                int repeatLines = Int32.Parse(a);
                string[] lineInfolist = lineStr.Substring(endIndex + 1).Split(' ');
                for (int j = 0; j < repeatLines; j++)
                {
                    curLineIndex = curLineIndex + 1;
                    int x = -1;
                    for (int k = 0; k < lineInfolist.Length; k++)
                    {
                        string[] oneInfo = lineInfolist[k].Split(',');
                        int repeatOnePixel = Int32.Parse(oneInfo[0]);
                        for (int z = 1; z <= repeatOnePixel; z++)
                        {
                            resBitmap.SetPixel(x + z, curLineIndex, Color.FromArgb(oneInfo[1] == "1" ? 255 : 0, 0, 0, 0));
                            if (z == repeatOnePixel)
                            {
                                x = x + z;
                            }
                        }
                    }
                }
            }
            g.DrawImage(resBitmap, gap -10, 15);
        }

5. 总结

   通过以上几步,已经实现了二维码的加密 /解密,其实本质就是 把二维码图像信息,转换为很简短的, 用 0/1>*等字符拼接起来的字符串,因为有自己的处理,字符串不会很大,程序中 用字符串代替了图片而已。 需要注意的一点是,一般使用的二维码 不可能是完完全全的 255/0 纯黑白的 图像,图像 中是有过度颜色的,不能按块来处理,这是比较麻烦的,需要使用上面的方法进行 处理。

   二维码图像,是非常非常重要的,一般不要以图像资源进行处理显示,很容易被人替换,或者显示不正常, 采取此工具的方式, 除非破解代码,否则不可能把二维码  图像给破坏的。这是一种思路。大家可以多理解理解。

   编写不易,请多支持,谢谢。



项目结构图


44.png












本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
手机上随时阅读、收藏该文章 ?请扫下方二维码