c++实现rsa算法_【免费毕设】ASP.NETRSA可视化算法程序的实现与研究(源代码+论文)...

e7c2e7748626ad13b6b7e39592f83c47.png点击上方“蓝字”关注我们 804fb3f9f3afa8f76a22304802d4c176.png 2f08e6b4bd8285fb83c4d9eba8fb7f05.png目录

2c5c9c55b611e4abe8ab5742e7ea5417.png

2f08e6b4bd8285fb83c4d9eba8fb7f05.png系统设计

RSA文件加密软件的设计与实现

2.1 需求分析与总体设计

2.1.1功能分析

经过1.3.2节的论述,我们可以将对软件的要求总结如下:

①  可以按要求的位数生成非对称密钥。

②  可以用指定密钥以RSA算法加密任意一个文件,加密生成的数据为纯文本。

③  可以装载加密过的文件,并用指定的密钥解密还原出原文件。

④ 提示信息完整、操作舒适、图形界面雅观

按上述描述,给出Use Case和Statechart如图2-1。

86dcb932d9209f2801e0c52e5a7dcd04.png

根据以上分析,一般来说,需要进行编码的程序有

①RSA密钥生成 ②RSA加密解密 ③任意文件的读取④各环节必要的数据编码转换 ⑤图形操作界面。

工程方案选择

综合考虑复用性、可维护性和执行效率,较妥当的方法是分层设计。核心的RSA算法由C++类库实现,针对用户所在的操作系统封装成本地化组件。其他各功能如文件操作、数据编码转换和图形界面等,由托管代码借助虚拟机平台标准库的功能快速开发实现(本文针对选用.Net上的C#论述,选用java由JNI或其他方式调用本地组件,设计模式上是完全类似的)。这种开发方式,核心功能集中在最底层,在不断的封装中针对具体环境对组件功能不断扩充,任意一个层面的封装都可以被直接应用到其他项目,比如在Web使用以前为某窗体程序写的组件、给嵌入式设备交叉编译算法库等。但是每一层都需要依赖底层的所有组件。图2-2形象的说明了分层设计给复用带来的好处。

fdd32e0873d2e49b3d0c80b31c252904.png

选用这种设计方案,上层使用C#,底层算法使用C++,可以由一个Visual Studio解决方案管理,给调试带来极大的方便。整个工程分四层,实现RSA加密算法的C++核心类库、封装C++核心类库的DLL组件、引用DLL的.Net类、实现文件操作功能的.Net窗体应用程序。2.2节详细介绍各部分的设计与开发。

考虑到工作量,本软件加解密数据没有严格遵从RSA标准PKCS #1,而是在满足设计要求的前提下,以一种尽可能简单的方式实现加密和解密。

2f08e6b4bd8285fb83c4d9eba8fb7f05.png系统实现

实现RSA加密算法的C++核心类库

1. 大数存储和四则运算

根据RSA算法的要求,为了实现大数的各种复杂运算,需要首先实现大数存储和基本四则运算的功能。当今开源的大数运算C++类有很多,多用于数学分析、天文计算等,本文选用了一个流行的大数类型,并针对RSA算法和本项目的具体需要对其进行了扩充和改进。下面简单介绍大数存储和四则运算的实现原理。

最先完成的功能是大数的存储,存储功能由flex_unit类提供。和普通的类型一样,每一个大数对应一个flex_unit的实例。类flex_unit中,用一个无符号整数指针unsigned * a指向一块内存空间的首地址,这块内存空间用来存储一个大数,所以可以说,大数是被存储在一个以unsigned为单元的线性组中。在方法void reserve( unsigned x )中通过C++的new来给a开辟空间,当flex_unit的实例中被存入比当前存储的数更大的数时,就会调用reserve来增加存储空间,但是当flex_unit的实例中被存入比当前存储的数更小的数时,存储空间并不会自动紧缩,这是为了在运算的时候提高执行效率。结合指针a,有两个重要的无符号整数来控制存储,unsigned z和unsigned n,z是被分配空间的单元数,随数字变大不断增大,不会自己紧缩,而n是当前存储的大数所占的单元数,组成一个大数的各unsigned单元的存入和读出由set、get方法完成,变量n是只读的。类型unsigned在32位机是32位的,所以对于flex_unit这个大数类来说,每个大数最大可以达到 2**32个字节长,这已经超过了32位机通常的最大内存容量,所以是足够进行RSA所需要的各种运算的。图2-3形象的说明了大数存储类flex_unit对大数的管理。

8ee900c0a63828311b10157297b5f6f4.png

在flex_unit的存储功能基础上,将其派生,得到vlong_value,在vlong_value中实现四则运算函数,并实现强制转换运算符unsigned,以方便大数类型和普通整数的互相赋值。当大数被强制转换为unsigned时,将取其最低四字节的值。四则运算实现的原理十分简单,都是按最基本的算术原理实现的,四则运算过程的本质就是按一定数制对数字的计算,比如相加,就是低位单元对齐,逐单元相加并进位,减法同理。而乘除法和取余也都是按照竖式运算的原理实现,并进行了必要的优化。虽然实现了四则运算函数,但是若是程序里的运算都要调用函数,显得烦琐而且看起来不美观,所以我们另写一个类vlong,关联(Associate,即使用vlong_value类型的对象或其指针作为成员)vlong_value,在vlong重载运算符。这样,当我们操作vlong大数对象的时候,就可以像使用一个简单类型一样使用各种运算符号了。之所以将vlong_value的指针作为成员而不是直接构造的对象,也是为了提高执行效率,因为大型对象的拷贝要消耗不少机器时间。

2. 大数幂模与乘模运算Montgomery幂模算法

在实现了vlong类型后,大数的存储和四则运算的功能都完成了。考虑到RSA算法需要进行幂模运算,需要准备实现这些运算的方法。所以写一个vlong的友元,完成幂模运算功能。幂模运算是RSA 算法中比重最大的计算,最直接地决定了RSA 算法的性能,针对快速幂模运算这一课题,西方现代数学家提出了很多的解决方案。经查阅相关数学著作,发现通常都是依据乘模的性质,先将幂模运算化简为乘模运算。

通常的分解习惯是指数不断的对半分,如果指数是奇数,就先减去一变成偶数,然后再对半分,例如求D=,E=15,可分解为如下6个乘模运算。

归纳分析以上方法,对于任意指数E,可采用如图2-4的算法流程计算 。

c4b68f50e44e70a86f560744cd52fab7.png

按照上述流程,列举两个简单的幂模运算实例来形象的说明这种方法。

①  求的值

开始            D = 1                                 P = 2 mod 17 =2       E = 15

E奇数         D= DP mod n = 2             P = PP mod n =4       E= (E-1)/2 =7

E奇数         D= DP mod n = 8             P = PP mod n =16     E= (E-1)/2 =3

E奇数         D= DP mod n = 9             P = PP mod n =1       E= (E-1)/2 =1

E奇数         D= DP mod n = 9             P = PP mod n =1       E= (E-1)/2 =0

最终D = 9 即为所求。

②  求的值

开始            D = 1                                 P= 2 mod 13 = 2       E = 8

E偶数         D= 1                                 P = PPmod n = 4       E = E/2 =4

E偶数         D= 1                                 P = PPmod n = 3       E = E/2 =2

E偶数         D= 1                                 P = PPmod n = 9       E = E/2 =1

E奇数         D= DP mod n = 9             P = 不需要计算        E = (E-1)/2 =0

最终D = 9 即为所求。

观察上述算法,发现E根据奇偶除以二或减一除以二实际就是二进制的移位操作,所以要知道需要如何乘模变量,并不需要反复对E 进行除以二或减一除以二的操作,只需要验证E 的二进制各位是0 还是1 就可以了。同样是计算,下面给出从右到左扫描二进制位进行的幂模算法描述,设中间变量D,P,E的二进制各位下标从左到右为u,u-1,u-2,…,0。

Powmod(C,E,n)

{

             D=1;

             P=C mod n;

             for i=0 to u do

          {

                     if(Ei=1)D=D*P(mod n);

                P=P*P(modn);

      }

     return D;

}

有些文献将上述算法称为平方乘积二进制快速算法,例如参考文献中的《基于RSA算法的一种新的加密核设计》,其实这种算法本质上和图2-4的流程完全一致,只是把根据指数奇偶分开的减一和除以二合并成对指数二进制各位的判断而已。在本软件的代码中采用直接扫描vlong二进制各位的办法。

剩下的问题就是乘模运算了。提高乘模运算的速度是提高模幂运算速度的关键。一般情况下,n是数百位乃至千位以上的二进制整数,用普通的除法求模而进行乘模运算是不能满足速度的要求的。为此,Montgomery在1983年提出了一种模加右移的乘模算法(主要著作发表于1985年),从而避免了通常求模算法中费时的除法步骤。本软件仅仅是应用Montgomery(蒙哥马利)算法,算法的具体推导证明需要颇多数论知识,不在本文的讨论范围内,如需了解可参见蒙哥马利的相关著作。下面简单描述RSA中常用的Montgomery(蒙哥马利)算法供参考理解源程序。

选择与模数n互素的基数R=2k,n满足2k-1≤n<2k, n应为奇数。并且选择R-1及n’,满足0< R-1使得 RR-1-nn’=1。对于0≤m的任意整数,Montgomery给出求模乘法mR-1 mod n 的快速算法M(m):

M(m)

{

if (t≥n) return (t-n);

else return t;

}

因为,故t为整数;同时,得。由于,M(m) 中t结果范围是0≤t<2n,返回时如果t不小于n,应返回t-n。

本软件程序中,RSA核心运算使用的乘模算法就是 M(A*B)。虽然M(A*B)并不是乘模所需要的真正结果,但只要在幂模算法中进行相应的修改,就可以调用这个乘模算法进行计算了。

将上述乘模算法结合前面叙述的幂模算法,构成标准Montgomery幂模算法,即本软件所使用的流程,叙述如下。

M(m)//蒙哥马利乘模 

{

k = ( m * n’ ) mod R;

x = (m + k*n ) / R;

    if (x>=n) x -= n;

      return x;

}

exp(C,E,n) //蒙哥马利幂模

{

            D=R-n;

            P=C*R mod n;

             i=0;

            while(true)

           {

                    if(E的当前二进制位Ei==1)D=M(D*P); //从低位到高位检测二进制位

                    i+=1;

                    if(i==E的二进制位数)break;

         P=M(P*P);

      }

     return D*R-1 (mod n);

}

在具体的实现中,对应monty类的mul和exp方法。全局函数modexp初始化monty对象并调用其exp方法,使用的时候直接调用modexp即可。

3. 寻找素数Eratosthenes筛选与Fermat素数测试

首先要说明的是,事实上,当今的计算机还不足以聪明到立刻计算生成一个很大的随机素数。一般来说,要得到100%准确的大素数,都是通过查已经计算好的素数表的方式。但是素数表的方式给RSA的安全性带来隐患,因为攻击者如果得到了密钥生成时所使用的素数表,攻破RSA加密的难度将会大大降低。本程序起初使用素数表的方式,后来考虑到安全性问题,生成密钥的方式改为随机计算生成。这样,短时间内如果要得到一个100%准确的大素数是很困难的,只能以尽可能高的概率得到一个大素数。

经过2.2.1.1和2.2.1.2小节,所有的大数运算功能都准备完毕,在此基础上,本工程将寻找素数的功能置于类Prime_factory_san之中。外部只要调用本类实例的成员vlong find_prime( vlong & start )就可以以大数start为起点,得到一个数,这个数是素数的概率很大。下面介绍寻找素数的原理。

首先在需要寻找素数的整数范围内对整数进行筛选,把所有确知为合数的整数排除出去。程序中构造了一个数组b[],大小为一轮素数搜索的范围,记搜索范围大小为SS。b[0]到b[SS]分别对应大数start到start+SS。b[]中所有元素先初始化为1,如果对应的大数确定为合数,就将b[]中对应的元素置为0。最后,只需对那些b[]中为1的元素对应的大数进行比较确切的素数测试即可,只要被测试的数是素数概率达到一定门限,就判这个数为素数。这样做既保证了这段程序可以在短时间内执行完,又保证了可以以比较高的准确度得到素数。

函数find_prime先把b[]的所有元素赋值为1,然后按参数start给标记数组b[]的各元素赋0值。下面描述标记数组b[]的赋0值算法。首先,在类Prime_factory_san被构造的时候,构造函数中从2开始搜寻一些小素数,记录在数组pl[]中,共记录NP个。这些小素数用来当作因子,他们的倍数将被从大素数搜索范围内剔除(即把数组b[]的对应元素标记为0),剔除的程序代码如下。

    for(i=0;i

    {

      unsignedp = pl[i];

      unsignedr = start % vlong(p);

      if (r) r = p - r;

      while ( r < SS )

      {

        b[r] =0;

        r += p;

      }

    }

这里利用start对各小素数因子p求模的办法,得到当前p在素数搜索范围内的最小倍数在b[]中的对应位置,将其剔除后,不断后移p个位置,将这个小素数因子p在搜索范围内的所有倍数全部剔除,如图2-5所示。在完成对所有小素数因子的类似操作后,他们的倍数在搜索范围内的位置标记b[r]被全部标记为0。实际上这就是Eratosthenes筛选法。

ffb48bba3f60acf71fc155e877b3dc60.png

接下来,对可能为素数的数(即标记数组b[]中值为1的元素对应的数)进行素数测试。数论学家利用费马小定理研究出了多种素数测试方法,本程序使用一种最简单的方式,直接应用费马小定理。取一个与p互素的整数A,对于大素数p来说应该满足Ap-1mod p=1,但是我们把p代入一个大整数,满足这个关系的数不一定是素数。这时我们改变A,进行多次测试,如果多次测试都通过,这个数是素数的概率就比较大。按这种原理,我们编写素数测试函数如下。

int is_probable_prime_san( const vlong &p )

{

  const rep =4; //测试次数

  constunsigned any[rep] = { 2,3,5,7 }; //测试用的底数

  for (unsigned i=0; i

if ( modexp( any[i], p-vlong(1), p ) != vlong(1) )return 0;

 //modexp是幂模函数,按上一小节叙述的算法编码。

//这里modexp计算any[i]p-1modp。

  return 1;

}

测试通过,程序就判定这个数为找到的素数,将找到的素数返回给上层程序使用。在这里其实有一个不可忽视的问题,就是得到一个测试通过的合数。对于这种情况,RSA算法加密解密是否还可以实现,是一个需要从数学角度论证的问题。因为得到素数的概率很高,经过一整天的生成密钥和加密操作,没有发现失败的密钥,所以本文暂没有对这个问题进行讨论。

实际得到素数的流程:

(1)     先得到一个随机的大整数N当作寻找的起点.

(2)     确定一个寻找范围的大小SS,把(N,N+SS)范围内的小素数倍数去掉,即前面叙述的古希腊某人发明的筛选法.小素数因子从2开始取,取几百个(论文中将小素数因子个数记为NP).

(3)     对范围内没有去掉的数逐一进行素数测试,一个数如果通过测试次数达到一定标准,就判为素数.

(4)     如果范围内没找到素数,就令N=N+SS,回到(2)继续寻找.

用以上算法,直到以某成功概率得到素数为止

综上所述,总结素数寻找的流程,如图2-6所示。

96cc5bedf6cdccfb36e967c021569c65.png

得到了大素数,即RSA算法中的p、q,我们就可以计算出密钥,进行加密等操作了。

4. 二元一次不定方程

在RSA 算法中,往往要在已知A、M的情况下,求B的最小值,使得 (AB)mod M = 1。即相当于求解B、N都是未知数的二元一次不定方程 AB-MN=1的最小整数解。

而针对不定方程ax-by=1的最小整数解,古今中外都进行过详尽的研究,西方有著名的欧几里德算法,即一种辗转相除法,中国有秦九韶的“大衍求一术”。欧几里德算法是一种递归算法,较容易理解。下面举例说明用欧几里德算法求解二元一次不定方程的最小整数解。

给定不定方程11x-49y=1,求最小的x

(1) 11 x - 49 y = 1    49 mod 11 = 5

(2) 11 x - 5 y = 1    11 mod 5 = 1

(3)   x- 5 y = 1          5 mod 1 = 0

逆向代入:

令y=0 代入(3)得x=1

令x=1 代入(2)得y=2

令y=2 代入(1)得x=9

x=9;y=2即为所求。

程序中,全局函数vlong modinv(const vlong &a, const vlong &m )用来完成这种算法。对应前面的叙述,参数a对应A,参数m对应M,函数返回值即为B的最小值。

5. RSA算法实现加密与解密

最后,类RSA_san基于前面的准备工作,实现RSA密钥生成和加解密的功能(算法在此不再赘述,RSA算法协议见(http://www.di-mgt.com.au/rsa_alg.html)。为了方便阅读,整个类的源程序中,所使用的变量字母均和RSA算法协议中一致。在类RSA_san的构造函数里,执行准备一对随机密钥的操作。之后可以直接使用类的其他成员进行RSA加解密操作。类中各成员频繁的用到字符串和vlong类型的转换,因为大数是用字符串置入的,而把大数读出,也是保存在字符指针指向的一段内存空间里,所以也是字符串。所以,需要实现一系列的编码转换函数,比如将unsigned指针指向的一段空间里保存的一个大数,表示成十六进制形式的字符串文本。编码转换通常是用C风格的指针操作和sprintf函数来完成。

需要加密和解密的数据也是通过字符串参数置入的。由于字符串的结尾字符“\0”实际上也可能是需要加密的数据,所以置入的串长度并不能以“\0”来决定,程序里引入一个unsigned类型的参数来决定置入的串长度,这样就解决了加密连0数据时候被截断的问题。

因为是对文件加密的软件,需要加密的数据通常并不止几字节,本软件默认的分块大小是1字节,即逐个字节作为参数,调用C++核心模块中的方法。

加密解密流程均为标准RSA算法,具体过程见下图:

①生成密钥:

97d0496980b63b0f828b992beb4a97b1.png

相关代码:

public staticint GetRandomString()//实现随机字串的获得

             {

                    Randomrnd = new Random();

                    Byte[]b=newByte[System.Math.Max(RSAprimeplen1,RSAprimeplen2)];

                    s1="";

                    s2="";

                    for(inti=0;i

                    {

                           Bytetmp=System.Convert.ToByte(254.0*rnd.NextDouble());

                           if(tmp!=0)b[i]=tmp;elseb[i]=1;                   }

                    s1=wujunjie_rsa.FromASCIIByteArray(b);

                    for(inti=0;i

                    {

                           Bytetmp=System.Convert.ToByte(254.0*rnd.NextDouble());

                           if(tmp!=0)b[i]=tmp;elseb[i]=1;

                    }

                    s2=wujunjie_rsa.FromASCIIByteArray(b);

                    return1;}

②加密过程:

a8b5c625b486b2e1dfb909257687a07b.png

e1da97624ecff9e18c29527a9adc2bfc.png

2dcf2c19b8dea292e554576c5ffe4dd2.png

相关代码:

private voidmenuItem10_Click(object sender,System.EventArgs e)//公钥加密

             {

                    if(wujunjie_rsa.charlist.Count==0)

                    {

                           emptymsgem=new emptymsg(this);

                           em.Show();

                           return;

                    }

                    StreammyStream ;

                    SaveFileDialogsaveFileDialog1 = new SaveFileDialog();

                    saveFileDialog1.Filter= "Hex text files (*.hextxt)|*.hextxt|All files (*.*)|*.*"  ;

                    saveFileDialog1.FilterIndex= 1 ;

                    saveFileDialog1.RestoreDirectory= true ;

                    if(saveFileDialog1.ShowDialog() == DialogResult.OK)

                    {

                           if((myStream=saveFileDialog1.OpenFile()) != null)

                           {

                                  textBox1.Text+="\r\n正在对读入的文件进行处理,请稍候:)\r\n";

                                  System.Threading.Thread.Sleep(500);

                                  HighResolutionTimertimer = new HighResolutionTimer();

                                  timer.Start();

                                  using (StreamWriter sw = newStreamWriter(myStream))

                                  {

                                         sw.WriteLine("#RSA.HexText");

                                         sw.WriteLine("#___________________________________________");

                                         Byte[]b=new Byte[wujunjie_rsa.RSAstep];

                                         wujunjie_rsa.result_hexstrings.Clear();

                                         progressBar1.Minimum=0;

                                         progressBar1.Maximum=wujunjie_rsa.charlist.Count;

                                         for(inti=0;i

                                         {

                                                for(int j=0;j

                                                {

                                                       b[j]=System.Convert.ToByte(wujunjie_rsa.charlist[i+j]);

                                                }

                                                string s;

                                                 wujunjie_rsa.RSA_san_en(b,wujunjie_rsa.RSAstep);

                                                s=wujunjie_rsa.get_result_hexstring();

                                                wujunjie_rsa.result_hexstrings.Add(s);

                                                progressBar1.Value=i+1;

                                         }

                                         for(inti=0;i

                                         {

                                                stringhs=System.Convert.ToString(wujunjie_rsa.result_hexstrings[i]);

                                                if(hs==null||hs=="")sw.WriteLine("0");

                                                else sw.WriteLine(hs);

                                         }

                                         sw.WriteLine("#___________________________________________");

                                         sw.Write("#");

                                         sw.WriteLine(DateTime.Now);

                                         wujunjie_rsa.result_hexstrings.Clear();

                                  }

                                  myStream.Close();

                                  timer.Stop();

                                  textBox1.Text+="\r\n消耗时间:"+timer.ElapsedTime+"\r\n";

                                  textBox1.Text+="\r\n处理完成,新生成文件\r\n"+saveFileDialog1.FileName+"\r\n";

                                  progressBar1.Value=0;

                           }

                    }

             }

e3081a9b0a7e723292a383a52697529f.png

637293c3f92ea66640ae4fc95e5caa38.png

相关代码:

private voidmenuItem8_Click(object sender, System.EventArgse)//私钥解密

             {

                    if(wujunjie_rsa.hextxtlist.Count==0)

                    {

                           emptymsgem=new emptymsg(this);

                           em.Show();

                           return;

                    }

                    StreammyStream ;

                    SaveFileDialogsaveFileDialog1 = new SaveFileDialog();

                    saveFileDialog1.Filter= "Text files (*.txt)|*.txt|All files (*.*)|*.*"  ;

                    saveFileDialog1.FilterIndex= 2 ;

                    saveFileDialog1.RestoreDirectory= true ;

                    if(saveFileDialog1.ShowDialog() == DialogResult.OK)

                    {

                           if((myStream=saveFileDialog1.OpenFile())!= null)

                           {

                                  textBox1.Text+="\r\n正在对十六进制文本进行处理,请稍候:)\r\n";

                                  System.Threading.Thread.Sleep(500);

                                  HighResolutionTimertimer = new HighResolutionTimer();

                                  timer.Start();

                                  using (BinaryWriter bn = newBinaryWriter(myStream))

                                  {

                                         progressBar1.Minimum=0;

                                        progressBar1.Maximum=wujunjie_rsa.hextxtlist.Count;

                                         for(inti=0;i

                                         {

                                         wujunjie_rsa.RSA_san_dn_hexstring(System.Convert.ToString(wujunjie_rsa.hextxtlist[i]));

                                                for(uintj=0;j

                                                {

                                                       bn.Write(wujunjie_rsa.get_result_byte(j));

                                                }

                                                progressBar1.Value=i+1;

                                         }

                                  }

                                  myStream.Close();

                                  timer.Stop();

                                  textBox1.Text+="\r\n消耗时间:"+timer.ElapsedTime+"\r\n";

                                  textBox1.Text+="\r\n处理完成,新生成文件\r\n"+saveFileDialog1.FileName+"\r\n";

                                  progressBar1.Value=0;}

                    }    

             }

2f08e6b4bd8285fb83c4d9eba8fb7f05.png源文件

b79ea3c595204b7b0f5048faa1b9846d.png

e74349769159049419af551db65ebc06.png

b26c5228f270f6967176b933c8a95d36.png

e78d8c2cfe7f0d6eee5b703f204fb534.gif

详情请关注小编继续了解,免费赠送源代码与论文哦!

计算机毕业设计(源程序+论文+开题报告+文献综述+翻译+答辩稿)

联系QQ:2932963541进行咨询

e7dfbf4c4833543b5a269eb399afa325.png 0bb382cef285fc3ef1e22d1865cb5a66.png 网站地址: http://www.webtmall.com/ 扫码关注最新动态更多精彩,点击下方“
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值