android 和服务端rsa,android(客户端)和PC(服务器端)通信RSA 加密解密

android客户端代码:

package com.suning.reminder.util;

import java.io.BufferedReader;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.security.Key;

import java.security.KeyFactory;

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.NoSuchAlgorithmException;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.SecureRandom;

import java.security.Security;

import java.security.Signature;

import java.security.interfaces.RSAPrivateKey;

import java.security.interfaces.RSAPublicKey;

import java.security.spec.InvalidKeySpecException;

import java.security.spec.PKCS8EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

import java.util.Arrays;

import java.util.HashMap;

import java.util.Map;

import javax.crypto.Cipher;

import

org.apache.commons.configuration.ConfigurationException;

import

org.apache.commons.configuration.PropertiesConfiguration;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.openssl.PEMReader;

import org.bouncycastle.openssl.PasswordFinder;

public class RSAUtil {

public static final String KEY_ALGORITHM =

"RSA";

public static final String SIGNATURE_ALGORITHM =

"MD5withRSA";

private static final String PUBLIC_KEY =

"RSAPublicKey";

private static final String PRIVATE_KEY =

"RSAPrivateKey";

private static final int MAX_ENCRYPT_BLOCK =

117;

private static final int MAX_DECRYPT_BLOCK =

128;

public static String

privateKeyPath="private_key.pem";

public static String

publicKeyPath="public_key.pem";

public static File getFile(){

String

p=RSAUtil.class.getClassLoader().getResource(privateKeyPath).getPath();

return new

File(Escape.(p));

}

public static File getPublicFile(){

String

p=RSAUtil.class.getClassLoader().getResource(publicKeyPath).getPath();

return new

File(Escape.(p));

}

public static KeyPair generateKey() throws

NoSuchAlgorithmException{

KeyPairGenerator keyPairGen =

KeyPairGenerator.getInstance(KEY_ALGORITHM);

keyPairGen.initialize(1024, new

SecureRandom());

KeyPair keyPair =

keyPairGen.generateKeyPair();

return keyPair;

}

public static void saveKey(KeyPair keyPair,

String publicKeyFile,

String

privateKeyFile) throws ConfigurationException {

PublicKey pubkey =

keyPair.getPublic();

PrivateKey prikey =

keyPair.getPrivate();

// save public key

PropertiesConfiguration

publicConfig = new PropertiesConfiguration(

publicKeyFile);

publicConfig.setProperty("PULIICKEY",

toHexString(pubkey.getEncoded()));

publicConfig.save();

// save private key

PropertiesConfiguration

privateConfig = new PropertiesConfiguration(

privateKeyFile);

privateConfig.setProperty("PRIVATEKEY",

toHexString(prikey.getEncoded()));

privateConfig.save();

}

public static PrivateKey loadKeyPRI(String

filename)

throws

ConfigurationException, NoSuchAlgorithmException,

InvalidKeySpecException

{

PropertiesConfiguration config

= new PropertiesConfiguration(filename);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM,

new

BouncyCastleProvider());

// privateKey

String privateKeyValue =

config.getString("PRIVATEKEY");

PKCS8EncodedKeySpec priPKCS8 =

new PKCS8EncodedKeySpec(

toBytes(privateKeyValue));

PrivateKey privateKey =

keyFactory.generatePrivate(priPKCS8);

return privateKey;

}

public static PublicKey loadKeyPUB(String

filename)

throws

ConfigurationException, NoSuchAlgorithmException,

InvalidKeySpecException

{

PropertiesConfiguration config

= new PropertiesConfiguration(filename);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM,

new

BouncyCastleProvider());

// publicKey

String privateKeyValue =

config.getString("PULIICKEY");

X509EncodedKeySpec

bobPubKeySpec = new X509EncodedKeySpec(

toBytes(privateKeyValue));

PublicKey publicKey =

keyFactory.generatePublic(bobPubKeySpec);

return publicKey;

}

public static byte[] encrypt(RSAPublicKey

publicKey, byte[] data) {

if (publicKey != null) {

try {

Cipher

cipher = Cipher.getInstance(KEY_ALGORITHM,

new

BouncyCastleProvider());

cipher.init(Cipher.ENCRYPT_MODE,

publicKey);

return

cipher.doFinal(data);

} catch

(Exception e) {

e.printStackTrace();

}

}

return null;

}

public static byte[] decrypt(RSAPrivateKey

privateKey, byte[] raw) {

if (privateKey != null) {

try {

Cipher

cipher = Cipher.getInstance(KEY_ALGORITHM,

new

BouncyCastleProvider());

cipher.init(Cipher.DECRYPT_MODE,

privateKey);

return

cipher.doFinal(raw);

} catch

(Exception e) {

e.printStackTrace();

}

}

return null;

}

public static String toHexString(byte[] b)

{

StringBuilder sb = new

StringBuilder(b.length * 2);

for (int i = 0; i <

b.length; i++) {

sb.append(HEXCHAR[(b[i]

& 0xf0) >>> 4]);

sb.append(HEXCHAR[b[i]

& 0x0f]);

}

return sb.toString();

}

public static final byte[] toBytes(String s)

{

byte[] bytes;

bytes = new byte[s.length() /

2];

for (int i = 0; i <

bytes.length; i++) {

bytes[i] =

(byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2),

16);

}

return bytes;

}

private static char[] HEXCHAR = { '0', '1', '2',

'3', '4', '5', '6', '7',

'8', '9',

'a', 'b', 'c', 'd', 'e', 'f' };

public static Map genKeyPair() throws Exception

{

KeyPair keyPair =

generateKey();

RSAPublicKey publicKey =

(RSAPublicKey) keyPair.getPublic();

RSAPrivateKey privateKey =

(RSAPrivateKey) keyPair.getPrivate();

Map keyMap = new

HashMap(2);

keyMap.put(PUBLIC_KEY,

publicKey);

keyMap.put(PRIVATE_KEY,

privateKey);

return keyMap;

}

public static String sign(byte[] data, String

privateKey) throws Exception {

byte[] keyBytes =

Base64Utils.decode(privateKey);

PKCS8EncodedKeySpec

pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM);

PrivateKey privateK =

keyFactory.generatePrivate(pkcs8KeySpec);

Signature signature =

Signature.getInstance(SIGNATURE_ALGORITHM);

signature.initSign(privateK);

signature.update(data);

return

Base64Utils.encode(signature.sign());

}

public static boolean verify(byte[] data, String

publicKey, String sign)

throws

Exception {

byte[] keyBytes =

Base64Utils.decode(publicKey);

X509EncodedKeySpec keySpec =

new X509EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM);

PublicKey publicK =

keyFactory.generatePublic(keySpec);

Signature signature =

Signature.getInstance(SIGNATURE_ALGORITHM);

signature.initVerify(publicK);

signature.update(data);

return

signature.verify(Base64Utils.decode(sign));

}

public static byte[] decryptByPrivateKey(byte[]

encryptedData,

String

privateKey) throws Exception {

byte[] keyBytes =

Base64Utils.decode(privateKey);

PKCS8EncodedKeySpec

pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM);

Key privateK =

keyFactory.generatePrivate(pkcs8KeySpec);

Cipher cipher =

Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.DECRYPT_MODE,

privateK);

int inputLen =

encryptedData.length;

ByteArrayOutputStream out = new

ByteArrayOutputStream();

int offSet = 0;

byte[] cache;

int i = 0;

// 对数据分段解密

while (inputLen - offSet >

0) {

if (inputLen

- offSet > MAX_DECRYPT_BLOCK) {

cache

= cipher

.doFinal(encryptedData,

offSet, MAX_DECRYPT_BLOCK);

} else

{

cache

= cipher

.doFinal(encryptedData,

offSet, inputLen - offSet);

}

out.write(cache,

0, cache.length);

i++;

offSet = i *

MAX_DECRYPT_BLOCK;

}

byte[] decryptedData =

out.toByteArray();

out.close();

return decryptedData;

}

public static byte[] decryptByPublicKey(byte[]

encryptedData,

String

publicKey) throws Exception {

byte[] keyBytes =

Base64Utils.decode(publicKey);

X509EncodedKeySpec x509KeySpec

= new X509EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM);

Key publicK =

keyFactory.generatePublic(x509KeySpec);

Cipher cipher =

Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.DECRYPT_MODE,

publicK);

int inputLen =

encryptedData.length;

ByteArrayOutputStream out = new

ByteArrayOutputStream();

int offSet = 0;

byte[] cache;

int i = 0;

// 对数据分段解密

while (inputLen - offSet >

0) {

if (inputLen

- offSet > MAX_DECRYPT_BLOCK) {

cache

= cipher

.doFinal(encryptedData,

offSet, MAX_DECRYPT_BLOCK);

} else

{

cache

= cipher

.doFinal(encryptedData,

offSet, inputLen - offSet);

}

out.write(cache,

0, cache.length);

i++;

offSet = i *

MAX_DECRYPT_BLOCK;

}

byte[] decryptedData =

out.toByteArray();

out.close();

return decryptedData;

}

public static byte[] encryptByPublicKey(byte[]

data, String publicKey)

throws

Exception {

byte[] keyBytes =

Base64Utils.decode(publicKey);

X509EncodedKeySpec x509KeySpec

= new X509EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM);

Key publicK =

keyFactory.generatePublic(x509KeySpec);

// 对数据加密

Cipher cipher =

Cipher.getInstance("RSA/None/PKCS1Padding");//keyFactory.getAlgorithm()解决javax.crypto.BadPaddingException:

Blocktype异常的几种解决办法

cipher.init(Cipher.ENCRYPT_MODE,

publicK);

int inputLen =

data.length;

ByteArrayOutputStream out = new

ByteArrayOutputStream();

int offSet = 0;

byte[] cache;

int i = 0;

// 对数据分段加密

while (inputLen - offSet >

0) {

if (inputLen

- offSet > MAX_ENCRYPT_BLOCK) {

cache

= cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);

} else

{

cache

= cipher.doFinal(data, offSet, inputLen - offSet);

}

out.write(cache,

0, cache.length);

i++;

offSet = i *

MAX_ENCRYPT_BLOCK;

}

byte[] encryptedData =

out.toByteArray();

out.close();

return encryptedData;

}

public static byte[] encryptByPrivateKey(byte[]

data, String privateKey)

throws

Exception {

byte[] keyBytes =

Base64Utils.decode(privateKey);

PKCS8EncodedKeySpec

pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM);

Key privateK =

keyFactory.generatePrivate(pkcs8KeySpec);

Cipher cipher =

Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.ENCRYPT_MODE,

privateK);

int inputLen =

data.length;

ByteArrayOutputStream out = new

ByteArrayOutputStream();

int offSet = 0;

byte[] cache;

int i = 0;

// 对数据分段加密

while (inputLen - offSet >

0) {

if (inputLen

- offSet > MAX_ENCRYPT_BLOCK) {

cache

= cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);

} else

{

cache

= cipher.doFinal(data, offSet, inputLen - offSet);

}

out.write(cache,

0, cache.length);

i++;

offSet = i *

MAX_ENCRYPT_BLOCK;

}

byte[] encryptedData =

out.toByteArray();

out.close();

return encryptedData;

}

public static String getPrivateKey(Map

keyMap)

throws

Exception {

Key key = (Key)

keyMap.get(PRIVATE_KEY);

return

Base64Utils.encode(key.getEncoded());

}

public static String getPublicKey(Map

keyMap)

throws

Exception {

Key key = (Key)

keyMap.get(PUBLIC_KEY);

return

Base64Utils.encode(key.getEncoded());

}

public static PublicKey getPublicKey(String key) throws Exception

{ byte[]

keyBytes; keyBytes =Base64Utils.decode(key);

X509EncodedKeySpec keySpec = new

X509EncodedKeySpec(keyBytes); KeyFactory keyFactory =

KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicKey =

keyFactory.generatePublic(keySpec); return publicKey; } public

static byte[] encrypt(PublicKey pubkey, String text) {

try {

Cipher

rsa;

rsa =

Cipher.getInstance(KEY_ALGORITHM);

rsa.init(Cipher.ENCRYPT_MODE,

pubkey);

return

rsa.doFinal(text.getBytes());

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

public static String decrypt(PrivateKey

decryptionKey, byte[] buffer) {

try {

Cipher

rsa;

rsa =

Cipher.getInstance(KEY_ALGORITHM);

rsa.init(Cipher.DECRYPT_MODE,

decryptionKey);

byte[] utf8 =

rsa.doFinal(buffer);

return new

String(utf8, "UTF8");

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

public static KeyPair readKeyPair(File

privateKey, String keyPassword)

throws

IOException {

FileReader fileReader = new

FileReader(privateKey);

PEMReader r = new

PEMReader(fileReader, new DefaultPasswordFinder(

keyPassword.toCharArray()));

try {

return

(KeyPair) r.readObject();

} catch (IOException ex)

{

throw

ex;

} finally {

r.close();

fileReader.close();

}

}

public static Key readPublicKey(File privateKey,

String keyPassword)

throws

IOException {

FileReader fileReader = new

FileReader(privateKey);

PEMReader r = new

PEMReader(fileReader, new DefaultPasswordFinder(

keyPassword.toCharArray()));

try {

return

(RSAPublicKey) r.readObject();

} catch (IOException ex)

{

throw

ex;

} finally {

r.close();

fileReader.close();

}

}

public static class DefaultPasswordFinder

implements PasswordFinder {

private final char[]

password;

private

DefaultPasswordFinder(char[] password) {

this.password

= password;

}

@Override

public char[] getPassword()

{

return

Arrays.copyOf(password, password.length);

}

}

//

----------------------------------------------------------

// 测试

public static void main(String args[]) throws

Exception {

Security.addProvider(new

BouncyCastleProvider());

Security.addProvider(new

BouncyCastleProvider());

String userInfo=null;

//userInfo="logonId="+loginBackBean.getLogonId()+"&userId="+loginBackBean.getUserId()+"&phoneNo="+loginBackBean.getPhoneNo();

userInfo="logonId=15510717593&userId=34006452405&phoneNo=18801148262";

PublicKey publicKey =

RSAUtil.getPublicKey(getFromAssets("D:\workspace\PBR_SERVICE\src\main\resources\public_key.pem"));

System.out.println("r加密前文字:rn"

+ userInfo);

byte[] encodedData =

RSAUtil.encryptByPublicKey(userInfo.getBytes(), Base64Utils

.encode(publicKey.getEncoded()));

userInfo=Base64Utils.encode(encodedData);

System.out.println("加密后文字:rn" +

userInfo);

File privateKey =

RSAUtil.getFile();

Security.addProvider(new

BouncyCastleProvider());

KeyPair keyPair =

RSAUtil.readKeyPair(privateKey, "1234");

PrivateKey prikey =

keyPair.getPrivate();

byte[] decodedData =

RSAUtil.decryptByPrivateKey(

Base64Utils.decode(userInfo),

Base64Utils.encode(prikey.getEncoded()));

String target = new

String(decodedData);

System.out.println("解密后文字: rn"

+ target);

}

public void testSign() throws Exception {

// System.err.println("私钥加密——公钥解密");

// String source =

"这是一行测试RSA数字签名的无意义文字";

// System.out.println("原文字:rn" +

source);

// byte[] data =

source.getBytes();

// byte[] encodedData =

encryptByPrivateKey(data, privateKey);

// System.out.println("加密后:rn" +

new String(encodedData));

// byte[] decodedData =

decryptByPublicKey(encodedData, publicKey);

// String target = new

String(decodedData);

// System.out.println("解密后: rn"

+ target);

// System.err.println("私钥签名——公钥验证签名");

// String sign =

sign(encodedData, privateKey);

// System.err.println("签名:r" +

sign);

// boolean status =

verify(encodedData, publicKey, sign);

// System.err.println("验证结果:r" +

status);

}

public

String getFromAssets(String fileName){

String

result=""; try

{ InputStreamReader

inputReader = new

InputStreamReader(getAssets().open(fileName));

BufferedReader

bufReader = new

BufferedReader(inputReader); String

line=""; while((line =

bufReader.readLine()) != null) {

result +=

line; }

} catch

(Exception e) { e.printStackTrace(); } return

result; } }

发送请求:

PublicKey

publicKey =

RSAUtil.getPublicKey(getFromAssets(RSAUtil.publicKeyPath));

System.out.println("r加密前文字:rn"

+ userInfo);

//PublicKey

pubkey = (PublicKey)RSAUtil.encryptByPublicKey(publicKey);

byte[]

encodedData = RSAUtil.encryptByPublicKey(userInfo.getBytes(),

Base64Utils

.encode(publicKey.getEncoded()));

userInfo=Base64Utils.encode(encodedData);

System.out.println("加密后文字:rn"

+ userInfo);

服务器端主要代码:

public static byte[] decryptByPrivateKey(byte[]

encryptedData,

String

privateKey) throws Exception {

byte[] keyBytes =

Base64Utils.decode(privateKey);

PKCS8EncodedKeySpec

pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory keyFactory =

KeyFactory.getInstance("RSA"); Key

privateK = keyFactory.generatePrivate(pkcs8KeySpec);

Cipher cipher =

Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.DECRYPT_MODE,

privateK);

int inputLen =

encryptedData.length;

ByteArrayOutputStream out = new

ByteArrayOutputStream();

int offSet = 0;

byte[] cache;

int i = 0;

// 对数据分段解密

while (inputLen - offSet >

0) {

if (inputLen

- offSet > MAX_DECRYPT_BLOCK) {

cache

= cipher

.doFinal(encryptedData,

offSet, MAX_DECRYPT_BLOCK);

} else

{

cache

= cipher

.doFinal(encryptedData,

offSet, inputLen - offSet);

}

out.write(cache,

0, cache.length);

i++;

offSet = i *

MAX_DECRYPT_BLOCK;

}

byte[] decryptedData =

out.toByteArray();

out.close();

return decryptedData;

}

接收请求,处理私钥解密

File privateKey = RSAUtil.getFile();

Security.addProvider(new

BouncyCastleProvider()); KeyPair

keyPair = RSAUtil.readKeyPair(privateKey, "1234");

PrivateKey prikey =

keyPair.getPrivate();

byte[] decodedData =

RSAUtil.decryptByPrivateKey(

Base64Utils.decode(userInfo),

Base64Utils.encode(prikey.getEncoded()));

String target = new

String(decodedData);

System.out.println("解密后文字: rn"

+ target);

return target;

android客户端加密和pc服务器端解密需要注意事项:javax.crypto.BadPaddingException:

Blocktype

1.异常描述:最近做项目为了增强数据传输的安全性用到了RSA加密。即android客户端将要传送的信息,用私钥通过RSA非对称加密算法加密后,传到服务器端(PC端)。服务器端用对应(密钥)的公钥来解密时解密失败,抛出“javax.crypto.BadPaddingException:

Blocktype”异常。

2.异常原因:Android系统使用的虚拟机(dalvik)跟SUN标准JDK是有所区别的,其中他们默认的RSA实现就不同。即Android端用Cipher.getInstance("RSA")方法进行加密时,使用的provider是Bouncycastle

Security provider,Bouncycastle Security

provider默认实现的是“RSA/None/NoPadding”算法,而服务器(PC)端用Cipher.getInstance("RSA")进行解密时,使用的是Sun的security

provider,实现的是“RSA/None/PKCS1Padding”算法,所以,解密时会失败。

3.解决办法:我这里提供三种解决办法:

第一种:将服务器(pc)端的Cipher.getInstance("RSA")方法改为Cipher.getInstance("RSA/ECB/NoPadding")。但这种改法有一个缺点就是解密后的明文比加密之前多了很多空格。(空格的长度个数+原来的明文字符数=产生密钥时采用的bit数/8)

第二种:将Android端的Cipher.getInstance("RSA")方法改为Cipher.getInstance("RSA/None/PKCS1Padding")。这种方法解密后的明文和加密前的明文是对应的,不会出现第一种方法中的现象,推荐这种方法。

第三种:在服务器(pc)端的jdk中加入Bouncycastle Security provider,关于Bouncycastle

JCE的安装配置及验证请参看http://blog.csdn.net/caoshichao520326/article/details/8732670,

配置好Bouncycastle Security

provider后,将服务器(pc)端的Cipher.getInstance("RSA")方法改为Cipher.getInstance("RSA","BC")。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
客户端代码: ```python import socket import rsa # 生成公钥和私钥 (pubkey, privkey) = rsa.newkeys(512) # 连接服务器 host = '127.0.0.1' port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) # 发送公钥给服务器 s.send(pubkey.save_pkcs1()) # 接收加密消息 msg = s.recv(1024) # 使用私钥解密 msg = rsa.decrypt(msg, privkey).decode('utf-8') print('Received from server:', msg) # 发送消息给服务器 msg = 'Hello, server!' # 使用服务器的公钥加密 msg = rsa.encrypt(msg.encode('utf-8'), rsa.PublicKey.load_pkcs1(s.recv(1024))) s.send(msg) s.close() ``` 服务端代码: ```python import socket import rsa # 生成公钥和私钥 (pubkey, privkey) = rsa.newkeys(512) # 创建服务器 host = '127.0.0.1' port = 12345 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host, port)) s.listen(1) print('Waiting for client...') # 等待客户端连接 conn, addr = s.accept() print('Connected by', addr) # 接收客户端公钥 pubkey_client = rsa.PublicKey.load_pkcs1(conn.recv(1024)) # 发送加密消息给客户端 msg = 'Hello, client!' # 使用客户端的公钥加密 msg = rsa.encrypt(msg.encode('utf-8'), pubkey_client) conn.send(msg) # 接收客户端消息 msg = conn.recv(1024) # 使用私钥解密 msg = rsa.decrypt(msg, privkey).decode('utf-8') print('Received from client:', msg) conn.close() s.close() ``` 注意,这里的RSA加密只是为了演示如何使用RSA加密通信,实际应用中需要考虑安全性,并采用更加复杂的加密算法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值