![cover](https://devpress.csdnimg.cn/113e0eabab884fd28906290132beff20.jpeg)
鸿蒙仓颉语言【cryptocj 库】RC2、 RC4 、AES对称加密算法
2 提供RC2、 RC4 、AES对称加密算法 前置条件:NA 场景: 支持对称加密算法。 约束:RC2密钥长度一般16字节,加密块长度8字节;AES加密块长度16字节 性能: 支持版本几何性能持平 可靠性: NA 2.1 RC2 RC2 是一种对称加密算法,所见到的安全解决方案中,使用 RC2 的算法不多,从资料上看,RC2 算法可以替代 DES 算法,而且计算速度快,能在 16 位计算机上实现
2 提供RC2、 RC4 、AES对称加密算法
前置条件:NA
场景:
- 支持对称加密算法。
约束:RC2密钥长度一般16字节,加密块长度8字节;AES加密块长度16字节
性能: 支持版本几何性能持平
可靠性: NA
2.1 RC2
RC2 是一种对称加密算法,所见到的安全解决方案中,使用 RC2 的算法不多,从资料上看,RC2 算法可以替代 DES 算法,而且计算速度快,能在 16 位计算机上实现,密钥长度从 1 到 128 字节都可以。一般采用 16 字节,计算的数据块为 8 字节。
支持 ecb、cbc、cfb64、ofb64 加解密
2.1.1 主要接口
注意:
- RC2 一般采用 16 字节,计算的数据块为 8 字节,不足位补
\0
,输出缓存区为一个块长 8 字节 - 偏移量每次加解密操作需要重新赋值
- 密钥长度从 1 到 128 字节都可以,一般采用 16 字节。
- 加解密必须设置密钥
// 加密标识 1
public let RC2_ENCRYPT: Int32 = 1
// 解密标识 0
public let RC2_DECRYPT: Int32 = 0
// 加密块长
public let RC2_BLOCK: Int32 = 8
// 密钥长度
public let RC2_KEY_LENGTH: Int32 = 16
class RC2KEY
RC2KEY 类
public class RC2KEY {
/*
* 初始化 RC2KEY
*/
public init()
}
2.1.2 全局函数
/*
* 设置密钥
*
* 参数 key - RC2 的密钥类
* 参数 data - 密钥数据,不能为空
* 参数 bits - 密钥数据的位数
* 返回值 Unit
*/
public func rc2SetKey(key: RC2KEY, data: Array<UInt8>, bits: Int32): Unit
/*
* RC2 ecb 加解密计算
*
* 参数 inside - 输入数据,一个块的长度 8
* 参数 out - 输出缓存区,一个块的长度 8
* 参数 key - RC2 的密钥类
* 参数 enc - 加密/解密模式, 加密:RC2_ENCRYPT, 解密:RC2_DECRYPT
* 返回值 Unit
*/
public func rc2EcbEncrypt(inside: Array<UInt8>, out: Array<UInt8>, key: RC2KEY, enc: Int32): Unit
/*
* RC2 cbc加密/解密计算;
*
* 参数 inside - 输入数据,一个块的长度 8
* 参数 out - 输出缓存区,一个块的长度 8
* 参数 ks - RC2 的密钥类
* 参数 iv - 初始化向量
* 参数 enc - 加密/解密模式, 加密:RC2_ENCRYPT, 解密:RC2_DECRYPT
* 返回值 Unit
*/
public func rc2CbcEncrypt(inside: Array<UInt8>, out: Array<UInt8>, ks: RC2KEY, iv: Array<UInt8>, enc: Int32): Unit
/*
* RC2的cfb64 加密/解密计算
*
* 参数 inside - 输入数据,一个块的长度 8
* 参数 out - 输出缓存区,一个块的长度 8
* 参数 schedule - RC2 的密钥类
* 参数 ivec - 初始化向量
* 参数 num - ivec 索引
* 参数 enc - 加密/解密模式, 加密:RC2_ENCRYPT, 解密:RC2_DECRYPT
* 返回值 Unit
*/
public func rc2Cfb64Encrypt(inside: Array<UInt8>, out: Array<UInt8>, schedule: RC2KEY, ivec: Array<UInt8>, num: Int32, enc: Int32): Unit
/*
* RC2的ofb64 加密
*
* 参数 inside - 输入数据,一个块的长度 8
* 参数 out - 输出缓存区,一个块的长度 8
* 参数 schedule - RC2 的密钥类
* 参数 ivec - 初始化向量
* 参数 num - ivec 索引( 0<= num < RC2_BLOCK)
* 返回值 Unit
*/
public func rc2Ofb64Encrypt(inside: Array<UInt8>, out: Array<UInt8>, schedule: RC2KEY, ivec: Array<UInt8>, num: Int32): Unit
2.1.3 示例
from crypto4cj import rc2cj.*
from crypto4cj import utils.*
from encoding import base64.*
from encoding import hex.*
from std import collection.*
from std import math.*
main() {
var rc2key = RC2KEY()
var keys: String = "1234567896465451"
var datas: String = "helloword"
var res: Array<UInt8> = Array<UInt8>(8, item: 0)
rc2SetKey(rc2key, keys.toUtf8Array(), 0)
// 编码
var inside: Array<UInt8> = datas.toUtf8Array()
var insides2: ArrayList<Array<UInt8>> = arrayTo2Array(inside, 8)
var a: ArrayList<UInt8> = ArrayList<UInt8>()
for(i in 0..insides2.size) {
rc2EcbEncrypt(insides2[i], res, rc2key, RC2_ENCRYPT)
var b = res
a.appendAll(b)
}
var resultE = toHexString(a.toArray())
if(resultE != "97d61c569253660da654fb13588f9c84") {
return -1
}
// 解码
var deRes: Array<UInt8> = Array<UInt8>(8, item: 0)
var deData = fromHexString(resultE).getOrThrow()
var insides3: ArrayList<Array<UInt8>> = arrayTo2Array(deData, 8)
var c: ArrayList<UInt8> = ArrayList<UInt8>()
for(i in 0..insides3.size) {
rc2EcbEncrypt(insides3[i], deRes, rc2key, RC2_DECRYPT)
var b = deRes
c.appendAll(b)
}
var decryptRes = String.fromUtf8(c.toArray())
if(!decryptRes.contains(datas)) {
return -1
}
return 0
}
运行结果如下:
0
2.2 RC4
RC4(Ron Rivest Cipher 4)是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,属于对称加密算法,是使用最广泛的序列密码。RC4 是一种基于非线性数据表变换的序列密码。它以一个足够大的数据表(S盒)为基础,对表进行非线性变换,产生非线性的密钥流序列。它是一个可变密钥长度、面向字节操作的序列密码,该算法以随机置换作为基础。
支持 rc4 加解密
2.2.1 主要接口
class RC4KEY
RC4KEY 类
public class RC4KEY {
/*
* 初始化 RC4KEY
*/
public init()
2.2.2 全局函数
/*
* 设置密钥
*
* 参数 key - RC4 的密钥类
* 参数 data - 密钥数据
* 返回值 Unit
*/
public func rc4SetKey(key: RC4KEY, data: Array<UInt8>): Unit
/*
* RC4 加解密计算
*
* 参数 key - key 值
* 参数 indata - 加密的数据
* 参数 outdata - 加解密后的数据
* 返回值 Unit
*/
public func rc4(key: RC4KEY, indata: Array<UInt8>, outdata: Array<UInt8>): Unit
2.1.3 示例
from crypto4cj import rc4cj.*
from encoding import base64.*
from std import collection.*
from std import math.*
main() {
var keys: Array<UInt8> = "1234567891111111".toUtf8Array()
var indata: Array<UInt8> = "helloword".toUtf8Array()
var encodeRes = rc4Encode(indata, keys)
var decodeRes = rc4Decode(encodeRes, keys)
if(toBase64String(encodeRes) != "Dqd7cGrLT0a7" || String.fromUtf8(decodeRes) != "helloword") {
return -1
}
return 0
}
func rc4Encode(indata: Array<UInt8>, keys: Array<UInt8>): Array<UInt8> {
var indataLen: Int32 = Int32(indata.size)
var keysLen: Int32 = Int32(keys.size)
var outdata: Array<UInt8> = Array<UInt8>(Int64(indataLen) , item: 0)
if(indataLen == 0 || keysLen == 0) {
return outdata
}
var key = RC4KEY()
rc4SetKey(key, keys)
rc4(key, indata, outdata)
return outdata
}
func rc4Decode(indata: Array<UInt8>, keys: Array<UInt8>): Array<UInt8> {
var indataLen: Int32 = Int32(indata.size)
var keysLen: Int32 = Int32(keys.size)
var outdata: Array<UInt8> = Array<UInt8>(Int64(indataLen) , item: 0)
if(indataLen == 0 || keysLen == 0) {
return outdata
}
var key = RC4KEY()
rc4SetKey(key, keys)
rc4(key, indata, outdata)
return outdata
}
运行结果如下:
0
2.3 AES
密码学中的高级加密标准(Advanced Encryption Standard,AES),又称 Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
支持 aes、ecb、cbc、cfb128、cfb1、cfb8、ofb128、Ige、bilge、wrap、unwrap 加解密
注意:
- AES 计算的数据块一般为 16 字节(或16字节的倍数),不足位补
\0
- 调用加密函数之前,必须先设置加密key:aesSetEncryptKey
- 调用解密函数之前,必须先设置解密key:aesSetDecryptKey
- 对于 aesCbcEncrypt 加密解密,每次调用前必须先初始化 ivec 向量, ivec 一般为 16 字节
- 密码长度支持 128/192/256 bits
2.3.1 主要接口
// 加密标识 1
public let AES_ENCRYPT: Int32 = 1
// 解密标识 0
public let AES_DECRYPT: Int32 = 0
// 加密块长
public let AES_BLOCK_SIZE: Int32 = 16
class AESKEY
/*
* 初始化 AESKEY
*/
public init()
2.3.2 全局函数
注意:
- userKey 长度 16、24、32
- bits 长度 对应为 128、192、256
- 加解密密钥需相同
func aesSetEncryptKey
/*
* 设置加密密钥
*
* 参数 userKey - 密钥,必须为 16、24、32
* 参数 bits - 密钥位数,与 userKey ,对应为 128、192、256
* 参数 key - AESKEY
* 返回值 Unit
*/
public func aesSetEncryptKey(userKey: Array<UInt8>, bits: Int32, key: AESKEY): Unit
func aesSetDecryptKey
/*
* 设置解密密钥(加解密的参数需一致)
*
* 参数 userKey - 密钥,必须为 16、24、32
* 参数 bits - 密钥位数,与 userKey ,对应为 128、192、256
* 参数 key - AESKEY
* 返回值 Unit
*/
public func aesSetDecryptKey(userKey: Array<UInt8>, bits: Int32, key: AESKEY): Unit
func aesEncrypt
注意:
- AES 计算的数据块一般为 16 字节(或16字节的倍数),不足位补
\0
, 输出缓存区为一个数据块长 16 字节
/*
* 加密数据
*
* 参数 inside - 加密数据,一个数据块长 16 字节
* 参数 outside - 输出缓存区,一个数据块长 16 字节
* 参数 key - AESKEY
* 返回值 Unit
*/
public func aesEncrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY): Unit
func aesDecrypt
注意:
- AES 计算的数据块一般为 16 字节(或16字节的倍数),不足位补
\0
,输出缓存区为一个数据块长 16 字节
/*
* 解密数据
*
* 参数 inside - 解密数据,一个数据块长 16 字节
* 参数 outside - 输出缓存区,一个数据块长 16 字节
* 参数 key - AESKEY
* 返回值 Unit
*/
public func aesDecrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY): Unit
func aesEcbEncrypt
注意:
- AES 计算的数据块一般为 16 字节(或16字节的倍数),不足位补
\0
,输出缓存区为一个数据块长 16 字节
/*
* 加解密数据
*
* 参数 inside - 输入数据,一个数据块长 16 字节
* 参数 outside - 输出缓存区,一个数据块长 16 字节
* 参数 key - AESKEY
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesEcbEncrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, enc: Int32): Unit
func aesCbcEncrypt
注意:
- AES 计算的数据块一般为 16 字节(或16字节的倍数),不足位补
\0
,输出缓存区为一个数据块长 16 字节 - 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 16 字节
/*
* 加解密数据
*
* 参数 inside - 输入数据,一个数据块长 16 字节
* 参数 outside - 输出缓存区,一个数据块长 16 字节
* 参数 key - AESKEY
* 参数 ivec - 初始向量
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesCbcEncrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>, enc: Int32): Unit
func aesCfb128Encrypt
注意:
- CFB128 模式加密和解密均使用 aesSetEncryptKey,这一点比较反常,务必记住。
- CFB128 模式可一次性加解密。
- 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 16 字节
- CFB128 单词中的 128 属于 cfb 模式中加解密时分组继续分段的段的大小,与密钥 128 无关
/*
* 加解密数据
*
* 参数 inside - 输入数据,长度任意
* 参数 outside - 输出数据,长度与输入数据相等
* 参数 key - AESKEY
* 参数 ivec - 可读写的一块内存。长度必须是16字节。
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesCfb128Encrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>, enc: Int32): Unit
func aesCfb1Encrypt
注意:
- CFB1 模式加密和解密均使用 aesSetEncryptKey,这一点比较反常,务必记住。
- CFB1 模式可一次性加解密。
- 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 16 字节
- CFB1 单词中的 1 属于 cfb 模式中加解密时分组继续分段的段的大小,与密钥 128 无关
/*
* 加解密数据
*
* 参数 inside - 输入数据,长度任意
* 参数 outside - 输出数据,长度与输入数据相等
* 参数 key - AESKEY
* 参数 ivec - 可读写的一块内存。长度必须是16字节。
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesCfb1Encrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>, enc: Int32): Unit
func aesCfb8Encrypt
注意:
- CFB8 模式加密和解密均使用 aesSetEncryptKey,这一点比较反常,务必记住。
- CFB8 模式可一次性加解密。
- 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 16 字节
- CFB8 单词中的 8 属于 cfb 模式中加解密时分组继续分段的段的大小,与密钥 128 无关
/*
* 加解密数据
*
* 参数 inside - 输入数据,长度任意
* 参数 outside - 输出数据,长度与输入数据相等
* 参数 key - AESKEY
* 参数 ivec - 可读写的一块内存。长度必须是16字节。
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesCfb8Encrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>, enc: Int32): Unit
func aesOfb128Encrypt
注意:
- OFB128 模式加密和解密均使用 aesSetEncryptKey,这一点比较反常,务必记住。
- OFB128 模式可一次性加解密。
- 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 16 字节。
- aesOfb128Encrypt函数既是加密,又是解密。当 inside 为明文时,执行的是加密操作;当 inside 为密文时,执行的是解密操作。
- OFB128 单词中的 128 属于 Ofb 模式中加解密时分组继续分段的段的大小,与密钥 128 无关
/*
* 加解密数据
*
* 参数 inside - 输入数据,长度任意
* 参数 outside - 输出数据,长度与输入数据相等
* 参数 key - AESKEY
* 参数 ivec - 可读写的一块内存。长度必须是16字节。
* 返回值 Unit
*/
public func aesOfb128Encrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>): Unit
func aesIgeEncrypt
注意:
- 可一次性加解密。
- 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 32(加密数据块的 2 倍) 字节。
- 输入数据长度必须是 16 的整数倍
/*
* 加解密数据
*
* 参数 inside - 输入数据,长度必须是 16 的整数倍
* 参数 outside - 输出数据,长度与输入数据相等
* 参数 key - AESKEY
* 参数 ivec - 可读写的一块内存。长度必须是 32 字节(加密数据块的 2 倍)。
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesIgeEncrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>, enc: Int32): Unit
func aesBiIgeEncrypt
注意:
- 可一次性加解密。
- 对于加密解密,每次调用前必须先初始化 ivec 向量, ivec 必须是 64(加密数据块的 4 倍) 字节。
- 输入数据长度必须是 16 的整数倍
/*
* 加解密数据
*
* 参数 inside - 输入数据,长度必须是 16 的整数倍
* 参数 outside - 输出数据,长度与输入数据相等
* 参数 key - AESKEY
* 参数 ivec - 可读写的一块内存。长度必须是 64 字节(加密数据块的 4 倍)。
* 参数 enc - 加解密参数,加密: AES_ENCRYPT = 1,解密:AES_DECRYPT = 0
* 返回值 Unit
*/
public func aesBiIgeEncrypt(inside: Array<UInt8>, outside: Array<UInt8>, key: AESKEY, ivec: Array<UInt8>, enc: Int32): Unit
func aesWrapEncrypt
注意:
- 需设置加密密钥
- ivec 偏移量必须为 8 字节
- 输入数据长度必须是 8 的整数倍,且 大于等于 16 位
- 最小输出缓存区长度 = 输入数据长度 + 8
/*
* 加解密数据
*
* 参数 key - AESKEY
* 参数 ivec - 长度必须是 8 字节
* 参数 outside - 输出数据,等于 输入数据长度 + 8
* 参数 inside - 输入数据,长度必须是 8 的整数倍,且 大于等于 16 位
* 返回值 Unit
*/
public func aesWrapEncrypt(key: AESKEY, ivec: Array<UInt8>, outside: Array<UInt8>, inside: Array<UInt8>): Unit
func aesUnWrapEncrypt
注意:
- 需设置解密密钥
- ivec 偏移量必须为 8 字节
- 输入数据长度必须是 8 的整数倍,且 大于等于 24 位
- 最小输出缓存区长度 = 输入数据长度 - 8
/*
* 加解密数据
*
* 参数 key - AESKEY
* 参数 ivec - 长度必须是 8 字节
* 参数 outside - 输出数据,等于 输入数据长度 + 8
* 参数 inside - 输入数据,长度必须是 8 的整数倍,且 大于等于 24 位
* 返回值 Unit
*/
public func aesUnWrapEncrypt(key: AESKEY, ivec: Array<UInt8>, outside: Array<UInt8>, inside: Array<UInt8>): Unit
2.3.3 示例
from crypto4cj import aescj.*
from crypto4cj import utils.*
from encoding import hex.*
from std import collection.*
from std import unicode.*
main() {
var keys: Array<UInt8> = "1234567812345678".toUtf8Array()
var inside: Array<UInt8> = "skfhafahglkahglahglkahgalg".toUtf8Array()
var encodeRes = aesEncode(inside, keys)
if(toHexString(encodeRes) != "7da4e06948c190ecf633625517c1e7cbd40afb1fbe2dd55438c8f806c1c549d5") {
return -1
}
var decodeRes = aesDecode(encodeRes, keys)
if(!String.fromUtf8(decodeRes).contains("skfhafahglkahglahglkahgalg")) {
return -1
}
return 0
}
func aesEncode(inside: Array<UInt8>, keys: Array<UInt8>): Array<UInt8> {
var key = AESKEY()
var outside: Array<UInt8> = Array<UInt8>(Int64(AES_BLOCK_SIZE), item: 0)
var keyRet = aesSetEncryptKey(keys, 128, key)
var data: ArrayList<Array<UInt8>> = arrayTo2Array(inside, Int64(AES_BLOCK_SIZE))
var res: ArrayList<UInt8> = ArrayList<UInt8>()
for( i in 0..data.size ) {
aesEncrypt(data[i], outside, key)
res.appendAll(outside)
}
return res.toArray()
}
func aesDecode(inside: Array<UInt8>, keys: Array<UInt8>): Array<UInt8> {
var key = AESKEY()
var outside: Array<UInt8> = Array<UInt8>(Int64(AES_BLOCK_SIZE), item: 0)
var keyRet = aesSetDecryptKey(keys, 128, key)
var data: ArrayList<Array<UInt8>> = arrayTo2Array(inside, Int64(AES_BLOCK_SIZE))
var res: ArrayList<UInt8> = ArrayList<UInt8>()
for( i in 0..data.size ) {
aesDecrypt(data[i], outside, key)
res.appendAll(outside)
}
return res.toArray()
}
运行结果如下:
0
最后呢
很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。
而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点
如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。
针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细鸿蒙(OpenHarmony )手册(共计1236页)与鸿蒙(OpenHarmony )开发入门视频,帮助大家在技术的道路上更进一步。
- 《鸿蒙 (OpenHarmony)开发学习视频》
- 《鸿蒙生态应用开发V2.0白皮书》
- 《鸿蒙 (OpenHarmony)开发基础到实战手册》
- OpenHarmony北向、南向开发环境搭建
- 《鸿蒙开发基础》
- 《鸿蒙开发进阶》
- 《鸿蒙开发实战》
鸿蒙开发应用知识已更新gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md参考前往
总结
鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。
并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行! 自↓↓↓拿
更多推荐
所有评论(0)