zoukankan      html  css  js  c++  java
  • RSA密钥,JAVA与.NET之间转换

    前言

       最近由于项目需求,服务端由c#编写,客户端由java编写。通信数据使用RSA非对称加密。但是java和c#生成的密钥格式是不一样的,所以需要转换格式才可以正常使用。网上搜到使用java进行格式转换的代码(如:http://blog.csdn.net/road2010/article/details/40071881 ),本文将给出一种c#的实现方法。

    密钥格式

    java密钥格式如下:

    私钥:

    MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKSZSSEdPhv77O5ocnLNGXeQ21qJDArC1+yId+9/pY5bXkZk5vCB49EpfMwNukLv6AJqofThZPNOs1t015fdEYIUnkIc2QVxRwa0xTeFP6N8D4WQXRWs4fNG27JK5kP45s+9KlJtx5hs7G97aMczehIWpHaO6j9inOmjlU8l62KZAgMBAAECgYEAmK3TRtMwRJb33OGnn9OeFumYfy92qxi3X6Hq1o6qDBW2qkd4bImfv+ni6AinyOVuaadt2Y+lq4dKGcCVJzoZvPm1VKxD2y7xKa8/vEbPRiRTt0qnPq9T7UJkpDsiXf/zOMfWdjc3uA1bPnQ65RWHSJ7zAE+Gd7xnyCE5MEyijLECQQDVYqQWDqOVLZ5lJUuIfUIrhv26GvuoTX8v60+opCz4/Mdfh6JlefICVD6SAaYvufXBHVFY26JicNlR62ZOiBoNAkEAxXhsuEnNJNQcQHEVTPZoulbMbTV1VZIDQ1zjG8fvu8sv6IBYcR5+EsC8n3/6RkW8/iCJDzxE++VHzhoSQSoDvQJBAM6/63J/rpndAIrJ7vyJOPLJsc9/U3SH2gMJAT7KC9UXvuldlsixtf3xuEpplKbLjEUXbfklnZm586a+6XqPvoUCQDFotltOLARBBmihYuEE7qNhQHk63QbyJ9rdDP5Qgo2Mg4o7QuXa6VSr4QZPsUGQBX/YiDLFs8ULU3IgV9zyNEkCQADAYR8DctYzg0eBGdcOrDA5Szc62pYrS2wk89wUxyL4FyDL3omkVMKvtu5tccy7Xht2ikJZRqefZ3dS7ASm8/4=

    公钥:

    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkmUkhHT4b++zuaHJyzRl3kNtaiQwKwtfsiHfvf6WOW15GZObwgePRKXzMDbpC7+gCaqH04WTzTrNbdNeX3RGCFJ5CHNkFcUcGtMU3hT+jfA+FkF0VrOHzRtuySuZD+ObPvSpSbceYbOxve2jHM3oSFqR2juo/Ypzpo5VPJetimQIDAQAB

    .NET密钥格式如下:

    私钥:

    <RSAKeyValue>
      <Modulus>pJlJIR0+G/vs7mhycs0Zd5DbWokMCsLX7Ih373+ljlteRmTm8IHj0Sl8zA26Qu/oAmqh9OFk806zW3TXl90RghSeQhzZBXFHBrTFN4U/o3wPhZBdFazh80bbskrmQ/jmz70qUm3HmGzsb3toxzN6Ehakdo7qP2Kc6aOVTyXrYpk=</Modulus>
      <Exponent>AQAB</Exponent>
      <P>1WKkFg6jlS2eZSVLiH1CK4b9uhr7qE1/L+tPqKQs+PzHX4eiZXnyAlQ+kgGmL7n1wR1RWNuiYnDZUetmTogaDQ==</P>
      <Q>xXhsuEnNJNQcQHEVTPZoulbMbTV1VZIDQ1zjG8fvu8sv6IBYcR5+EsC8n3/6RkW8/iCJDzxE++VHzhoSQSoDvQ==</Q>
      <DP>zr/rcn+umd0Aisnu/Ik48smxz39TdIfaAwkBPsoL1Re+6V2WyLG1/fG4SmmUpsuMRRdt+SWdmbnzpr7peo++hQ==</DP>
      <DQ>MWi2W04sBEEGaKFi4QTuo2FAeTrdBvIn2t0M/lCCjYyDijtC5drpVKvhBk+xQZAFf9iIMsWzxQtTciBX3PI0SQ==</DQ>
      <InverseQ>wGEfA3LWM4NHgRnXDqwwOUs3OtqWK0tsJPPcFMci+Bcgy96JpFTCr7bubXHMu14bdopCWUann2d3UuwEpvP+</InverseQ>
      <D>mK3TRtMwRJb33OGnn9OeFumYfy92qxi3X6Hq1o6qDBW2qkd4bImfv+ni6AinyOVuaadt2Y+lq4dKGcCVJzoZvPm1VKxD2y7xKa8/vEbPRiRTt0qnPq9T7UJkpDsiXf/zOMfWdjc3uA1bPnQ65RWHSJ7zAE+Gd7xnyCE5MEyijLE=</D>
    </RSAKeyValue>

    公钥:

    <RSAKeyValue>
      <Modulus>pJlJIR0+G/vs7mhycs0Zd5DbWokMCsLX7Ih373+ljlteRmTm8IHj0Sl8zA26Qu/oAmqh9OFk806zW3TXl90RghSeQhzZBXFHBrTFN4U/o3wPhZBdFazh80bbskrmQ/jmz70qUm3HmGzsb3toxzN6Ehakdo7qP2Kc6aOVTyXrYpk=</Modulus>
      <Exponent>AQAB</Exponent>
    </RSAKeyValue>

      

    转换实现代码(c#)

      格式转换要用到一个开源加密库Bouncy Castle Crypto APIs,官网地址: http://www.bouncycastle.org/csharp/

      具体实现代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    using System;
    using System.Xml;
    using Org.BouncyCastle.Asn1.Pkcs;
    using Org.BouncyCastle.Asn1.X509;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Pkcs;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.X509;
     
    /// <summary>
    /// RSA密钥格式转换
    /// </summary>
    public class RSAKeyConvert
    {
        /// <summary>
        /// RSA私钥格式转换,java->.net
        /// </summary>
        /// <param name="privateKey">java生成的RSA私钥</param>
        /// <returns></returns>
        public static string RSAPrivateKeyJava2DotNet(string privateKey)
        {
            RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));
     
            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
        }
     
        /// <summary>
        /// RSA私钥格式转换,.net->java
        /// </summary>
        /// <param name="privateKey">.net生成的私钥</param>
        /// <returns></returns>
        public static string RSAPrivateKeyDotNet2Java(string privateKey)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(privateKey);
            BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
            BigInteger exp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
            BigInteger d = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText));
            BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText));
            BigInteger q = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText));
            BigInteger dp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText));
            BigInteger dq = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText));
            BigInteger qinv = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText));
     
            RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv);
     
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);
            byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();
            return Convert.ToBase64String(serializedPrivateBytes);
        }
     
        /// <summary>
        /// RSA公钥格式转换,java->.net
        /// </summary>
        /// <param name="publicKey">java生成的公钥</param>
        /// <returns></returns>
        public static string RSAPublicKeyJava2DotNet(string publicKey)
        {
            RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
                Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
                Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
        }
     
        /// <summary>
        /// RSA公钥格式转换,.net->java
        /// </summary>
        /// <param name="publicKey">.net生成的公钥</param>
        /// <returns></returns>
        public static string RSAPublicKeyDotNet2Java(string publicKey)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(publicKey);
            BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
            BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
            RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
     
            SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
            byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
            return Convert.ToBase64String(serializedPublicBytes);
        }
    }

      

    最近在做银联的一个接口,用到RSA签名,悲剧来了,.net用的RSA密钥格式和JAVA用的不一样

    .net为XML格式

    1
    <RSAKeyValue><Modulus>53KnujHcV0962zoLigW8d4AUb+1TS3LiySGrXhF5FgjUQhLzI6PCM/hyHPhUat6MTcgWK3kAVInughtNOHXrBI92I1nAdwlMwBPh+F+0UGhQDR5LMaBg7tQq7ebyhy8/QRCtxEO+F0QQYYv0t15RIup+F+08HdWSnTroTBwcEpU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>

    JAVA需要PEM文件或DER格式

    PEM文件

    1
    2
    3
    4
    5
    6
    -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz4PgoQ/2o5cMmFJgcFHLwEl1V
    2olZxAEMb7mWfwH36JvORq/maQEE4kYbF2gQN7lQ0C+km0WK6s6ZdzHlhIm/CoK9
    YdFCssoTyzj9BDc1RpCtiF1siz/f9vOmGzYKp3bHYHCoEX21XaOuDuVURLuVCWws
    HPBpk841ayGwoz4PWQIDAQAB
    -----END PUBLIC KEY-----

    x50916进制展开

    1
    30819f300d06092a864886f70d010101050003818d0030818902818100c166a9a72c74666ed033492d99fa85dffab5230511a3099cd2103a3c89024bcaa8e53b3811fe1588d4827f0621f806c7598fcb4de4624dac420cbbcb84e265589d9fb636a727c7046bcc83ca3bd15980c0ea64246c286b62f55be382b75901f1ee20875018612c69e30e316179460f00cb6f1d965223738c4e58b0da9da4bc4d0203010001

      

    DER16进制展开

    30818902818100c166a9a72c74666ed033492d99fa85dffab5230511a3099cd2103a3c89024bcaa8e53b3811fe1588d4827f0621f806c7598fcb4de4624dac420cbbcb84e265589d9fb636a727c7046bcc83ca3bd15980c0ea64246c286b62f55be382b75901f1ee20875018612c69e30e316179460f00cb6f1d965223738c4e58b0da9da4bc4d0203010001

    问题来了,它们之间是它喵的啥关系

    x509与DER

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
         * x509格式公钥转换为Der格式
         *
         * @param x509PublicKey x509格式公钥字符串
         * @return Der格式公钥字符串
         */
        public static String getRsaPublicKeyDerFromX509(String x509PublicKey) {
            try {
                ASN1InputStream aIn = new ASN1InputStream(hexString2ByteArr(x509PublicKey));
                SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(aIn.readObject());
                RSAPublicKeyStructure struct = RSAPublicKeyStructure.getInstance(info.getPublicKey());
                if (aIn != null)
                    aIn.close();
                return byteArr2HexString(struct.getDERObject().getEncoded());
            catch (IOException e) {
                return null;
            }
        }

    x509与PEM

    对于公钥,x509=Convert.FromBase64String(PEM中间内容)

    私钥就不知道怎么算的

    因此JAVA使用格式都是可以用x509进行转换的,剩下的就是如何将.NET的和JAVA的相互转换

    格式转换要用到一个开源加密库Bouncy Castle Crypto APIs,官网地址: http://www.bouncycastle.org/csharp/

    x509与.NET相互转换

    复制代码
    /// <summary>
            /// RSA私钥格式转换,java->.net
            /// </summary>
            /// <param name="privateKeyInfoData">java生成的RSA私钥</param>
            /// <returns></returns>
            public static string RSAPrivateKeyJava2DotNet(byte[] privateKeyInfoData)
            {
                RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(privateKeyInfoData);
    
                return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                    Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
                    Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
            }
    
            /// <summary>
            /// RSA私钥格式转换,.net->java
            /// </summary>
            /// <param name="privateKey">.net生成的私钥</param>
            /// <returns></returns>
            public static byte[] RSAPrivateKeyDotNet2Java(string privateKey)
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(privateKey);
                BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
                BigInteger exp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
                BigInteger d = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText));
                BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText));
                BigInteger q = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText));
                BigInteger dp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText));
                BigInteger dq = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText));
                BigInteger qinv = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText));
    
                RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv);
    
                PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);
                byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();
                return serializedPrivateBytes;
                //return Convert.ToBase64String(serializedPrivateBytes);
            }
    
            /// <summary>
            /// RSA公钥格式转换,java->.net
            /// </summary>
            /// <param name="keyInfoData">java生成的公钥</param>
            /// <returns></returns>
            public static string RSAPublicKeyJava2DotNet(byte[] keyInfoData)
            {
                RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(keyInfoData);
                return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
                    Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
                    Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
            }
    
            /// <summary>
            /// RSA公钥格式转换,.net->java
            /// </summary>
            /// <param name="publicKey">.net生成的公钥</param>
            /// <returns></returns>
            public static byte[] RSAPublicKeyDotNet2Java(string publicKey)
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(publicKey);
                BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
                BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
                RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
    
                SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
                byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
                return serializedPublicBytes;
                //return Convert.ToBase64String(serializedPublicBytes);
            }
    复制代码

    PEM与.NET相互转换

    复制代码
     public static void Xml2PemPrivate(string xml,string saveFile)
            {
                var rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(xml);
                var p = rsa.ExportParameters(true);
                var key = new RsaPrivateCrtKeyParameters(
                    new BigInteger(1, p.Modulus), new BigInteger(1, p.Exponent), new BigInteger(1, p.D),
                    new BigInteger(1, p.P), new BigInteger(1, p.Q), new BigInteger(1, p.DP), new BigInteger(1, p.DQ),
                    new BigInteger(1, p.InverseQ));
                using (var sw = new StreamWriter(saveFile))
                {
                    var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
                    pemWriter.WriteObject(key);
                }
            }
            public static string Pem2XmlPrivate(string pemFile)
            {
                AsymmetricCipherKeyPair keyPair;
                using (var sr = new StreamReader(pemFile))
                {
                    var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr);
                    keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
                }
                var key = (RsaPrivateCrtKeyParameters)keyPair.Private;
                var p = new RSAParameters
                {
                    Modulus = key.Modulus.ToByteArrayUnsigned(),
                    Exponent = key.PublicExponent.ToByteArrayUnsigned(),
                    D = key.Exponent.ToByteArrayUnsigned(),
                    P = key.P.ToByteArrayUnsigned(),
                    Q = key.Q.ToByteArrayUnsigned(),
                    DP = key.DP.ToByteArrayUnsigned(),
                    DQ = key.DQ.ToByteArrayUnsigned(),
                    InverseQ = key.QInv.ToByteArrayUnsigned(),
                };
                var rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(p);
                return rsa.ToXmlString(true);
            }
    
    
            public static string Xml2PemPublic(string xml, string saveFile)
            {
                var rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(xml);
                var p = rsa.ExportParameters(false);
                RsaKeyParameters key = new RsaKeyParameters(false, new BigInteger(1, p.Modulus), new BigInteger(1, p.Exponent)); 
                using (var sw = new StreamWriter(saveFile))
                {
                    var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
                    pemWriter.WriteObject(key);
                }
                return System.IO.File.ReadAllText(saveFile);
            }
    
            public static string Pem2XmlPublic(string pemFileConent)
            {
                pemFileConent = pemFileConent.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("
    ", "").Replace("
    ", "");
                var data = Convert.FromBase64String(pemFileConent);
                return RSAPublicKeyJava2DotNet(data);
            }
  • 相关阅读:
    LeetCode——Generate Parentheses
    LeetCode——Best Time to Buy and Sell Stock IV
    LeetCode——Best Time to Buy and Sell Stock III
    LeetCode——Best Time to Buy and Sell Stock
    LeetCode——Find Minimum in Rotated Sorted Array
    Mahout实现基于用户的协同过滤算法
    使用Java对文件进行解压缩
    LeetCode——Convert Sorted Array to Binary Search Tree
    LeetCode——Missing Number
    LeetCode——Integer to Roman
  • 原文地址:https://www.cnblogs.com/Alex80/p/11526634.html
Copyright © 2011-2022 走看看