一个性能还不错的DES加密库(转换自同事改写的一份C++代码)

通过改进DES加密算法并解决线程安全问题,实现加密性能提升至接近未加密状态的45%-50%,介绍具体实现过程及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于项目需要将日志进行加密,并且合同附件写明是使用DES,不得已而做了下来,经过测试,性能仅为原来没有加密前的九至十分之一,而现成的东西不确定是否为线程安全的,即便是使用DLL库,进行线程分离加载也算大废周章。难耐之下,取来一C++的DES加密单元进行了代码转换,直接将其中公共变量(存在线程安全问题)声明改为threadval,调入程序当中,发现性能还不错,是原来加密库的四至五倍,接近不加密时的45%~50%,多少也算了过得去。下面列出源代码和用例(时间关系,注释都是原来C++单元当中搬过来的):

unit UnitDES;
{$ifndef LITTLE_ENDIAN}
    
{$define LITTLE_ENDIAN 1}
{$endif}
interface
const
(
* 32-bit permutation function P used on the output of the S-boxes *)
p32i: array[
0..31] of Byte = (
    
1672021,
    
29122817,
    
1152326,
    
5183110,
    
282414,
    
322739,
    
1913306,
    
2211425
);

(
* The (in)famous S-boxes *)
     si: array[
0..7,0..63] of Byte =
(
    (
* S1 *)
    (
1441312151183106125907,
    
0157414213110612119538,
    
4114813621115129731050,
    
1512824917511314100613),

    (
* S2 *)
    (
1518146113497213120510,
    
3134715281412011069115,
    
0147111041315812693215,
    
1381013154211671205149),

    (
* S3 *)
    (
1009146315511312711428,
    
1370934610285141211151,
    
1364981530111212510147,
    
1101306987415143115212),
    
    (
* S4 *)
    (
7131430691012851112415,
    
1381156150347212110149,
    
1069012117131513145284,
    
3150610113894511127214),

    (
* S5 *)
    (
2124171011685315130149,
    
1411212471315015103986,
    
4211110137815912563014,
    
1181271142136150910453),
    
    (
* S6 *)
    (
1211015926801334147511,
    
1015427129561131401138,
    
9141552812370410113116,
    
4321295151011141760813),
    
    (
* S7 *)
    (
4112141508133129751061,
    
1301174911014351221586,
    
1411131237141015680592,
    
6111381410795015142312),
    
    (
* S8 *)
    (
1328461511110931450127,
    
1151381037412561101492,
    
7114191214206101315358,
    
2114741081315129035611)
);

(
* bit 0 is left-most in byte *)
bytebit: array [
0..7] of Integer =
(
    
02000100040020010040201
);

nibblebit : array [
0..3] of Integer =
(
    
010040201
);

(
* initial permutation IP *)
ip :array[
0..63] of Byte =
(
    
585042342618102,
    
605244362820124,
    
625446383022146,
    
645648403224168,
    
57494133251791,
    
595143352719113,
    
615345372921135,
    
635547393123157
);

(
* final permutation IP^-1 *)
fp: array[
0..63] of Byte =
(
    
408481656246432,
    
397471555236331,
    
386461454226230,
    
375451353216129,
    
364441252206028,
    
353431151195927,
    
342421050185826,
    
33141949175725
);

(
* permuted choice table (key) *)
pc1: array[
0..55]of Byte =
(
    
5749413325179,
    
1585042342618,
    
1025951433527,
    
1911360524436,

    
63554739312315,
    
7625446383022,
    
1466153453729,
    
211352820124
);

(
* number left rotations of pc1 *)
totrot: array [
0..15] of Byte =
(
    
124681012141517192123252728
);


(
* permuted choice key (table) *)
 pc2:array[
0..47] of Byte =
(
    
1417112415,
    
3281562110,
    
2319124268,
    
1672720132,
    
415231374755,
    
304051453348,
    
444939563453,
    
464250362932
);
//var
threadvar
(
* Lookup tables initialized once only at startup by desinit() *)
  sp: array [
0..7,0..63]  of LongWord; (* Combined S and P boxes *)
  iperm: array [
0..15,0..15,0..7]  of Byte;(* Initial and final permutations *)
  fperm: array[
0..15,0..15,0..7]  of Byte;
  (
* 8 6-bit subkeys for each of 16 rounds, initialized by dessetkey() *)
  kn: array [
0..15,0..7]  of Byte;
  desmode: Integer;
function desinit(mode: Integer):Integer;
function dessetkey(
const key:PByte): Integer;
procedure endes(block:PByte);
procedure dedes(block:PByte);
function spinit: Integer;(
*static*)
procedure DES_round(num:Integer; block:PLongWord);(
*static*)

implementation
{$ifdef    LITTLE_ENDIAN}
(
* Byte swap a long *)
//function byteswap(x:LongWord):LongWord;
//var
//    cp,CP1:PChar;
//  tmp:Char;(*register*)
//begin
//    cp := PChar( @x);
//  Inc(cp,3);
//    tmp := cp^;
//  cp1 := cp;
//  Dec(cp,3);
//    cp1^ := cp^;
//    cp^ := tmp;
//
//  Dec(cp1);
//  Inc(cp);
//    tmp := cp1^;
//    cp1^ := cp^;
//    cp^ := tmp;
//
//    Result := x;
//end;
//{$else}
function byteswap(x:LongWord):LongWord;assembler;
asm
  bswap eax
end;
{$endif}                (* LITTLE_ENDIAN *)

(
* Initialize the lookup table for the combined S and P boxes *)
function spinit: Integer;
var
    pbox: array [
0..31] of Byte;
    p, i, s, j, rowcol: Integer;
    val: longInt;
begin
    (
*
    
* Compute pbox, the inverse of p32i. This is easier to work with
    
*)
    
for p := 0 to  32-1 do
    begin
        
for i := 0 to 32-1 do
        begin
            
if (p32i[i] - 1 = p) then
            begin
                pbox[p] :
= i;
                
break;
            end;
        end;
    end;
    
for s := 0 to 8 - 1 do
    begin    (
* For each S-box *)
        
for i := 0 to 64 - 1 do
        begin    (
* For each possible input *)
            val :
= 0;
            (
*
            
* The row number is formed from the first and last bits; the
            
* column number is from the middle 4
            
*)

            
//rowcol := (i and 32) or ((i and 1) ? 16 : 0) or ((i shr 1) and $f);
      if (i and 1<> 0 then
        rowcol :
= (i and 32) or ( 16 ) or ((i shr 1) and $f)
      
else
        rowcol :
= (i and 32) or ( 0) or ((i shr 1) and $f);

            
for j := 0 to 4-1 do
            begin    (
* For each output bit *)
                
if (si[s][rowcol] and (8 shr j)) <> 0 then
                begin
                    val :
= val or (1 shl (31 - pbox[4 * s + j]));
                end;
            end;
            sp[s][i] :
= val;
        end;
    end;
    Result :
= 0;
end;

(
* The nonlinear function f(r,k), the heart of DES *)
//    unsigned long    r;        /* 32 bits */
//    unsigned char    subkey[8];    /* 48-bit key for this round */
function f( r:LongWord; subkey:PByte):LongWord;(*static*)
var
     rval, rt:LongWord;(
*register *)
begin
    (
*
    
* Run E(R) ^ K through the combined S & P boxes This code takes
    
* advantage of a convenient regularity in E, namely that each group of 6
    
* bits in E(R) feeding a single S-box is a contiguous segment of R.
    
*)
    
//rt := (r shr 1) or ((r and 1) ? $80000000 : 0);
  if (r and 1<> 0 then
    rt :
= (r shr 1) or $80000000
  
else
    rt :
= (r shr 1) or 0;

    rval :
= 0;
    rval :
=rval or ( sp[0][((rt shr 26) xor subkey^) and $3f]);
  Inc(subkey);
    rval :
=rval or ( sp[1][((rt shr 22) xor subkey^) and $3f]);
  Inc(subkey);
    rval :
=rval or ( sp[2][((rt shr 18) xor subkey^) and $3f]);
  Inc(subkey);
    rval :
=rval or ( sp[3][((rt shr 14) xor subkey^) and $3f]);
  Inc(subkey);
    rval :
=rval or ( sp[4][((rt shr 10) xor subkey^) and $3f]);
  Inc(subkey);
    rval :
=rval or ( sp[5][((rt shr 6) xor subkey^) and $3f]);
  Inc(subkey);
    rval :
=rval or ( sp[6][((rt shr 2) xor subkey^) and $3f]);
  Inc(subkey);
    
//rt := (r shl 1) or ((r and $80000000) ? 1 : 0);
  if (r and $80000000)<>0 then
    rt :
= (r shl 1) or ( 1 )
  
else
    rt :
= (r shl 1) or (0);
  
    rval :
=rval or sp[7][(rt xor subkey^) and $3f];
    
    result :
= rval;
end;

(
* Do one DES cipher round *)
//    int             num;    /* i.e. the num-th one     */
//    unsigned long  *block;
procedure DES_round(num:Integer; block:PLongWord);(*static*)
var
  block1:PLongWord;
begin
    
//    long            f();

    (
*
    
* The rounds are numbered from 0 to 15. On even rounds the right half is
    
* fed to f() and the result exclusive-ORs the left half; on odd rounds
    
* the reverse is done.
    
*)
  block1 :
= block;
  Inc(block1);
    
if (num and 1)<> 0 then
    begin
        block1
^ :=block1^ xor f(block^, @kn[num]);
    end
    
else
    begin
        block
^ :=block^ xor f(block1^, @kn[num]);
    end;
end;

(
* Allocate space and initialize DES lookup arrays
* mode == 0: standard Data Encryption Algorithm
* mode == 1: DEA without initial and final permutations for speed
* mode == 2: DEA without permutations and with 128-byte key (completely
*              independent subkeys for each round)
*)
function desinit(mode: Integer):Integer;
procedure iperminit;

var
  l, j, k: Integer;(
*register*)
    i, m: Integer;
begin
    (
* Clear the permutation array *)
    
for i := 0 to 16 - 1 do
    begin
        
for j := 0 to 16-1 do
        begin
            
for k := 0 to 8-1 do
            begin
                iperm[i][j][k] :
= 0;
            end;
        end;
    end;
    
    
for i := 0 to 16-1 do    (* each input nibble position *)
    begin
        
for j := 0 to 16-1 do(* each possible input nibble *)
        begin
            
for k := 0 to 64-1 do
            begin    (
* each output bit position *)
                l :
= ip[k] - 1;    (* where does this bit come from *)
                
if ((l shr 2<> i)    then (* does it come from input posn? *)
                    
continue;    (* if not, bit k is 0     *)
                
if (0 = (j and nibblebit[l and 3])) then
                    
continue;    (* any such bit in input? *)
                m :
= k and 07; (* which bit is this in the byte *)
                iperm[i][j][k shr 
3] := iperm[i][j][k shr 3] or bytebit[m];
            end;
    end;
    end;
end;

procedure fperminit;

var
  l, j, k: Integer;(
*register*)
    i, m: Integer;
begin
    (
* Clear the permutation array *)
    
for i := 0 to 16 - 1 do
    begin
        
for j := 0 to 16-1 do
        begin
            
for k := 0 to 8-1 do
            begin
                fperm[i][j][k] :
= 0;
            end;
        end;
    end;
    
    
for i := 0 to 16-1 do    (* each input nibble position *)
    begin
        
for j := 0 to 16-1 do(* each possible input nibble *)
        begin
            
for k := 0 to 64-1 do
            begin    (
* each output bit position *)
                l :
= fp[k] - 1;    (* where does this bit come from *)
                
if ((l shr 2<> i)    then (* does it come from input posn? *)
                    
continue;    (* if not, bit k is 0     *)
                
if (0 = (j and nibblebit[l and 3])) then
                    
continue;    (* any such bit in input? *)
                m :
= k and 07; (* which bit is this in the byte *)
                fperm[i][j][k shr 
3] := fperm[i][j][k shr 3] or bytebit[m];
            end;
    end;
    end;
end;
begin
  Result :
= 0;
    desmode :
= mode;
    spinit();
    
if (mode = 1) or (mode = 2) then(* No permutations *)
    Exit;
  iperminit;
  fperminit;
end;


(
* Set key (initialize key schedule array) *)
//    char           *key;    /* 64 bits (will use only 56) */
function dessetkey(const key:PByte): Integer;
var
    pc1m:array [
0..55] of Byte;    (* place to modify pc1 into *)
    pcr: array [
0..55] of Byte;    (* place to rotate pc1 into *)
    i, j, l:Integer;(
*register*)
    M:integer;
  Key1:PByte;
begin
    (
*
    
* In mode 2, the 128 bytes of subkey are set directly from the user's
    * key, allowing him to use completely independent subkeys for each
    
* round. Note that the user MUST specify a full 128 bytes.
    
*
    
* I would like to think that this technique gives the NSA a real headache,
    
* but I'm not THAT naive.
    *)
  Result :
= -1;
    
if (desmode = 2) then
    begin
    Key1 :
= Key;
        
for i := 0 to 16-1 do
        begin
            
for j := 0 to 8-1 do
            begin
                kn[i][j] :
= key1^;
        Inc(Key1);
            end;
        end;
        Exit;
    end;
    (
* Clear key schedule *)
    
for i := 0 to 16-1 do
    begin
        
for j := 0 to 8-1 do
        begin
            kn[i][j] :
= 0;
        end;
    end;
    
for j := 0 to 56-1 do
    begin    (
* convert pc1 to bits of key *)
        l :
= pc1[j] - 1;     (* integer bit location  *)
        m :
= l and 07;     (* find bit      *)
        
//pc1m[j] := (key[l shr 3]  and (* find which key byte l is in *)
        
//    bytebit[m])    (* and which bit of that byte *)
        
//    ? 1 : 0;        (* and store 1-bit result *)
    if (PByte(Integer(key)+(l shr 3))^  and bytebit[m]) <> 0 then
      pc1m[j] :
= 1
    
else
      pc1m[j] :
= 0;
    end;
    
for i := 0 to 16-1 do
    begin    (
* key chunk for each iteration *)
        
for j := 0 to 56-1 do(* rotate pc1 the right amount *)
    begin
            
//pcr[j] := pc1m[(l = j + totrot[i]) < (j < 28 ? 28 : 56) ? l : l - 28];
      l := j + totrot[I];
      
if J < 28 then
        begin
          
if l < 28 then
            begin
              pcr[j] :
= pc1m[l];
            end
          
else
            begin
              pcr[j] :
= pc1m[l-28];
            end;
        end
      
else
        begin
          
if l < 56 then
            begin
              pcr[j] :
= pc1m[l];
            end
          
else
            begin
              pcr[j] :
= pc1m[l-28];
            end;
        end;
    end;
        (
* rotate left and right halves independently *)
        
for j := 0 to 48-1 do
        begin    (
* select bits individually *)
            (
* check bit that goes to kn[j] *)
            
if (pcr[pc2[j] - 1<> 0) then
            begin
                (
* mask it in if it's there *)
                l := j mod 6;
                kn[i][j div 
6] :=kn[i][j div 6] or (bytebit[l] shr 2);
            end;
        end;
    end;
    Result :
= 0;
end;

(
* In-place encryption of 64-bit block *)
procedure endes(block:PByte);
var
    i:Integer;(
*register*)
    work:array [
0..1] of LongWord;    (* Working data storage *)
    tmp:LongInt;
function Ipermute:Integer;(
*static*)
var
    i, j:Integer;(
*register*)
    ib,ob:PByte;(
*register*)    (* ptr to input or output block *)
  p, q:PByte;(
*register*)
begin
  Result :
= 0;
    
if (iperm[0][0][0= 0) then
    begin
        (
* No permutation, just copy *)
    ib :
= block;
    ob :
= PByte(@work[0]);
        
for i := 8 downto 1 do
      begin
              ob
^:= ib^;
        Inc(ib);
        Inc(ob);
      end;
        Exit;
    end;

    (
* Clear output block     *)
  ob :
= PByte(@work[0]);
    
for i := 8 downto 1 do
    begin
          ob
^ := 0;
      Inc(ob);
    end;

    ib :
= block;
  J :
= 0;
    
while j < 16 do
    begin (
* for each input nibble *)
        ob :
= PByte(@work[0]);
        p :
= @iperm[j][(ib^ shr 4) and 017];
        q :
= @iperm[j + 1][ib^ and 017];
        
for i := 8 downto 1 do
        begin    (
* and each output byte *)
            ob
^ :=ob^ or (p^ or q^);    (* OR the masks together *)
      inc(q);
      inc(p);
      Inc(ob);
        end;
    Inc(ib);
    Inc(J,
2);
  end;
end;
function fpermute:Integer;(
*static*)
var
    i, j:Integer;(
*register*)
    ib,ob:PByte;(
*register*)    (* ptr to input or output block *)
  p, q:PByte;(
*register*)
begin
  Result :
= 0;
    
if (fperm[0][0][0= 0) then
    begin
        (
* No permutation, just copy *)
    ib :
= PByte(@work[0]);
    ob :
= block;
        
for i := 8 downto 1 do
      begin
              ob
^:= ib^;
        Inc(ib);
        Inc(ob);
      end;
        Exit;
    end;

    (
* Clear output block     *)
  ob :
= block;
    
for i := 8 downto 1 do
    begin
          ob
^ := 0;
      Inc(ob);
    end;

    ib :
= PByte(@work[0]);
  j :
= 0;
    
while j < 16 do
    begin (
* for each input nibble *)
        ob :
= block;
        p :
= @fperm[j][(ib^ shr 4) and 017];
        q :
= @fperm[j + 1][ib^ and 017];
        
for i := 8 downto 1 do
        begin    (
* and each output byte *)
            ob
^ :=ob^ or (p^ or q^);    (* OR the masks together *)
      inc(q);
      inc(p);
      Inc(ob);
        end;
    Inc(ib);
    Inc(J,
2);
  end;
end;
begin
  Ipermute; (
* Initial Permutation *)

{$ifdef LITTLE_ENDIAN}
    work[
0] := byteswap(work[0]);
    work[
1] := byteswap(work[1]);
{$endif}                (* LITTLE_ENDIAN *)

    (
* Do the 16 rounds *)
    
for i := 0 to 16-1 do
        DES_round(i, @work[
0]);

    (
* Left/right half swap *)
    tmp :
= work[0];
    work[
0] := work[1];
    work[
1] := tmp;

{$ifdef LITTLE_ENDIAN}
    work[
0] := byteswap(work[0]);
    work[
1] := byteswap(work[1]);
{$endif}                (* LITTLE_ENDIAN *)

  fpermute;  (
* Inverse initial * permutation *)
end;

(
* In-place decryption of 64-bit block *)
procedure dedes(block:PByte);
var
    I: Integer;(
*register*)
    work: array [
0..1] of LongWord;    (* Working data storage *)
    tmp:LongInt;
function Ipermute:Integer;(
*static*)
var
    i, j:Integer;(
*register*)
    ib,ob:PByte;(
*register*)    (* ptr to input or output block *)
  p, q:PByte;(
*register*)
begin
  Result :
= 0;
    
if (iperm[0][0][0= 0) then
    begin
        (
* No permutation, just copy *)
    ib :
= block;
    ob :
= PByte(@work[0]);
        
for i := 8 downto 1 do
      begin
              ob
^:= ib^;
        Inc(ib);
        Inc(ob);
      end;
        Exit;
    end;

    (
* Clear output block     *)
  ob :
= PByte(@work[0]);
    
for i := 8 downto 1 do
    begin
          ob
^ := 0;
      Inc(ob);
    end;

    ib :
= block;
  J :
= 0;
    
while j < 16 do
    begin (
* for each input nibble *)
        ob :
= PByte(@work[0]);
        p :
= @iperm[j][(ib^ shr 4) and 017];
        q :
= @iperm[j + 1][ib^ and 017];
        
for i := 8 downto 1 do
        begin    (
* and each output byte *)
            ob
^ :=ob^ or (p^ or q^);    (* OR the masks together *)
      inc(q);
      inc(p);
      Inc(ob);
        end;
    Inc(ib);
    Inc(J,
2);
  end;
end;
function Fpermute:Integer;(
*static*)
var
    i, j:Integer;(
*register*)
    ib,ob:PByte;(
*register*)    (* ptr to input or output block *)
  p, q:PByte;(
*register*)
begin
  Result :
= 0;
    
if (fperm[0][0][0= 0) then
    begin
        (
* No permutation, just copy *)
    ib :
= PByte(@work[0]);
    ob :
= block;
        
for i := 8 downto 1 do
      begin
              ob
^:= ib^;
        Inc(ib);
        Inc(ob);
      end;
        Exit;
    end;

    (
* Clear output block     *)
  ob :
= block;
    
for i := 8 downto 1 do
    begin
          ob
^ := 0;
      Inc(ob);
    end;

    ib :
= PByte(@work[0]);
  J :
= 0;
    
while j < 16 do
    begin (
* for each input nibble *)
        ob :
= block;
        p :
= @fperm[j][(ib^ shr 4) and 017];
        q :
= @fperm[j + 1][ib^ and 017];
        
for i := 8 downto 1 do
        begin    (
* and each output byte *)
            ob
^ :=ob^ or (p^ or q^);    (* OR the masks together *)
      inc(q);
      inc(p);
      Inc(ob);
        end;
    Inc(ib);
    Inc(J,
2);
  end;
end;
begin
  Ipermute;  (
* Initial permutation *)

{$ifdef LITTLE_ENDIAN}
    work[
0] := byteswap(work[0]);
    work[
1] := byteswap(work[1]);
{$endif}                (* LITTLE_ENDIAN *)

    (
* Left/right half swap *)
    tmp :
= work[0];
    work[
0] := work[1];
    work[
1] := tmp;

    (
* Do the 16 rounds in reverse order *)
    
for i := 15 downto 0 do
        DES_round(i, @work[
0]);

{$ifdef LITTLE_ENDIAN}
    work[
0] := byteswap(work[0]);
    work[
1] := byteswap(work[1]);
{$endif}                (* LITTLE_ENDIAN *)

  Fpermute;  (
* Inverse initial * permutation *)
end;

end.
uses
  UnitDES;
procedure TForm1.btnTestClick(Sender: TObject);
var
  sIn,sOutHex,sKey:String;
  NewLen,I: Integer;
  block :PByte;
  P,P1:PChar;
  B,B1:Byte;
begin
  sIn :
= '测试字符串';
  sKey :
= '这是密钥';

  desinit(
0);

  I :
= Length(sKey);
  SetLength(sKey,
8); //调整密钥为8位
  if I < 8 then
    
//填充密钥
    FillChar(PChar(Integer(PChar(sKey))+I)^,8-I,100);

  I :
= Length(sIn);
  
if (I mod 8 )<>0 then
    begin
      
//调整串长为8的倍数
      NewLen := ((I div 8+ 1* 8;
      SetLength(sIn,NewLen);
      
//置串结束符(这里是串加密
      PChar(Integer(PChar(sIn))+I)^ := #0;
    end;

  
//初始化密钥
  dessetkey(PByte(PChar(sKey)));

  block :
= Pointer(sIn);
  NewLen :
= Length(sIn);
  
while( NewLen > 0 ) do
    begin
      
//加密
      endes( PByte(block));
      Dec(NewLen,
8);
      Inc(block,
8);
    end;

  
//十六进制转换
  SetString(sOutHex,Nil,Length(sIn) * 2);
  P :
= PChar(sIn);
  P1 :
= PChar(sOutHex);
  
for I := 0 to Length(sIn) - 1 do
    begin
      B :
= Byte(P^);
      B1 :
= ((B shr 4) and $f);
      
case B1 of
        
0..9:
          P1
^ := Chr(B1 + 48)
        
else
          P1
^ := Chr(B1 + 65 - 10);
      end;

      Inc(P1);
      B1 :
= (B  and $f);

      
case B1 of
        
0..9:
          P1
^ := Chr(B1 + 48)
        
else
          P1
^ := Chr(B1 + 65 - 10);
      end;

      Inc(P1);
      Inc(P);
    end;

  ShowMessage(sOutHex);

  
//初始化密钥
  dessetkey(PByte(PChar(sKey)));

  NewLen :
= Length(sIn);
  block :
= Pointer(sIn);
  
while( NewLen > 0 ) do
    begin
      
//解密
      dedes(PByte(block));
      Dec(NewLen, 
8);
      Inc(block, 
8);
    end;
  SetLength(sIn,strlen(PChar(sIn)));

  ShowMessage(sIn);
end;
 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值