.NET如何实现数据加密

.NET如何实现数据加密,第1张

http://www.microsoft.com/china/technet/security/guidance/secmod39.mspx(最好的理论资料)

建立一个win form程序,然后把代码拷到后台,即可实现C#非对称加密程序 。

using System

using System.Drawing

using System.Collections

using System.ComponentModel

using System.Windows.Forms

using System.Data

using System.IO

using System.Text

using System.Security.Cryptography

namespace 非对称加密

{

/// <summary>

/// Form1 的摘要说明。

/// </summary>

public class Form1 : System.Windows.Forms.Form

{

private System.Windows.Forms.TabControl tabControl1

private System.Windows.Forms.TabPage tabPage1

private System.Windows.Forms.TabPage tabPage2

private System.Windows.Forms.Button button1

private System.Windows.Forms.Button button2

private System.Windows.Forms.Button button3

private System.Windows.Forms.SaveFileDialog save

private System.Windows.Forms.Label label1

private System.Windows.Forms.TextBox textBox1

private System.Windows.Forms.Button button4

private System.Windows.Forms.Button button5

private System.Windows.Forms.Label label2

private System.Windows.Forms.RichTextBox richtext2

private System.Windows.Forms.OpenFileDialog open

private System.Windows.Forms.Label label3

private System.Windows.Forms.RichTextBox richtext3

private System.Windows.Forms.RichTextBox richtext

private System.Windows.Forms.Button button6

private System.Windows.Forms.Button button7

private System.Windows.Forms.Button button8

/// <summary>

/// 必需的设计器变量。

private static RSACryptoServiceProvider crypt

private static string privatekey

private static string publickey

private static byte [] bytes

private static string publicinfo

private static string privateinfo

private static string readpublickey

private static string readprivatekey

// ///private static byte [] onebytes

/// </summary>

private System.ComponentModel.Container components = null

public Form1()

{

//

// Windows 窗体设计器支持所必需的

//

InitializeComponent()

//

// TODO: 在 InitializeComponent 调用后添加任何构造函数代码

//

}

/// <summary>

/// 清理所有正在使用的资源。

/// </summary>

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose()

}

}

base.Dispose( disposing )

}

#region Windows 窗体设计器生成的代码

/// <summary>

/// 设计器支持所需的方法 - 不要使用代码编辑器修改

/// 此方法的内容。

/// </summary>

private void InitializeComponent()

{

this.tabControl1 = new System.Windows.Forms.TabControl()

this.tabPage1 = new System.Windows.Forms.TabPage()

this.button6 = new System.Windows.Forms.Button()

this.button3 = new System.Windows.Forms.Button()

this.button2 = new System.Windows.Forms.Button()

this.richtext = new System.Windows.Forms.RichTextBox()

this.button1 = new System.Windows.Forms.Button()

this.tabPage2 = new System.Windows.Forms.TabPage()

this.button8 = new System.Windows.Forms.Button()

this.button7 = new System.Windows.Forms.Button()

this.label3 = new System.Windows.Forms.Label()

this.richtext3 = new System.Windows.Forms.RichTextBox()

this.label2 = new System.Windows.Forms.Label()

this.richtext2 = new System.Windows.Forms.RichTextBox()

this.button5 = new System.Windows.Forms.Button()

this.button4 = new System.Windows.Forms.Button()

this.textBox1 = new System.Windows.Forms.TextBox()

this.label1 = new System.Windows.Forms.Label()

this.save = new System.Windows.Forms.SaveFileDialog()

this.open = new System.Windows.Forms.OpenFileDialog()

this.tabControl1.SuspendLayout()

this.tabPage1.SuspendLayout()

this.tabPage2.SuspendLayout()

this.SuspendLayout()

//

// tabControl1

//

this.tabControl1.Controls.Add(this.tabPage1)

this.tabControl1.Controls.Add(this.tabPage2)

this.tabControl1.Location = new System.Drawing.Point(32, 64)

this.tabControl1.Name = "tabControl1"

this.tabControl1.SelectedIndex = 0

this.tabControl1.Size = new System.Drawing.Size(416, 280)

this.tabControl1.TabIndex = 0

//

// tabPage1

//

this.tabPage1.Controls.Add(this.button6)

this.tabPage1.Controls.Add(this.button3)

this.tabPage1.Controls.Add(this.button2)

this.tabPage1.Controls.Add(this.richtext)

this.tabPage1.Controls.Add(this.button1)

this.tabPage1.Location = new System.Drawing.Point(4, 21)

this.tabPage1.Name = "tabPage1"

this.tabPage1.Size = new System.Drawing.Size(408, 255)

this.tabPage1.TabIndex = 0

this.tabPage1.Text = "得到钥匙"

//

// button6

//

this.button6.Location = new System.Drawing.Point(72, 168)

this.button6.Name = "button6"

this.button6.TabIndex = 4

this.button6.Text = "写入文件"

this.button6.Click += new System.EventHandler(this.button6_Click)

//

// button3

//

this.button3.Location = new System.Drawing.Point(200, 16)

this.button3.Name = "button3"

this.button3.TabIndex = 3

this.button3.Text = "保存私匙"

this.button3.Click += new System.EventHandler(this.button3_Click)

//

// button2

//

this.button2.Location = new System.Drawing.Point(128, 16)

this.button2.Name = "button2"

this.button2.TabIndex = 2

this.button2.Text = "保存公钥"

this.button2.Click += new System.EventHandler(this.button2_Click)

//

// richtext

//

this.richtext.Location = new System.Drawing.Point(16, 56)

this.richtext.Name = "richtext"

this.richtext.Size = new System.Drawing.Size(256, 96)

this.richtext.TabIndex = 1

this.richtext.Text = "richTextBox1"

//

// button1

//

this.button1.Location = new System.Drawing.Point(8, 16)

this.button1.Name = "button1"

this.button1.Size = new System.Drawing.Size(88, 23)

this.button1.TabIndex = 0

this.button1.Text = "得到钥匙信息"

this.button1.Click += new System.EventHandler(this.button1_Click)

//

// tabPage2

//

this.tabPage2.Controls.Add(this.button8)

this.tabPage2.Controls.Add(this.button7)

this.tabPage2.Controls.Add(this.label3)

this.tabPage2.Controls.Add(this.richtext3)

this.tabPage2.Controls.Add(this.label2)

this.tabPage2.Controls.Add(this.richtext2)

this.tabPage2.Controls.Add(this.button5)

this.tabPage2.Controls.Add(this.button4)

this.tabPage2.Controls.Add(this.textBox1)

this.tabPage2.Controls.Add(this.label1)

this.tabPage2.Location = new System.Drawing.Point(4, 21)

this.tabPage2.Name = "tabPage2"

this.tabPage2.Size = new System.Drawing.Size(408, 255)

this.tabPage2.TabIndex = 1

this.tabPage2.Text = "加密解密文字"

//

// button8

//

this.button8.Location = new System.Drawing.Point(216, 72)

this.button8.Name = "button8"

this.button8.TabIndex = 9

this.button8.Text = "读取私匙"

this.button8.Click += new System.EventHandler(this.button8_Click)

//

// button7

//

this.button7.Location = new System.Drawing.Point(56, 72)

this.button7.Name = "button7"

this.button7.TabIndex = 8

this.button7.Text = "读取公匙"

this.button7.Click += new System.EventHandler(this.button7_Click)

//

// label3

//

this.label3.Location = new System.Drawing.Point(200, 120)

this.label3.Name = "label3"

this.label3.Size = new System.Drawing.Size(100, 16)

this.label3.TabIndex = 7

this.label3.Text = "解密结果"

//

// richtext3

//

this.richtext3.Location = new System.Drawing.Point(200, 144)

this.richtext3.Name = "richtext3"

this.richtext3.Size = new System.Drawing.Size(120, 72)

this.richtext3.TabIndex = 6

this.richtext3.Text = "richTextBox2"

//

// label2

//

this.label2.Location = new System.Drawing.Point(48, 120)

this.label2.Name = "label2"

this.label2.Size = new System.Drawing.Size(128, 16)

this.label2.TabIndex = 5

this.label2.Text = "加密窗口:"

//

// richtext2

//

this.richtext2.Location = new System.Drawing.Point(40, 144)

this.richtext2.Name = "richtext2"

this.richtext2.Size = new System.Drawing.Size(120, 72)

this.richtext2.TabIndex = 4

this.richtext2.Text = "richTextBox2"

//

// button5

//

this.button5.Location = new System.Drawing.Point(200, 96)

this.button5.Name = "button5"

this.button5.Size = new System.Drawing.Size(112, 23)

this.button5.TabIndex = 3

this.button5.Text = "使用私匙解密"

this.button5.Click += new System.EventHandler(this.button5_Click)

//

// button4

//

this.button4.Location = new System.Drawing.Point(48, 96)

this.button4.Name = "button4"

this.button4.Size = new System.Drawing.Size(96, 23)

this.button4.TabIndex = 2

this.button4.Text = "使用公匙加密"

this.button4.Click += new System.EventHandler(this.button4_Click)

//

// textBox1

//

this.textBox1.Location = new System.Drawing.Point(104, 32)

this.textBox1.Multiline = true

this.textBox1.Name = "textBox1"

this.textBox1.Size = new System.Drawing.Size(168, 32)

this.textBox1.TabIndex = 1

this.textBox1.Text = "textBox1"

//

// label1

//

this.label1.Location = new System.Drawing.Point(128, 8)

this.label1.Name = "label1"

this.label1.Size = new System.Drawing.Size(120, 16)

this.label1.TabIndex = 0

this.label1.Text = "请输入加密的文字"

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(6, 14)

this.ClientSize = new System.Drawing.Size(504, 357)

this.Controls.Add(this.tabControl1)

this.Name = "Form1"

this.Text = "Form1"

this.tabControl1.ResumeLayout(false)

this.tabPage1.ResumeLayout(false)

this.tabPage2.ResumeLayout(false)

this.ResumeLayout(false)

}

#endregion

/// <summary>

/// 应用程序的主入口点。

/// </summary>

[STAThread]

static void Main()

{

Application.Run(new Form1())

}

//得到钥匙信息

private void button1_Click(object sender, System.EventArgs e)

{

crypt=new RSACryptoServiceProvider()

publickey=crypt.ToXmlString(false)

richtext.Text="导出秘匙的情况下:\n"+publickey+"\n"

privatekey=crypt.ToXmlString(true)

string info="仅仅导出公匙的情况下:\n"+privatekey+"\n"

richtext.AppendText(info)

crypt.Clear()

}

//保存公匙信息

private void button2_Click(object sender, System.EventArgs e)

{

save=new SaveFileDialog()

save.Filter="File Text (*.txt)|*.txt|All File (*.*)|*.*"

save.ShowDialog()

publicinfo=save.FileName

}

//保存密匙信息

private void button3_Click(object sender, System.EventArgs e)

{

save=new SaveFileDialog()

save.Filter="File Text (*.txt)|*.txt|All File (*.*)|*.*"

save.ShowDialog()

privateinfo=save.FileName

}

//把钥匙信息写入文件

private void button6_Click(object sender, System.EventArgs e)

{

StreamWriter one=new StreamWriter(publicinfo,true,UTF8Encoding.UTF8)

one.Write(publickey)

StreamWriter two=new StreamWriter(privateinfo,true,UTF8Encoding.UTF8)

two.Write(privatekey)

one.Flush()

two.Flush()

one.Close()

two.Close()

MessageBox.Show("成功保存公匙和密匙!")

}

//用公匙加密

private void button4_Click(object sender, System.EventArgs e)

{

crypt=new RSACryptoServiceProvider()

UTF8Encoding enc=new UTF8Encoding()

bytes=enc.GetBytes(textBox1.Text)

crypt.FromXmlString( readpublickey )

bytes = crypt.Encrypt( bytes,false )

string encryttext=enc.GetString(bytes)//encryptbyte)

richtext2.Text="加密结果:\n"+encryttext+"\n"+"加密结束!"

}

private void button5_Click(object sender, System.EventArgs e)

{

UTF8Encoding enc=new UTF8Encoding()

byte [] decryptbyte

crypt.FromXmlString ( readprivatekey )

decryptbyte = crypt.Decrypt( bytes,false )

string decrypttext=enc.GetString( decryptbyte )

richtext3.Text = "解密结果:\n" + decrypttext + "\n" + "解密结束!"

}

//从文件中读取公匙信息

private void button7_Click(object sender, System.EventArgs e)

{

StreamReader sr

open = new OpenFileDialog( )

open.Filter="Text File (*.txt)|*.txt|All File (*.*)|*.* "

// open.ShowDialog()

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

{

sr = new StreamReader(open.FileName,UTF8Encoding.UTF8)

}

else

{

MessageBox.Show("发生错误!")

return

}

readpublickey = sr.ReadToEnd()

sr.Close()

}

//从文件中读取私匙信息

private void button8_Click(object sender, System.EventArgs e)

{

open = new OpenFileDialog( )

open.Filter="Text File (*.txt)|*.txt|All File (*.*)|*.* "

open.ShowDialog()

StreamReader sr = new StreamReader(open.FileName,UTF8Encoding.UTF8)

readprivatekey = sr.ReadToEnd()

sr.Close()

}

}

}

●前言

我们知道,现在网络上一般的网站,稍微完善一点的,往往都需要用户先注册,提供诸如电子邮件、账号、密码等信息以后,成为网站栏目的注册用户,才可以享受网站一些特殊栏目提供的信息或者服务,比如免费电子邮件、论坛、聊天等,都需要用户注册。而对于电子商务网站,比如igo5等大型电子商务网站,用户需要购买商品,就一定需要详细而准确的注册,而这些信息,往往是用户很隐秘的信息,比如电话、电子邮件、地址等,所以,注册信息对于用户和网站都是很重要的资源,不能随意透露,更加不能存在安全上的隐患。

如果我们也设计一个需要用户注册的网站,根据现在的常用技术实现方法,可以在数据库中建立一个用于存放用户信息的表,这个表中至少包括用户账号字段:UserAccount和用户密码字段:Password,当然,实际应用中一个用户信息表不可能就只有这些信息,往往根据网站服务要求,会适当增加一些其他的信息,以方便网站提供更加完善的服务。一般的,一个用户信息占用这个用户信息表的一行也就是一个数据记录,当用户登录或者提交资料的时候,程序将用户填写的信息与表中的信息对照,如果用户账号和密码都准确无误,那么说明这个用户是合法用户,通过注册;反之,则是非法用户,不许通过。

然而,是不是这样就安全了了?是不是这样就能满足网站的注册要求了呢?仔细想想,我们一般将用户资料直接保存在数据库中,并没有进行任何的保密措施,对于一些文件型数据库比如Access等,如果有人得到这个文件,岂不是所有的资料都泄露无疑?更加重要的是,如果一个不负责任的网管,不需要任何技术手段,就可以查看网站中的任何资料,如果我们的用户信息在数据库中没有加密,对于网管而言,查看这些信息是太简单了。所以,为了增加安全性,我们有必要对数据库中的资料进行加密,这样,即使有人得到了整个数据库,如果没有解密算法,也一样不能查看到数据库中的用户信息。但是,在考虑数据库是否安全之前,我们有必要对我们的数据是否真的那么重要进行考虑,如果数据只是简单的一些文件资料,没有保密的必要,显然,没有必要对这些数据进行加密而浪费系统资源、加重程序负担,如果这些数据具有一定的隐私性,当然就有必要进行加密。所以,在考虑加密以前,我们可以对需要加密的数据做适当的选择,以免浪费系统资源。

●MD5加密算法简单介绍

在现阶段,我们一般认为存在两种加密方式,单向加密和双向加密。双向加密是加密算法中最常用的,它将我们可以直接理解的明文数据加密为我们不可直接理解的密文数据,然后,在需要的时候,可以使用一定的算法将这些加密以后的密文解密为原来可以理解的明文。双向加密适合于隐秘通讯,比如,我们在网上购物的时候,需要向网站提交xyk密码,我们当然不希望我们的数据直接在网上明文传送,因为这样很可能被别的用户“偷听”,我们希望我们的xyk密码是通过加密以后,再在网络传送,这样,网站接受到我们的数据以后,通过解密算法就可以得到准确的xyk账号。

单向加密刚好相反,只能对数据进行加密,也就是说,没有办法对加密以后的数据进行解密。可能我们立即就会想,这样的加密有什么用处?不能解密的加密算法有什么作用呢?在实际中的一个应用就是数据库中的用户信息加密,当用户创建一个新的账号或者密码,他的信息不是直接保存到数据库,而是经过一次加密以后再保存,这样,即使这些信息被泄露,也不能立即理解这些信息的真正含义。

MD5就是采用单向加密的加密算法,对于MD5而言,有两个特性是很重要的,第一是任意两段明文数据,加密以后的密文不能是相同的;第二是任意一段明文数据,经过加密以后,其结果必须永远是不变的。前者的意思是不可能有任意两段明文加密以后得到相同的密文,后者的意思是如果我们加密特定的数据,得到的密文一定是相同的。

MD5CyptoServiceProvider类是.NET中System.Security.Cryptography名字空间的一个类,提供专门用于MD5单向数据加密的解决方法,也是本文中我们用来加密数据库中密码的类。在真正进行数据加密之前,我们首先来了解MD5CyptoServiceProvider类中的主要方法:ComputeHash,它将输入的明文数据数组使用MD5加密以后输出加密后的密文数据数组。现在,我们就来看一个具体的实例:

'要加密的明文字符串

Dim strPlainText as String = "Encrypt me!"

'用于存放明文字符串的数组

Dim hashedDataBytes as Byte()

Dim encoder as New UTF8Encoding()

'建立MD5CryptoService实例

Dim md5Hasher as New MD5CryptoServiceProvider()

'加密运算

hashedDataBytes = md5Hasher.ComputeHash(encoder.GetBytes(strPlainText))

看完以上的具体实例以后,我们知道,ComputeHash方法只能接受数组作为加密对象,输出的密文也是数组,因此,在对字符串加密之前,我们必须首先将这些字符串转化为数组,这就要用到UTF8Encoding类的GetBytes方法,将字符串转化为数组,而加密以后的结果也是使用数组输出。

以上我们大致了解了MD5的具体加密实现方法,下面,我们结合数据库来看看MD5的实际使用。

●使用MD5存储密码

在前面的介绍中,我们提到网站往往将用户的账号、密码等信息使用非加密的方式保存到数据库,比如账号使用类型为VarChar的UserCount字段,同样,密码也是采用类型为VarChar的Password字段。但是,如果我们打算采用MD5加密方式存储密码信息,就必须改变密码字段PassWord的类型为16为二进制方式,这个其实我们也不难理解,因为在前面的介绍中,我们知道加密以后的输出,是使用二进制数组的,所以,这里必须做相应的改变。

当用户注册成功,正式建立一个账号的时候,数据库中就必须为这个用户增加一条记录。以下的程序代码实现了建立一个账号的功能,在页面中,程序要求用户输入账号、密码等信息,然后,将这些信息作为账号信息存入名为UserCount的数据表,在这个表中,用户密码是使用MD5加密保存的。下面就是实现以上页面的具体代码:

<%@ Import Namespace="System.Security.Cryptography" %>

<%@ Import Namespace="System.Text" %>

<%@ Import Namespace="System.Data" %>

<%@ Import Namespace="System.Data.SqlClient" %>

<script runat="server" language="VB">

Sub CreateAccount(sender as Object, e as EventArgs)

'1. 建立数据库连接

Const strConnString as String = "connection string"

Dim objConn as New SqlConnection(strConnString)

'2. 建立Command对象

Dim strSQL as String = _

"INSERT INTO UserAccount(Username,Password) " &_

"VALUES(@Username, @Password)"

Dim objCmd as New SqlCommand(strSQL, objConn)

'3. SQL参数

Dim paramUsername as SqlParameter

paramUsername= New SqlParameter("@Username", SqlDbType.VarChar, 25)

paramUsername.Value = txtUsername.Text

objCmd.Parameters.Add(paramUsername)

'加密用户密码

Dim md5Hasher as New MD5CryptoServiceProvider()

Dim hashedBytes as Byte()

Dim encoder as New UTF8Encoding()

hashedBytes=md5Hasher.ComputeHash(encoder.GetBytes(txtPwd.Text))

Dim paramPwd as SqlParameter

paramPwd = New SqlParameter("@Password", SqlDbType.Binary, 16)

paramPwd.Value = hashedBytes

objCmd.Parameters.Add(paramPwd)

'加入数据库

objConn.Open()

objCmd.ExecuteNonQuery()

objConn.Close()

End Sub

</script>

<form runat="server">

<h1>建立一个账号</h1>

用户名:<asp:TextBox runat="server" id="txtUsername" />

密码:

<asp:TextBox runat="server" id="txtPwd" TextMode="Password" />

<p><asp:Button runat="server" Text="建立用户账号"

OnClick="CreateAccount" /></p>

</form>

在以上程序实现的页面中,“用户名”和“密码”输入框要求用户输入自己的账号和密码,用户输入自己的信息以后,按“建立用户账号”按钮,就可以建立一个账号并且存入数据库。我们同时需要特别注意,因为以上的程序使用到了MD5加密和数据库等功能,所以,在代码最开头,我们引入了几个稍微特别一点的名字空间,这是不可缺少的。

我们可以看到,PassWord字段的信息是二进制方式保存的,即使数据库被人取得,也不可能知道密码具体是什么意思。当然,密码也就不会泄露。

●使用MD5鉴别是否合法用户

既然用户密码是按照MD5加密以后保存在数据库中的,我们知道,MD5是单次加密算法,所以,不可能将加密以后的信息转为明文,也就是说,已经没有办法知道。这就出现一个问题,如果用户使用账号、密码登录,怎么知道用户提供的密码是否准确呢?

这就不得不提到我们前文说到的MD5的特征,我们知道,任意一段明文数据,经过加密以后,其结果必须永远是不变的,也就是说,如果需要验证用户密码是否正确,只需要将用户当前提供的密码使用MD5加密,然后和数据库中保存的密码字段比较就可以了。以下代码就可以实现这个功能:

<%@ Import Namespace="System.Security.Cryptography" %>

<%@ Import Namespace="System.Text" %>

<%@ Import Namespace="System.Data" %>

<%@ Import Namespace="System.Data.SqlClient" %>

<script runat="server" language="VB">

Sub Login(sender as Object, e as EventArgs)

'1. 建立数据库连接

Const strConnString as String = "connection string"

Dim objConn as New SqlConnection(strConnString)

'2. 建立Command对象

Dim strSQL as String = "SELECT COUNT(*) FROM UserAccount " &_

"WHERE Username=@Username AND Password=@Password"

Dim objCmd as New SqlCommand(strSQL, objConn)

'3. SQL参数

Dim paramUsername as SqlParameter

paramUsername = New SqlParameter("@Username", SqlDbType.VarChar, 25)

paramUsername.Value = txtUsername.Text

objCmd.Parameters.Add(paramUsername)

'加密密码信息

Dim md5Hasher as New MD5CryptoServiceProvider()

Dim hashedDataBytes as Byte()

Dim encoder as New UTF8Encoding()

hashedDataBytes = md5Hasher.ComputeHash(encoder.GetBytes(txtPwd.Text))

Dim paramPwd as SqlParameter

paramPwd = New SqlParameter("@Password", SqlDbType.Binary, 16)

paramPwd.Value = hashedDataBytes

objCmd.Parameters.Add(paramPwd)

objConn.Open()

Dim iResults as Integer = objCmd.ExecuteScalar()

objConn.Close()

If iResults = 1 then

'正确

Else

'错误

End If

End Sub

</script>

<form runat="server">

<h1>Login</h1>

用户账号: <asp:TextBox runat="server" id="txtUsername" />

密码:

<asp:TextBox runat="server" id="txtPwd" TextMode="Password" />

<p><asp:Button runat="server" Text="Login" OnClick="登录" />

</form>

●使用加密方式保存密码到数据库的限制

在决定是否使用加密方式保存密码以前,我们还需要考虑一些问题。因为MD5是单次加密算法,加密以后的信息不可以解密,所以,如果用户丢失密码,任何人都很难找到用户原来的密码,这时候,网站也就相应的失去一个很重要的功能,那就是用户提供其他信息来取得忘记的密码的功能,这不能不说是网站的一个大缺陷。另外,采用这样的加密方式,必须完全修改以前的用户资料,要求用户完全重新注册,这也是这种方法比较困难的一个地方。

●总结

以上我们详细介绍了MD5加密用户密码的实现方法,同时,也介绍了采用加密密码方式以后,用户鉴别的实现。并讨论了使用这种加密方式的应用限制。在实际应用中,我们可以将次方法做适当的修改和补充,以更加适合我们的应用需要


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/10864132.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-11
下一篇 2023-05-11

发表评论

登录后才能评论

评论列表(0条)

保存