zoukankan      html  css  js  c++  java
  • C#使用RSA私钥加密公钥解密的改进,解决特定情况下解密后出现乱码的问题

    最近需要对一些数据加密后进行HTTP传输,由于希望对方只能收到数据后解密,而无法知道加密方法以防止伪造,所以选择了一个通过BigInteger类,使用私钥加密,公钥解密的算法。

    算法是网上找来的,链接如下:

     一开始使用得挺好,加密解密都正常,但当加密的数据超过了128byte,解密后偶尔会出现乱码,解密失败。

    通过跟踪发现,这是算法的一个bug,是由于对BigInteger类不当使用产生的。 具体分析如下:

    先看加密方法:

    private string EncryptString(string source, BigInteger d, BigInteger n)
            {
                
    int len = source.Length;
                
    int len1 = 0;
                
    int blockLen = 0;
                
    if ((len % 128== 0)
                    len1 
    = len / 128;
                
    else
                    len1 
    = len / 128 + 1;
                
    string block = "";
                
    string temp = "";
                
    for (int i = 0; i < len1; i++)
                {
                    
    if (len >= 128)
                        blockLen 
    = 128;
                    
    else
                        blockLen 
    = len;
                    block 
    = source.Substring(i * 128, blockLen);
                    
    byte[] oText = System.Text.Encoding.Default.GetBytes(block);
                    BigInteger biText 
    = new BigInteger(oText);
                    BigInteger biEnText 
    = biText.modPow(d, n);
                    
    string temp1 = biEnText.ToHexString();
                    temp 
    += temp1;
                    len 
    -= blockLen;
                }
                
    return temp;
            }

    由于RSA算法单次加密只能支持128byte的数据,如果数据长度超过128byte,就会被分割为几段进行加密,最后把加密结果转换为16进制字符串,并连接起来输出结果。

    一般情况下,128byte的数据,加密后输出的hex字符串应该是256byte,所以对应的解密方法为:把加密后的hex字符串按256byte进行拆分,分别解密,最后得到原文。方法如下:

    private string DecryptString(string source, BigInteger e, BigInteger n)
            {
                
    int len = source.Length;
                
    int len1 = 0;
                
    int blockLen = 0;
                
    if ((len % 256== 0)
                    len1 
    = len / 256;
                
    else
                    len1 
    = len / 256 + 1;
                
    string block = "";
                
    string temp = "";
                
    for (int i = 0; i < len1; i++)
                {
                    
    if (len >= 256)
                        blockLen 
    = 256;
                    
    else
                        blockLen 
    = len;
                    block 
    = source.Substring(i * 256, blockLen);
                    BigInteger biText 
    = new BigInteger(block, 16);
                    BigInteger biEnText 
    = biText.modPow(e, n);
                    
    string temp1 = System.Text.Encoding.Default.GetString(biEnText.getBytes());
                    temp 
    += temp1;
                    len 
    -= blockLen;
                }
                
    return temp;
            }

     这个算法一般来讲是没问题的,但问题就在于,对于128byte的数据,BigInteger类输出的加密后的hex字符串,并不一定是256byte。所以,解密的时候按照256byte进行拆分,就会出现字符串拆分不正确,最终导致解密失败,解密出来的结果是乱码。

    我们来看看BigInteger类的ToHexString()方法,其实现如下:

    public string ToHexString()
            {
                
    string result = data[dataLength - 1].ToString("X");

                
    for (int i = dataLength - 2; i >= 0; i--)
                {
                    result 
    += data[i].ToString("X8");
                }

                
    return result;
            }

     对于128byte的BigInteger,此方法返回的结果并不一定是256byte。

    简单的解决办法,就是把这个方法的第一行,ToString("X")改为ToString("X8"),修改后的方法如下:

    public string ToHexString()
            {
                
    string result = data[dataLength - 1].ToString("X8");

                
    for (int i = dataLength - 2; i >= 0; i--)
                {
                    result 
    += data[i].ToString("X8");
                }

                
    return result;
            }

    这样改虽然可以解决问题,但属于治标不治本的方法,因为这样改,也不能保证输出的字符串就是256byte, 最靠谱的方法是改进加密方法和解密方法。

    加密方法的改进:

    由于加密后的结果是通过输入16进制字符串进行保存的,输入的结果不可能包含@字符,因此我们可以用@符号来分割每128byte数据的加密结果,解密的时候按照@符号进行分割就不会出错。

    改进后的加密方法如下:

    private string EncryptString(string source, BigInteger d, BigInteger n)
            {
                
    int len = source.Length;
                
    int len1 = 0;
                
    int blockLen = 0;
                
    if ((len % 128== 0)
                    len1 
    = len / 128;
                
    else
                    len1 
    = len / 128 + 1;
                
    string block = "";
                StringBuilder result 
    = new StringBuilder();
                
    for (int i = 0; i < len1; i++)
                {
                    
    if (len >= 128)
                        blockLen 
    = 128;
                    
    else
                        blockLen 
    = len;
                    block 
    = source.Substring(i * 128, blockLen);
                    
    byte[] oText = System.Text.Encoding.Default.GetBytes(block);
                    BigInteger biText 
    = new BigInteger(oText);
                    BigInteger biEnText 
    = biText.modPow(d, n);
                    
    string temp = biEnText.ToHexString();
                    result.Append(temp).Append(
    "@");
                    len 
    -= blockLen;
                }
                
    return result.ToString().TrimEnd('@');
            }

     改进后的解密方法如下:

    private string DecryptString(string source, BigInteger e, BigInteger n)
            {
                StringBuilder result 
    = new StringBuilder();
                
    string[] strarr1 = source.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries);
                
    for (int i = 0; i < strarr1.Length; i++)
                {
                    
    string block = strarr1[i];
                    BigInteger biText 
    = new BigInteger(block, 16);
                    BigInteger biEnText 
    = biText.modPow(e, n);
                    
    string temp = System.Text.Encoding.Default.GetString(biEnText.getBytes());
                    result.Append(temp);
                }
                
    return result.ToString();
            }

    相关链接:

    [1]RSA私钥加密公钥解密算法。

    http://blog.csdn.net/zhilunchen/archive/2008/09/17/2943158.aspx

    [2]BigInteger大整数运算类。

    http://www.codeproject.com/KB/cs/biginteger.aspx

    文章关键词:C#,RSA,私钥加密公钥解密,BigInteger类,乱码

     ----------END----------

  • 相关阅读:
    企业库连接形式简单例子记录 EnterpriseLibrary.Data
    .net 抽象类(abstract)和接口(interface)区别
    windows service 开发、安装及调试
    asp.net(mvc) 框架
    获取IP地址
    select2 模糊查询远程数据
    设计模式篇——初探命令模式
    初探MVC路由
    初探表达式目录树
    C#实现插入排序法
  • 原文地址:https://www.cnblogs.com/hhh/p/2070692.html
Copyright © 2011-2022 走看看