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----------

 

转载于:https://www.cnblogs.com/hhh/archive/2011/06/03/2070692.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值