RFC1321 MD5加密算法

global.h

 1 /* GLOBAL.H - RSAREF types and constants
 2  */
 3 
 4 /* PROTOTYPES should be set to one if and only if the compiler supports
 5 function argument prototyping.
 6 The following makes PROTOTYPES default to 0 if it has not already
 7 been defined with C compiler flags.
 8  */
 9  
10 #ifndef PROTOTYPES
11 #define PROTOTYPES 0
12 #endif
13 
14 /* POINTER defines a generic pointer type */
15 typedef unsigned char *POINTER;
16 
17 /* UINT2 defines a two byte word */
18 typedef unsigned short int UINT2;
19 
20 /* UINT4 defines a four byte word */
21 typedef unsigned long int UINT4;
22 
23 /* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
24 If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
25 returns an empty list.
26  */
27 #if PROTOTYPES
28 #define PROTO_LIST(list) list
29 #else
30 #define PROTO_LIST(list) ()
31 #endif

 

md5.h

 1 /* MD5.H - header file for MD5C.C
 2  */
 3 
 4 /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
 5 rights reserved.
 6 
 7 License to copy and use this software is granted provided that it
 8 is identified as the "RSA Data Security, Inc. MD5 Message-Digest
 9 Algorithm" in all material mentioning or referencing this software
10 or this function.
11 
12 License is also granted to make and use derivative works provided
13 that such works are identified as "derived from the RSA Data
14 Security, Inc. MD5 Message-Digest Algorithm" in all material
15 mentioning or referencing the derived work.
16 
17 RSA Data Security, Inc. makes no representations concerning either
18 the merchantability of this software or the suitability of this
19 software for any particular purpose. It is provided "as is"
20 without express or implied warranty of any kind.
21 These notices must be retained in any copies of any part of this
22 documentation and/or software.
23  */
24 
25 #include "global.h"
26 
27 /* MD5 context. */
28 typedef struct {
29     UINT4 state[4];                                    /* state (ABCD) */
30     UINT4 count[2];                                    /* number of bits, modulo 2^64 (lsb first) */
31     unsigned char buffer[64];                        /* input buffer */
32 } MD5_CTX;
33 
34 void MD5Init(MD5_CTX *);
35 void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
36 void MD5Final(unsigned char [16], MD5_CTX *);

 

md5c.c

  1 /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
  2  */
  3 
  4 /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
  5 rights reserved.
  6 
  7 License to copy and use this software is granted provided that it
  8 is identified as the "RSA Data Security, Inc. MD5 Message-Digest
  9 Algorithm" in all material mentioning or referencing this software
 10 or this function.
 11 
 12 License is also granted to make and use derivative works provided
 13 that such works are identified as "derived from the RSA Data
 14 Security, Inc. MD5 Message-Digest Algorithm" in all material
 15 mentioning or referencing the derived work.
 16 
 17 RSA Data Security, Inc. makes no representations concerning either
 18 the merchantability of this software or the suitability of this
 19 software for any particular purpose. It is provided "as is"
 20 without express or implied warranty of any kind.
 21 
 22 These notices must be retained in any copies of any part of this
 23 documentation and/or software.
 24  */
 25 
 26 #include "global.h"
 27 #include "md5.h"
 28 
 29 /* Constants for MD5Transform routine.
 30  */
 31 #define S11 7
 32 #define S12 12
 33 #define S13 17
 34 #define S14 22
 35 #define S21 5
 36 #define S22 9
 37 #define S23 14
 38 #define S24 20
 39 #define S31 4
 40 #define S32 11
 41 #define S33 16
 42 #define S34 23
 43 #define S41 6
 44 #define S42 10
 45 #define S43 15
 46 #define S44 21
 47 
 48 static void MD5Transform(UINT4 [4], unsigned char [64]);
 49 static void Encode(unsigned char *, UINT4 *, unsigned int);
 50 static void Decode(UINT4 *, unsigned char *, unsigned int);
 51 static void MD5_memcpy(POINTER, POINTER, unsigned int);
 52 static void MD5_memset(POINTER, int, unsigned int);
 53 
 54 static unsigned char PADDING[64] = {
 55   0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 56   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 57   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 58 };
 59 
 60 /* F, G, H and I are basic MD5 functions.
 61  */
 62 #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
 63 #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
 64 #define H(x, y, z) ((x) ^ (y) ^ (z))
 65 #define I(x, y, z) ((y) ^ ((x) | (~z)))
 66 
 67 /* ROTATE_LEFT rotates x left n bits.
 68  */
 69 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
 70 
 71 /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
 72 Rotation is separate from addition to prevent recomputation.
 73  */
 74 #define FF(a, b, c, d, x, s, ac) { \
 75  (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
 76  (a) = ROTATE_LEFT ((a), (s)); \
 77  (a) += (b); \
 78   }
 79 
 80 #define GG(a, b, c, d, x, s, ac) { \
 81  (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
 82  (a) = ROTATE_LEFT ((a), (s)); \
 83  (a) += (b); \
 84   }
 85 
 86 #define HH(a, b, c, d, x, s, ac) { \
 87  (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
 88  (a) = ROTATE_LEFT ((a), (s)); \
 89  (a) += (b); \
 90   }
 91 
 92 #define II(a, b, c, d, x, s, ac) { \
 93  (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
 94  (a) = ROTATE_LEFT ((a), (s)); \
 95  (a) += (b); \
 96   }
 97 
 98 /* MD5 initialization. Begins an MD5 operation, writing a new context.
 99  */
100 void MD5Init (MD5_CTX *context)
101 {
102     context->count[0] = context->count[1] = 0;
103     /* Load magic initialization constants.
104     */
105     context->state[0] = 0x67452301;
106     context->state[1] = 0xefcdab89;
107     context->state[2] = 0x98badcfe;
108     context->state[3] = 0x10325476;
109 }
110 
111 /* MD5 block update operation. Continues an MD5 message-digest
112   operation, processing another message block, and updating the
113   context.
114  */
115 void MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen)
116 {
117     unsigned int i, index, partLen;
118 
119     /* Compute number of bytes mod 64 */
120     index = (unsigned int)((context->count[0] >> 3) & 0x3F);
121 
122     /* Update number of bits */
123     if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3))
124     {
125         context->count[1]++;
126     }
127     context->count[1] += ((UINT4)inputLen >> 29);
128 
129     partLen = 64 - index;
130 
131     /* Transform as many times as possible.
132     */
133     if (inputLen >= partLen) 
134     {
135         MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen);
136         MD5Transform (context->state, context->buffer);
137 
138         for (i = partLen; i + 63 < inputLen; i += 64)
139         {
140             MD5Transform (context->state, &input[i]);
141         }
142 
143         index = 0;
144     }
145     else
146     {
147         i = 0;
148     }
149     
150     /* Buffer remaining input */
151     MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
152 }
153 
154 /* MD5 finalization. Ends an MD5 message-digest operation, writing the
155   the message digest and zeroizing the context.
156  */
157 void MD5Final (unsigned char digest[16], MD5_CTX *context)
158 {
159     unsigned char bits[8];
160     unsigned int index, padLen;
161 
162     /* Save number of bits */
163     Encode (bits, context->count, 8);
164 
165     /* Pad out to 56 mod 64.
166     */
167     index = (unsigned int)((context->count[0] >> 3) & 0x3f);
168     padLen = (index < 56) ? (56 - index) : (120 - index);
169     MD5Update (context, PADDING, padLen);
170 
171     /* Append length (before padding) */
172     MD5Update (context, bits, 8);
173 
174     /* Store state in digest */
175     Encode (digest, context->state, 16);
176 
177     /* Zeroize sensitive information.
178     */
179     MD5_memset ((POINTER)context, 0, sizeof (*context));
180 }
181 
182 /* MD5 basic transformation. Transforms state based on block.
183  */
184 static void MD5Transform (UINT4 state[4], unsigned char block[64])
185 {
186     UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
187 
188     Decode (x, block, 64);
189 
190     /* Round 1 */
191     FF (a, b, c, d, x[ 0], S11, 0xd76aa478);        /* 1 */
192     FF (d, a, b, c, x[ 1], S12, 0xe8c7b756);        /* 2 */
193     FF (c, d, a, b, x[ 2], S13, 0x242070db);        /* 3 */
194     FF (b, c, d, a, x[ 3], S14, 0xc1bdceee);        /* 4 */
195     FF (a, b, c, d, x[ 4], S11, 0xf57c0faf);            /* 5 */
196     FF (d, a, b, c, x[ 5], S12, 0x4787c62a);        /* 6 */
197     FF (c, d, a, b, x[ 6], S13, 0xa8304613);        /* 7 */
198     FF (b, c, d, a, x[ 7], S14, 0xfd469501);        /* 8 */
199     FF (a, b, c, d, x[ 8], S11, 0x698098d8);        /* 9 */
200     FF (d, a, b, c, x[ 9], S12, 0x8b44f7af);        /* 10 */
201     FF (c, d, a, b, x[10], S13, 0xffff5bb1);            /* 11 */
202     FF (b, c, d, a, x[11], S14, 0x895cd7be);        /* 12 */
203     FF (a, b, c, d, x[12], S11, 0x6b901122);        /* 13 */
204     FF (d, a, b, c, x[13], S12, 0xfd987193);        /* 14 */
205     FF (c, d, a, b, x[14], S13, 0xa679438e);        /* 15 */
206     FF (b, c, d, a, x[15], S14, 0x49b40821);        /* 16 */
207 
208     /* Round 2 */
209     GG (a, b, c, d, x[ 1], S21, 0xf61e2562);        /* 17 */
210     GG (d, a, b, c, x[ 6], S22, 0xc040b340);        /* 18 */
211     GG (c, d, a, b, x[11], S23, 0x265e5a51);        /* 19 */
212     GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa);        /* 20 */
213     GG (a, b, c, d, x[ 5], S21, 0xd62f105d);        /* 21 */
214     GG (d, a, b, c, x[10], S22,  0x2441453);        /* 22 */
215     GG (c, d, a, b, x[15], S23, 0xd8a1e681);        /* 23 */
216     GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8);        /* 24 */
217     GG (a, b, c, d, x[ 9], S21, 0x21e1cde6);        /* 25 */
218     GG (d, a, b, c, x[14], S22, 0xc33707d6);        /* 26 */
219     GG (c, d, a, b, x[ 3], S23, 0xf4d50d87);        /* 27 */
220 
221     GG (b, c, d, a, x[ 8], S24, 0x455a14ed);        /* 28 */
222     GG (a, b, c, d, x[13], S21, 0xa9e3e905);        /* 29 */
223     GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8);        /* 30 */
224     GG (c, d, a, b, x[ 7], S23, 0x676f02d9);        /* 31 */
225     GG (b, c, d, a, x[12], S24, 0x8d2a4c8a);        /* 32 */
226 
227     /* Round 3 */
228     HH (a, b, c, d, x[ 5], S31, 0xfffa3942);        /* 33 */
229     HH (d, a, b, c, x[ 8], S32, 0x8771f681);        /* 34 */
230     HH (c, d, a, b, x[11], S33, 0x6d9d6122);        /* 35 */
231     HH (b, c, d, a, x[14], S34, 0xfde5380c);        /* 36 */
232     HH (a, b, c, d, x[ 1], S31, 0xa4beea44);        /* 37 */
233     HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9);        /* 38 */
234     HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60);        /* 39 */
235     HH (b, c, d, a, x[10], S34, 0xbebfbc70);        /* 40 */
236     HH (a, b, c, d, x[13], S31, 0x289b7ec6);        /* 41 */
237     HH (d, a, b, c, x[ 0], S32, 0xeaa127fa);        /* 42 */
238     HH (c, d, a, b, x[ 3], S33, 0xd4ef3085);        /* 43 */
239     HH (b, c, d, a, x[ 6], S34,  0x4881d05);        /* 44 */
240     HH (a, b, c, d, x[ 9], S31, 0xd9d4d039);        /* 45 */
241     HH (d, a, b, c, x[12], S32, 0xe6db99e5);        /* 46 */
242     HH (c, d, a, b, x[15], S33, 0x1fa27cf8);        /* 47 */
243     HH (b, c, d, a, x[ 2], S34, 0xc4ac5665);        /* 48 */
244 
245     /* Round 4 */
246     II (a, b, c, d, x[ 0], S41, 0xf4292244);        /* 49 */
247     II (d, a, b, c, x[ 7], S42, 0x432aff97);            /* 50 */
248     II (c, d, a, b, x[14], S43, 0xab9423a7);        /* 51 */
249     II (b, c, d, a, x[ 5], S44, 0xfc93a039);        /* 52 */
250     II (a, b, c, d, x[12], S41, 0x655b59c3);        /* 53 */
251     II (d, a, b, c, x[ 3], S42, 0x8f0ccc92);            /* 54 */
252     II (c, d, a, b, x[10], S43, 0xffeff47d);            /* 55 */
253     II (b, c, d, a, x[ 1], S44, 0x85845dd1);        /* 56 */
254     II (a, b, c, d, x[ 8], S41, 0x6fa87e4f);            /* 57 */
255     II (d, a, b, c, x[15], S42, 0xfe2ce6e0);        /* 58 */
256     II (c, d, a, b, x[ 6], S43, 0xa3014314);        /* 59 */
257     II (b, c, d, a, x[13], S44, 0x4e0811a1);        /* 60 */
258     II (a, b, c, d, x[ 4], S41, 0xf7537e82);        /* 61 */
259     II (d, a, b, c, x[11], S42, 0xbd3af235);        /* 62 */
260     II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb);        /* 63 */
261     II (b, c, d, a, x[ 9], S44, 0xeb86d391);        /* 64 */
262 
263     state[0] += a;
264     state[1] += b;
265     state[2] += c;
266     state[3] += d;
267 
268     /* Zeroize sensitive information.
269     */
270     MD5_memset ((POINTER)x, 0, sizeof (x));
271 }
272 
273 /* Encodes input (UINT4) into output (unsigned char). Assumes len is
274   a multiple of 4.
275  */
276 static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
277 {
278     unsigned int i, j;
279 
280     for (i = 0, j = 0; j < len; i++, j += 4) 
281     {
282         output[j] = (unsigned char)(input[i] & 0xff);
283         output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
284         output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
285         output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
286     }
287 }
288 
289 /* Decodes input (unsigned char) into output (UINT4). Assumes len is
290   a multiple of 4.
291  */
292 static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
293 {
294     unsigned int i, j;
295 
296     for (i = 0, j = 0; j < len; i++, j += 4)
297     {
298         output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
299     }
300 }
301 
302 /* Note: Replace "for loop" with standard memcpy if possible.
303  */
304 static void MD5_memcpy (POINTER output, POINTER input, unsigned int len)
305 {
306     unsigned int i;
307 
308     for (i = 0; i < len; i++)
309     {
310         output[i] = input[i];
311     }
312 }
313 
314 /* Note: Replace "for loop" with standard memset if possible.
315  */
316 static void MD5_memset (POINTER output, int value, unsigned int len)
317 {
318     unsigned int i;
319 
320     for (i = 0; i < len; i++)
321     {
322         ((char *)output)[i] = (char)value;
323     }
324     
325 }

 

mddriver.c

  1 /* MDDRIVER.C - test driver for MD2, MD4 and MD5
  2  */
  3 
  4 /* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
  5 rights reserved.
  6 
  7 RSA Data Security, Inc. makes no representations concerning either
  8 the merchantability of this software or the suitability of this
  9 software for any particular purpose. It is provided "as is"
 10 without express or implied warranty of any kind.
 11 
 12 These notices must be retained in any copies of any part of this
 13 documentation and/or software.
 14  */
 15 
 16 /* The following makes MD default to MD5 if it has not already been
 17   defined with C compiler flags.
 18  */
 19 #ifndef MD
 20 #define MD 5
 21 #endif
 22 
 23 #include <stdio.h>
 24 #include <time.h>
 25 #include <string.h>
 26 #include <stdlib.h>
 27 #include <Windows.h>
 28 #include "global.h"
 29 #include "md5.h"
 30 
 31 /* Length of test block, number of test blocks.
 32  */
 33 #define TEST_BLOCK_LEN 1000
 34 #define TEST_BLOCK_COUNT 1000
 35 
 36 static void MDString(char *);
 37 static void MDTimeTrial(void);
 38 static void MDTestSuite(void);
 39 static void MDFile(char *);
 40 static void MDFilter(void);
 41 static void MDPrint(unsigned char [16]);
 42 
 43 #define MD_CTX MD5_CTX
 44 #define MDInit MD5Init
 45 #define MDUpdate MD5Update
 46 #define MDFinal MD5Final
 47 
 48 
 49 /* Main driver.
 50 Arguments (may be any combination):
 51   -sstring    - digests string
 52   -t            - runs time trial
 53   -x            - runs test script
 54   filename    - digests file
 55   (none)        - digests standard input
 56  */
 57 int main (int argc, char *argv[])
 58 {
 59     int i;
 60 
 61     if (argc > 1)
 62     {
 63         for (i = 1; i < argc; i++)
 64         {
 65             if (argv[i][0] == '-' && argv[i][1] == 's')
 66             {
 67                 MDString (argv[i] + 2);
 68             }
 69             else if (strcmp (argv[i], "-t") == 0)
 70             {
 71                 MDTimeTrial ();
 72             }
 73             else if (strcmp (argv[i], "-x") == 0)
 74             {
 75                 MDTestSuite ();
 76             }
 77             else
 78             {
 79                 MDFile (argv[i]);
 80             }
 81         }
 82     }
 83     else
 84     {
 85         MDFilter ();
 86     }
 87 
 88     return (0);
 89 }
 90 
 91 /* Digests a string and prints the result.
 92  */
 93 static void MDString(char *string)
 94 {
 95     MD_CTX context;
 96     unsigned char digest[16];
 97     unsigned int len = strlen (string);
 98 
 99     MDInit (&context);
100     MDUpdate (&context, string, len);
101     MDFinal (digest, &context);
102 
103     printf ("MD%d (\"%s\") = ", MD, string);
104     MDPrint (digest);
105     printf ("\n");
106 }
107 
108 /* Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte
109   blocks.
110  */
111 static void MDTimeTrial ()
112 {
113     MD_CTX context;
114     time_t endTime, startTime;
115     unsigned char block[TEST_BLOCK_LEN], digest[16];
116     unsigned int i;
117     printf("MD%d time trial. Digesting %d %d-byte blocks ...", MD, TEST_BLOCK_LEN, TEST_BLOCK_COUNT);
118 
119     /* Initialize block */
120     for (i = 0; i < TEST_BLOCK_LEN; i++)
121     {
122         block[i] = (unsigned char)(i & 0xff);
123     }
124     
125     /* Start timer */
126     time (&startTime);
127 
128     /* Digest blocks */
129     MDInit (&context);
130     for (i = 0; i < TEST_BLOCK_COUNT; i++)
131     {
132         MDUpdate (&context, block, TEST_BLOCK_LEN);
133     }
134     MDFinal (digest, &context);
135 
136     Sleep(2000);
137     /* Stop timer */
138     time (&endTime);
139 
140     printf (" done\n");
141     printf ("Digest = ");
142     MDPrint (digest);
143     printf ("\nTime = %ld seconds\n", (long)(endTime-startTime));
144     printf("Speed = %ld bytes/second\n", (long)TEST_BLOCK_LEN * (long)TEST_BLOCK_COUNT/(endTime-startTime));
145 }
146 
147 /* Digests a reference suite of strings and prints the results.
148  */
149 static void MDTestSuite ()
150 {
151     printf ("MD%d test suite:\n", MD);
152 
153     MDString("");
154     MDString("a");
155     MDString("abc");
156     MDString("message digest");
157     MDString("abcdefghijklmnopqrstuvwxyz");
158     MDString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
159     MDString("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
160 }
161 
162 /* Digests a file and prints the result.
163 */
164 static void MDFile (char *filename)
165 {
166     FILE *file;
167     MD_CTX context;
168     int len;
169     unsigned char buffer[1024], digest[16];
170 
171     if ((file = fopen (filename, "rb")) == NULL)
172     {
173         printf ("%s can't be opened\n", filename);
174     }
175     else 
176     {
177         MDInit (&context);
178 
179         while (len = fread (buffer, 1, 1024, file))
180         {
181             MDUpdate (&context, buffer, len);
182         }
183         
184         MDFinal(digest, &context);
185 
186         fclose (file);
187 
188         printf ("MD%d (%s) = ", MD, filename);
189         MDPrint (digest);
190         printf ("\n");
191     }
192 }
193 
194 /* Digests the standard input and prints the result.
195  */
196 static void MDFilter ()
197 {
198     MD_CTX context;
199     int len;
200     unsigned char buffer[16], digest[16];
201 
202     MDInit (&context);
203     
204     while (len = fread (buffer, 1, 16, stdin))
205     {
206         MDUpdate (&context, buffer, len);
207     }
208 
209     MDFinal (digest, &context);
210 
211     MDPrint (digest);
212     printf ("\n");
213 }
214 
215 /* Prints a message digest in hexadecimal.
216  */
217 static void MDPrint (unsigned char digest[16])
218 {
219     unsigned int i;
220 
221     for (i = 0; i < 16; i++)
222     {
223         printf ("%02x", digest[i]);
224     }
225     
226 }

 

转载于:https://www.cnblogs.com/paullam/p/3796553.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值