c# 使用 java的 rsa 进行签名

  1     /// <summary>  
  2     /// 类名:RSAFromPkcs8  
  3     /// 功能:RSA加密、解密、签名、验签  
  4     /// 详细:该类对Java生成的密钥进行解密和签名以及验签专用类,不需要修改  
  5     /// 版本:3.0  
  6     /// 日期:2013-07-08  
  7     /// 说明:  
  8     /// 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。  
  9     /// </summary>  
 10     public sealed class RSAFromPkcs8
 11     {
 12         /// <summary>  
 13         /// 签名  
 14         /// </summary>  
 15         /// <param name="content">待签名字符串</param>  
 16         /// <param name="privateKey">私钥</param>  
 17         /// <param name="input_charset">编码格式</param>  
 18         /// <returns>签名后字符串</returns>  
 19         public static string sign(string content, string privateKey, string input_charset)
 20         {
 21             byte[] Data = Encoding.GetEncoding(input_charset).GetBytes(content);
 22             RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
 23             SHA256 sh = new SHA256CryptoServiceProvider();
 24             //SHA1 sh = new SHA1CryptoServiceProvider();
 25             byte[] signData = rsa.SignData(Data, sh);
 26             return Convert.ToBase64String(signData);
 27         }
 28 
 29         /// <summary>  
 30         /// 验签  
 31         /// </summary>  
 32         /// <param name="content">待验签字符串</param>  
 33         /// <param name="signedString">签名</param>  
 34         /// <param name="publicKey">公钥</param>  
 35         /// <param name="input_charset">编码格式</param>  
 36         /// <returns>true(通过),false(不通过)</returns>  
 37         public static bool verify(string content, string signedString, string publicKey, string input_charset)
 38         {
 39             bool result = false;
 40             byte[] Data = Encoding.GetEncoding(input_charset).GetBytes(content);
 41             byte[] data = Convert.FromBase64String(signedString);
 42             RSAParameters paraPub = ConvertFromPublicKey(publicKey);
 43             RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();
 44             rsaPub.ImportParameters(paraPub);
 45             //SHA1 sh = new SHA1CryptoServiceProvider();
 46             SHA256 sh = new SHA256CryptoServiceProvider();
 47             result = rsaPub.VerifyData(Data, sh, data);
 48             return result;
 49         }
 50 
 51         /// <summary>  
 52         /// 加密  
 53         /// </summary>  
 54         /// <param name="resData">需要加密的字符串</param>  
 55         /// <param name="publicKey">公钥</param>  
 56         /// <param name="input_charset">编码格式</param>  
 57         /// <returns>明文</returns>  
 58         public static string encryptData(string resData, string publicKey, string input_charset)
 59         {
 60             byte[] DataToEncrypt = Encoding.ASCII.GetBytes(resData);
 61             string result = encrypt(DataToEncrypt, publicKey, input_charset);
 62             return result;
 63         }
 64 
 65 
 66         /// <summary>  
 67         /// 解密  
 68         /// </summary>  
 69         /// <param name="resData">加密字符串</param>  
 70         /// <param name="privateKey">私钥</param>  
 71         /// <param name="input_charset">编码格式</param>  
 72         /// <returns>明文</returns>  
 73         public static string decryptData(string resData, string privateKey, string input_charset)
 74         {
 75             byte[] DataToDecrypt = Convert.FromBase64String(resData);
 76             string result = "";
 77             for (int j = 0; j < DataToDecrypt.Length / 128; j++)
 78             {
 79                 byte[] buf = new byte[128];
 80                 for (int i = 0; i < 128; i++)
 81                 {
 82 
 83                     buf[i] = DataToDecrypt[i + 128 * j];
 84                 }
 85                 result += decrypt(buf, privateKey, input_charset);
 86             }
 87             return result;
 88         }
 89 
 90         #region 内部方法  
 91 
 92         private static string encrypt(byte[] data, string publicKey, string input_charset)
 93         {
 94             RSACryptoServiceProvider rsa = DecodePemPublicKey(publicKey);
 95             SHA1 sh = new SHA1CryptoServiceProvider();
 96             byte[] result = rsa.Encrypt(data, false);
 97 
 98             return Convert.ToBase64String(result);
 99         }
100 
101         private static string decrypt(byte[] data, string privateKey, string input_charset)
102         {
103             string result = "";
104             RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
105             SHA1 sh = new SHA1CryptoServiceProvider();
106             byte[] source = rsa.Decrypt(data, false);
107             char[] asciiChars = new char[Encoding.GetEncoding(input_charset).GetCharCount(source, 0, source.Length)];
108             Encoding.GetEncoding(input_charset).GetChars(source, 0, source.Length, asciiChars, 0);
109             result = new string(asciiChars);
110             //result = ASCIIEncoding.ASCII.GetString(source);  
111             return result;
112         }
113 
114         private static RSACryptoServiceProvider DecodePemPublicKey(String pemstr)
115         {
116             byte[] pkcs8publickkey;
117             pkcs8publickkey = Convert.FromBase64String(pemstr);
118             if (pkcs8publickkey != null)
119             {
120                 RSACryptoServiceProvider rsa = DecodeRSAPublicKey(pkcs8publickkey);
121                 return rsa;
122             }
123             else
124                 return null;
125         }
126 
127         private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)
128         {
129             byte[] pkcs8privatekey;
130             pkcs8privatekey = Convert.FromBase64String(pemstr);
131             if (pkcs8privatekey != null)
132             {
133                 RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);
134                 return rsa;
135             }
136             else
137                 return null;
138         }
139 
140         private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
141         {
142             byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
143             byte[] seq = new byte[15];
144 
145             MemoryStream mem = new MemoryStream(pkcs8);
146             int lenstream = (int)mem.Length;
147             BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
148             byte bt = 0;
149             ushort twobytes = 0;
150 
151             try
152             {
153                 twobytes = binr.ReadUInt16();
154                 if (twobytes == 0x8130)    //data read as little endian order (actual data order for Sequence is 30 81)  
155                     binr.ReadByte();    //advance 1 byte  
156                 else if (twobytes == 0x8230)
157                     binr.ReadInt16();    //advance 2 bytes  
158                 else
159                     return null;
160 
161                 bt = binr.ReadByte();
162                 if (bt != 0x02)
163                     return null;
164 
165                 twobytes = binr.ReadUInt16();
166 
167                 if (twobytes != 0x0001)
168                     return null;
169 
170                 seq = binr.ReadBytes(15);        //read the Sequence OID  
171                 if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct  
172                     return null;
173 
174                 bt = binr.ReadByte();
175                 if (bt != 0x04)    //expect an Octet string  
176                     return null;
177 
178                 bt = binr.ReadByte();        //read next byte, or next 2 bytes is  0x81 or 0x82; otherwise bt is the byte count  
179                 if (bt == 0x81)
180                     binr.ReadByte();
181                 else
182                     if (bt == 0x82)
183                     binr.ReadUInt16();
184                 //------ at this stage, the remaining sequence should be the RSA private key  
185 
186                 byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
187                 RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
188                 return rsacsp;
189             }
190 
191             catch (Exception)
192             {
193                 return null;
194             }
195 
196             finally { binr.Close(); }
197 
198         }
199 
200         private static bool CompareBytearrays(byte[] a, byte[] b)
201         {
202             if (a.Length != b.Length)
203                 return false;
204             int i = 0;
205             foreach (byte c in a)
206             {
207                 if (c != b[i])
208                     return false;
209                 i++;
210             }
211             return true;
212         }
213 
214         private static RSACryptoServiceProvider DecodeRSAPublicKey(byte[] publickey)
215         {
216             // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"  
217             byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
218             byte[] seq = new byte[15];
219             // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------  
220             MemoryStream mem = new MemoryStream(publickey);
221             BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
222             byte bt = 0;
223             ushort twobytes = 0;
224 
225             try
226             {
227 
228                 twobytes = binr.ReadUInt16();
229                 if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)  
230                     binr.ReadByte();    //advance 1 byte  
231                 else if (twobytes == 0x8230)
232                     binr.ReadInt16();   //advance 2 bytes  
233                 else
234                     return null;
235 
236                 seq = binr.ReadBytes(15);       //read the Sequence OID  
237                 if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct  
238                     return null;
239 
240                 twobytes = binr.ReadUInt16();
241                 if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)  
242                     binr.ReadByte();    //advance 1 byte  
243                 else if (twobytes == 0x8203)
244                     binr.ReadInt16();   //advance 2 bytes  
245                 else
246                     return null;
247 
248                 bt = binr.ReadByte();
249                 if (bt != 0x00)     //expect null byte next  
250                     return null;
251 
252                 twobytes = binr.ReadUInt16();
253                 if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)  
254                     binr.ReadByte();    //advance 1 byte  
255                 else if (twobytes == 0x8230)
256                     binr.ReadInt16();   //advance 2 bytes  
257                 else
258                     return null;
259 
260                 twobytes = binr.ReadUInt16();
261                 byte lowbyte = 0x00;
262                 byte highbyte = 0x00;
263 
264                 if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)  
265                     lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus  
266                 else if (twobytes == 0x8202)
267                 {
268                     highbyte = binr.ReadByte(); //advance 2 bytes  
269                     lowbyte = binr.ReadByte();
270                 }
271                 else
272                     return null;
273                 byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order  
274                 int modsize = BitConverter.ToInt32(modint, 0);
275 
276                 byte firstbyte = binr.ReadByte();
277                 binr.BaseStream.Seek(-1, SeekOrigin.Current);
278 
279                 if (firstbyte == 0x00)
280                 {   //if first byte (highest order) of modulus is zero, don't include it  
281                     binr.ReadByte();    //skip this null byte  
282                     modsize -= 1;   //reduce modulus buffer size by 1  
283                 }
284 
285                 byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes  
286 
287                 if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data  
288                     return null;
289                 int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)  
290                 byte[] exponent = binr.ReadBytes(expbytes);
291 
292                 // ------- create RSACryptoServiceProvider instance and initialize with public key -----  
293                 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
294                 RSAParameters RSAKeyInfo = new RSAParameters();
295                 RSAKeyInfo.Modulus = modulus;
296                 RSAKeyInfo.Exponent = exponent;
297                 RSA.ImportParameters(RSAKeyInfo);
298                 return RSA;
299             }
300             catch (Exception)
301             {
302                 return null;
303             }
304 
305             finally { binr.Close(); }
306 
307         }
308 
309         private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
310         {
311             byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
312 
313             // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------  
314             MemoryStream mem = new MemoryStream(privkey);
315             BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading  
316             byte bt = 0;
317             ushort twobytes = 0;
318             int elems = 0;
319             try
320             {
321                 twobytes = binr.ReadUInt16();
322                 if (twobytes == 0x8130)    //data read as little endian order (actual data order for Sequence is 30 81)  
323                     binr.ReadByte();    //advance 1 byte  
324                 else if (twobytes == 0x8230)
325                     binr.ReadInt16();    //advance 2 bytes  
326                 else
327                     return null;
328 
329                 twobytes = binr.ReadUInt16();
330                 if (twobytes != 0x0102)    //version number  
331                     return null;
332                 bt = binr.ReadByte();
333                 if (bt != 0x00)
334                     return null;
335 
336 
337                 //------  all private key components are Integer sequences ----  
338                 elems = GetIntegerSize(binr);
339                 MODULUS = binr.ReadBytes(elems);
340 
341                 elems = GetIntegerSize(binr);
342                 E = binr.ReadBytes(elems);
343 
344                 elems = GetIntegerSize(binr);
345                 D = binr.ReadBytes(elems);
346 
347                 elems = GetIntegerSize(binr);
348                 P = binr.ReadBytes(elems);
349 
350                 elems = GetIntegerSize(binr);
351                 Q = binr.ReadBytes(elems);
352 
353                 elems = GetIntegerSize(binr);
354                 DP = binr.ReadBytes(elems);
355 
356                 elems = GetIntegerSize(binr);
357                 DQ = binr.ReadBytes(elems);
358 
359                 elems = GetIntegerSize(binr);
360                 IQ = binr.ReadBytes(elems);
361 
362                 // ------- create RSACryptoServiceProvider instance and initialize with public key -----  
363                 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
364                 RSAParameters RSAparams = new RSAParameters();
365                 RSAparams.Modulus = MODULUS;
366                 RSAparams.Exponent = E;
367                 RSAparams.D = D;
368                 RSAparams.P = P;
369                 RSAparams.Q = Q;
370                 RSAparams.DP = DP;
371                 RSAparams.DQ = DQ;
372                 RSAparams.InverseQ = IQ;
373                 RSA.ImportParameters(RSAparams);
374                 return RSA;
375             }
376             catch (Exception)
377             {
378                 return null;
379             }
380             finally { binr.Close(); }
381         }
382 
383         private static int GetIntegerSize(BinaryReader binr)
384         {
385             byte bt = 0;
386             byte lowbyte = 0x00;
387             byte highbyte = 0x00;
388             int count = 0;
389             bt = binr.ReadByte();
390             if (bt != 0x02)        //expect integer  
391                 return 0;
392             bt = binr.ReadByte();
393 
394             if (bt == 0x81)
395                 count = binr.ReadByte();    // data size in next byte  
396             else
397                 if (bt == 0x82)
398             {
399                 highbyte = binr.ReadByte();    // data size in next 2 bytes  
400                 lowbyte = binr.ReadByte();
401                 byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
402                 count = BitConverter.ToInt32(modint, 0);
403             }
404             else
405             {
406                 count = bt;        // we already have the data size  
407             }
408 
409 
410 
411             while (binr.ReadByte() == 0x00)
412             {    //remove high order zeros in data  
413                 count -= 1;
414             }
415             binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte  
416             return count;
417         }
418 
419         #endregion
420 
421         #region 解析.net 生成的Pem  
422         private static RSAParameters ConvertFromPublicKey(string pemFileConent)
423         {
424 
425             byte[] keyData = Convert.FromBase64String(pemFileConent);
426             if (keyData.Length < 162)
427             {
428                 throw new ArgumentException("pem file content is incorrect.");
429             }
430             byte[] pemModulus = new byte[128];
431             byte[] pemPublicExponent = new byte[3];
432             Array.Copy(keyData, 29, pemModulus, 0, 128);
433             Array.Copy(keyData, 159, pemPublicExponent, 0, 3);
434             RSAParameters para = new RSAParameters();
435             para.Modulus = pemModulus;
436             para.Exponent = pemPublicExponent;
437             return para;
438         }
439 
440         private static RSAParameters ConvertFromPrivateKey(string pemFileConent)
441         {
442             byte[] keyData = Convert.FromBase64String(pemFileConent);
443             if (keyData.Length < 609)
444             {
445                 throw new ArgumentException("pem file content is incorrect.");
446             }
447 
448             int index = 11;
449             byte[] pemModulus = new byte[128];
450             Array.Copy(keyData, index, pemModulus, 0, 128);
451 
452             index += 128;
453             index += 2;//141  
454             byte[] pemPublicExponent = new byte[3];
455             Array.Copy(keyData, index, pemPublicExponent, 0, 3);
456 
457             index += 3;
458             index += 4;//148  
459             byte[] pemPrivateExponent = new byte[128];
460             Array.Copy(keyData, index, pemPrivateExponent, 0, 128);
461 
462             index += 128;
463             index += ((int)keyData[index + 1] == 64 ? 2 : 3);//279  
464             byte[] pemPrime1 = new byte[64];
465             Array.Copy(keyData, index, pemPrime1, 0, 64);
466 
467             index += 64;
468             index += ((int)keyData[index + 1] == 64 ? 2 : 3);//346  
469             byte[] pemPrime2 = new byte[64];
470             Array.Copy(keyData, index, pemPrime2, 0, 64);
471 
472             index += 64;
473             index += ((int)keyData[index + 1] == 64 ? 2 : 3);//412/413  
474             byte[] pemExponent1 = new byte[64];
475             Array.Copy(keyData, index, pemExponent1, 0, 64);
476 
477             index += 64;
478             index += ((int)keyData[index + 1] == 64 ? 2 : 3);//479/480  
479             byte[] pemExponent2 = new byte[64];
480             Array.Copy(keyData, index, pemExponent2, 0, 64);
481 
482             index += 64;
483             index += ((int)keyData[index + 1] == 64 ? 2 : 3);//545/546  
484             byte[] pemCoefficient = new byte[64];
485             Array.Copy(keyData, index, pemCoefficient, 0, 64);
486 
487             RSAParameters para = new RSAParameters();
488             para.Modulus = pemModulus;
489             para.Exponent = pemPublicExponent;
490             para.D = pemPrivateExponent;
491             para.P = pemPrime1;
492             para.Q = pemPrime2;
493             para.DP = pemExponent1;
494             para.DQ = pemExponent2;
495             para.InverseQ = pemCoefficient;
496             return para;
497         }
498         #endregion
499 
500     }
View Code

调用代码

using System;  
using System.Collections.Generic;  
using System.Text;  
using RSA.Class;  
  
namespace payapi_demo.RSA  
{  
    class TestRSA  
    {  
        static void Main(string[] arg)  
        {  
  
            /**RSA加密测试,RSA中的密钥对通过SSL工具生成,生成命令如下: 
             * 1 生成RSA私钥: 
             * openssl genrsa -out rsa_private_key.pem 1024 
             *2 生成RSA公钥 
             * openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem 
             * 
             * 3 将RSA私钥转换成PKCS8格式 
             * openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_pub_pk8.pem 
             * 
             * 直接打开rsa_private_key.pem和rsa_pub_pk8.pem文件就可以获取密钥对内容,获取密钥对内容组成字符串时,注意将换行符删除 
             * */  
  
            string publickey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDzOqfNunFxFtCZPlq7fO/jWwjqmTvAooVBB4y87BizSZ9dl/F7FpAxYc6MmX2TqivCvvORXgdlYdFWAhzXOnIUv9OGG///WPLe9TMs9kIwAZ/APUXauvC01oFLnYkzwPlAh0tQ1Au9arTE/OG1V1dKgf8BXHLPhKL4BmGBEUZBtQIDAQAB";  
            string privatekey = "MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAPM6p826cXEW0Jk+Wrt87+NbCOqZO8CihUEHjLzsGLNJn12X8XsWkDFhzoyZfZOqK8K+85FeB2Vh0VYCHNc6chS/04Yb//9Y8t71Myz2QjABn8A9Rdq68LTWgUudiTPA+UCHS1DUC71qtMT84bVXV0qB/wFccs+EovgGYYERRkG1AgMBAAECgYEA2PmnPdgnYKnolfvQ9tXiLaBFGPpvGk4grz0r6FB5TF7N4rErwxECunq0xioaowK4HPc40qHd2SvkkWQ7FCjYIDsnMk1oOhxNKn0J3FG0n5Cg1/dFai4eoXHs/nKn3SVZ8YZC1T2cMtN2srectLqNqhB8aQEe8xmykyUlUpg/qmECQQD9vkwjUotG5oUUrOj6etcB4WcdyyH0FtThKgyoJUDwgBv6lGGzWyFJEREvp47IgV+FgC7zeP2mL4MhgnD3tNCZAkEA9WRrjOLBNc379XZpoDsH7rZjobVvhnTrEuRDx/whqZ+vk64EPrEW81XYh647bAbJlFn2jPhY+IUHkrxFEFT/fQJBAMoLNOULXQtfkqgb5odMONeue0Ul8itB4tBHgzyALW1TFPQ6InGGJsLfbCfd67uMCFts7fXAaXhibK/KBdm3iEECQQChwVAjzlUN4nnzk9qMhFz2PcPvFGovd2J9UXpcmRaXeWuDLXIe4Rz/ydaxmWgSDWdTIvoicpIzP31+fBwKZ/0BAkEAy0bh4weKmYF29//rK0sxmY8RtqkQeFrwWbqx1daa1w0DfWlNSvy47zyW1G5/AdZU6JSpXxlxdlM/HSDw+v7kcA==";  
  
            //加密字符串  
            string data = "yibao";  
  
            Console.WriteLine("加密前字符串内容:"+data);  
            //加密  
            string encrypteddata = RSAFromPkcs8.encryptData(data, publickey, "UTF-8");  
            Console.WriteLine("加密后的字符串为:" + encrypteddata);  
            Console.WriteLine("解密后的字符串内容:" + RSAFromPkcs8.decryptData(encrypteddata, privatekey, "UTF-8"));  
  
            Console.WriteLine("***********");  
  
            //解密  
            string endata = "LpnnvnfA72VnyjboX/OsCPO6FOFXeEnnsKkI7aAEQyVAPfCTfQ43ZYVZVqnADDPMW7VhBXJWyQMAGw2Fh9sS/XLHmO5XW94Yehci6JrJMynePgtIiDysjNA+UlgSTC/MlResNrBm/4MMSPvq0qLwScgpZDynhLsVZk+EQ6G8wgA=";  
            string datamw = RSAFromPkcs8.decryptData(endata, privatekey, "UTF-8");  
            Console.WriteLine("静态加密后的字符串为:" + endata);  
            Console.WriteLine("解密后的字符串内容:" + datamw);  
  
            //签名  
            string signdata = "YB010000001441234567286038508081299";  
            Console.WriteLine("签名前的字符串内容:" + signdata);  
            string sign = RSAFromPkcs8.sign(signdata, privatekey, "UTF-8");  
            Console.WriteLine("签名后的字符串:" + sign);  
  
            Console.ReadLine();  
        }  
    }  
}  
View Code

 

 

 

转载自 http://blog.csdn.net/hjqqingqing/article/details/9272103

posted @ 2017-07-19 16:23  ccqin  阅读(450)  评论(0编辑  收藏  举报