You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForm实现Elgamal加解密无法还原字体格式问题求助

问题概述
  • 开发场景:基于C# Windows Form实现Elgamal加解密程序
  • 功能要求:加解密全流程保留并还原所有文本格式,包括字体样式、前景色、字体族信息
  • 现有实现思路:通过Encoding.Unicode.GetBytes(text)将字符串转为字节数组,再转Base64格式,结合扩展欧几里得算法求解逆元、平方-乘算法完成加解密与逆向还原
  • 故障现象:解密后仅能还原默认格式纯文本,原文本携带的字体格式信息完全丢失
现有业务代码

加密方法实现

private string Encode()
{
    //Tinh y1
    y1 = binhphuong(alpha, k, p);
    banro = txtTest.Text;
    string loi = "";
    string kqBanMa = "";
    if (String.IsNullOrEmpty(banro))
    {
        loi = "\n Bạn phải nhập bản rõ";
    }
    if (txtA.Text == "" || txtAlpha.Text == "" || txtAlpha.Text == "" || txtBeta.Text == "" || txtP.Text == "" || txtPDoc.Text == "")
    {
        loi += "\n Bạn chưa tạo khóa";
    }
    if (loi != "")
    {
        MessageBox.Show("Có lỗi xảy ra : " + loi, "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    else
    {
        if (!Regex.IsMatch(banro, @"^\d+$"))
        {
            // Chuyển chuỗi qua dạng mảng byte
            byte[] bytes = Encoding.Unicode.GetBytes(banro);

            //Chuyển từ byte sang dạng Base 64
            string dang64 = Convert.ToBase64String(bytes);

            //chuyển các kí tự cần mã hóa về dạng số và lưu vào 1 list
            List<int> c1 = new List<int>();
            for (int i = 0; i < dang64.Length; i++)
                c1.Add((int)dang64[i]);

            //mã hóa từng kí tự dưới dạng số
            List<int> c2 = new List<int>();
            for (int i = 0; i < c1.Count; i++)
            {
                int chuyendoi = ((c1[i] % p) * binhphuong(beta, k, p)) % p;
                c2.Add(chuyendoi);
            }

            //chuyển các kí tự mã hóa dưới dạng số về dạng chữ trong ASCII và nối lại
            string c = "";
            for (int i = 0; i < c2.Count; i++)
                c += (char)c2[i];

            /*
             Do mã hóa dựa trên bảng mã ASCII nên các kí tự bị mã hóa thành các kí tự đặc biệt khá khó chịu nên
             chuyển nó qua dạng base 64 để dễ nhìn hơn.
             */

            //Chuyển chuỗi sau mã hóa thành mảng byte
            byte[] byt = Encoding.Unicode.GetBytes(c);

            //Chuyển từ mảng byte sang dạng Base 64
            kqBanMa = Convert.ToBase64String(byt);
            //return kqBanMa1;
        }
        else
        {
            int dangso = int.Parse(banro.ToString());

            //Tinh y2
            y2 = ((dangso % p) * (binhphuong(beta, k, p))) % p;

            kqBanMa = $"Bản mã C = (C1, C2) = ({y1}, {y2})";
        }
    }
    return kqBanMa;
}

解密方法实现

private string Decode()
{
    int pt1 = binhphuong(y1, a, p);
    int pt2 = songhichdao(pt1, p);
    banma = txtReCipher.Text;
    string gm = "";
    try
    {
        if (banma.Length == 0)
        {
            MessageBox.Show("Có lỗi xảy ra : Chưa có bản mã để giải mã", "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        else
        {
            if (!Regex.IsMatch(banro, @"^\d+$"))
            {
                //chuyển chuỗi dạng base 64 qua mảng byte
                byte[] bytes = Convert.FromBase64String(banma);

                //chuyển từ mảng byte về chuỗi sau khi mã hóa
                string strMaHoa = Encoding.Unicode.GetString(bytes);

                //chuyển các kí tự qua dạng số
                List<int> temp1 = new List<int>();
                foreach (int item in strMaHoa)
                    temp1.Add((int)item);

                //giải mã từng kí tự
                List<int> temp2 = new List<int>();
                for (int i = 0; i < temp1.Count; i++)
                {
                    int element = ((temp1[i] % p) * pt2) % p;

                    temp2.Add(element);// giải mã
                }

                //chuyển các đã mã hóa qua dạng chữ và nối lại thành chuỗi
                string temp = "";
                for (int i = 0; i < temp2.Count; i++)
                    temp += (char)temp2[i];

                // chuyển chuỗi giải mã sang dạng bytes
                byte[] bytesLast = Convert.FromBase64String(temp);


                //chuyển từ mảng byte sang chuỗi đã giải mã
                gm = Encoding.Unicode.GetString(bytesLast);

            }
            else
            {
                int ptso = ((y2 % p) * pt2) % p;
                gm = ptso.ToString();
            }
        }
    }
    catch
    {
        MessageBox.Show("Có lỗi xảy ra", "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    return gm;

}
问题根因
  1. 未采集格式元数据:普通TextBox控件本身不支持分段存储字体、颜色格式,要实现多格式文本编辑必须使用RichTextBox控件。就算替换为RichTextBox,当前代码读取的是.Text属性,该属性仅返回无格式纯文本,所有字体族、字号、样式、前景色等格式信息都存储在.Rtf属性的结构化标记中,这部分内容从未进入加密流程,解密后自然没有格式信息可还原。
  2. 解密分支逻辑错误:解密流程中用加密前的明文变量banro做正则匹配判断分支,解密场景下banro为空时会直接走错逻辑分支,触发异常或返回错误结果。
  3. 赋值逻辑错误:解密得到纯文本后直接赋值给控件的.Text属性,就算拿到了带格式的内容,.Text属性也不会解析格式标记,只会显示纯文本。
修复步骤
  • 第一步:将输入、输出的文本控件全部替换为RichTextBox类型,支持多格式文本存储与渲染。
  • 第二步:修改加密时的内容读取逻辑,把原来读取.Text的代码改为读取.Rtf属性,将包含所有格式标记的RTF完整字符串作为明文输入到后续Elgamal加密流程,不需要单独拆分纯文本和格式信息,RTF本身是可直接序列化的字符串格式,兼容现有字节转码、加解密逻辑。
  • 第三步:修复解密分支判断逻辑,去掉依赖明文banro的判断,可统一走通用字符串加解密流程,不需要单独处理纯数字场景,避免分支判断错误。
  • 第四步:修改解密后的内容赋值逻辑,解密得到完整RTF字符串后,直接赋值给输出RichTextBox的.Rtf属性,控件会自动解析RTF标记,还原所有字体、颜色、样式格式。
  • 第五步:校验Elgamal参数,保证选取的素数p大于单字符加密单元的最大可能取值,避免模运算溢出导致解密后内容乱码。

内容的提问来源于stack exchange,提问作者Nguyễn Quang Huy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:27:14