openssl C++ DSA对指定文件内容签名和解签

证书工具之OpenSSL 1、查看openssl完整版本信息 [root@localhost ~]# openssl version -a OpenSSL 1.0.1e-fips 11 Feb 2013 built on: Mon May 9 09:54:24 CDT 2016 platform: linux-x86_64 options: bn(64,64) md2(int) rc4(16x,int) des(id... 阅读详情

DSA密钥对的生成请参考我这篇文章openssl在windows上生成RSA密钥、DSA密钥

用DSA私钥对指定文件签名

//DSA用私钥对文件内容签名
#pragma warning(disable:4996)
#include <iostream>
#include <fstream>
#include <openssl/dsa.h>
#include <openssl/pem.h>

using namespace std;

/*
函数名:DSA_sign
函数功能:用openssl的DSA接口读取DSA私钥,对指定文件内容进行签名,将签名保存到另一个文件中
输入参数:None
返回参数:bool 若签名成功,返回true,否则返回false
作者:Leo Ma
时间:2019.09.15
*/

bool DSA_sign(string input_file,string sign_file)
{
	
	DSA* dsa_private_key = DSA_new();//定义DSA私钥
	unsigned char* input_string = new unsigned char;//定义要签名的字符串
	unsigned char* sign_string;//定义签名后的字符串
	unsigned int sig_len;//签名长度


	//从文件中读取DSA私钥
	BIO *priio;//Binary IO
	priio = BIO_new_file("dsa_private_key.pem", "rb");
	dsa_private_key = PEM_read_bio_DSAPrivateKey(priio, &dsa_private_key, NULL, NULL);


	//从文件中读取要签名的内容,放在input_string中
	ifstream fin(input_file);
	if (!fin.is_open())
	{
		cout << "WARNING! NOT FOUND CHECK FILE,PLEASE CONTACT TO THE ADMISTRATOR!!!" << endl;
		return 0;
	}
	fin >> input_string;
	fin.close();



	//为签名后的字符串sign_string分配内存,如果不能正常分配内存,DSA_sign()将返回false
	sign_string = (unsigned char*)calloc(DSA_size(dsa_private_key), sizeof(unsigned char));
	if (sign_string == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for sign_string\n");
		return false;
		//exit(-1);
	}


	//对input_string进行签名
	//DSA_sign输入参数:签名类型,要签名的字符串地址,要签名的字符串长度,签名后的字符串存放地址,签名后的字符串长度,私钥
	//如果签名失败,DSA_sign()将返回0,DSA_sign()将返回false
	if (DSA_sign(0, input_string, strlen((char*)input_string),
		sign_string, &sig_len, dsa_private_key) == 0)
	{
		fprintf(stderr, "Sign Error.\n");
		return false;
		//exit(-1);
	}



	//将签名后的字符串内容保存到一个文件中
	ofstream fout;
	fout.open(sign_file);
	fout << sign_string;
	fout.close();

	return true;
}



int main(int argc, char** argv) 
{

	DSA_sign("check_file.txt", "checked_file.txt");
	system("pause");
}

用DSA公钥验证签名

#pragma warning(disable:4996)
#include <iostream>
#include <fstream>
#include <openssl/dsa.h>
#include <openssl/pem.h>
#include <string>

using namespace std;


/*
函数名:DSA_verify_general
函数功能:用openssl的DSA接口验证解签后的信息是否与给定信息相同
输入参数:	string sign_file 签名文件
			string verify_string 给定的验证信息
返回参数:bool 若验证通过,返回true,否则返回false
作者:Leo Ma
时间:2019.09.15
*/

bool DSA_verify_general(string sign_file, string verify_string)
{
	//将DSA公钥写死在文件中
	//把DSA公钥文件内容复制到下面的字符串里,将多余的空格(这是VS编辑器的问题)删掉,换行符遵循UNIX(LF)格式,即使用\n换行
	string dsa_public_key_string = "-----BEGIN PUBLIC KEY-----\nMIIBtjCCASsGByqGSM44BAEwggEeAoGBAKenvEftAsim1cta2VzV+SCSiwJZFq6v\n+UvbdOTkM+mB9vq4NcV1mOEgPyXczgb+dU/Mn7Y0Zi5CkvCe18YyjaillKfGjVCt\n9ftffA/ML3axyfxsiPAuX0I1GRWJk6qbFaXImUAKUAGUiK1yKtJPnXIbnIQBYxji\nAXUcV6Pa6KwfAhUAhHBmLP8lIBIso865Bz7z5XQMKmkCgYBZiMp+oVyEuzZ8eS1d\nBWvbyKk/gnvGMxv10o/X1dWiD7JlT9iUl6J1kh8BnEIB6D4+zXStgq/g/t8+j/fK\nbwmin/zTC5LrzPtvzS2nsCEM4jL5UJzil24k3F8AQX5PUCt+TOwA0DudLz59K+An\nME+CxAPrCkcjg2tmco1RwO1QhQOBhAACgYA3ix4u+0IrSKDI605qCUgmBBbD3YlK\niACidMGZpzZnTaY3GzFt5m/Yn8o2mRfU2/Ouhssuum7uKXep4N9OhidM76ErPbpV\nIHP7n6fWx09MdLz5HkOmomVI8s9gQEog89Y9sw5KMymxln44QGyJPRqnRIKK+h58\ncX/Uar7hPftz7A==\n-----END PUBLIC KEY-----\n";
	ofstream public_key_file;
	public_key_file.open("dsa_public_key.pem");
	public_key_file << dsa_public_key_string;
	public_key_file.close();


	//从刚写好的文件中读取DSA公钥
	DSA* dsa_public_key = DSA_new();
	BIO *pubio;//Binary IO
	pubio = BIO_new_file("dsa_public_key.pem", "rb");
	dsa_public_key = PEM_read_bio_DSA_PUBKEY(pubio, &dsa_public_key, NULL, NULL);
	/*
	//另一种读取DSA公钥的方法
	DSA* dsa_public_key = DSA_new();
	FILE *pubio;
	pubio = fopen("dsa_public_key.pem", "rb");
	dsa_public_key = PEM_read_DSA_PUBKEY(pubio, &dsa_public_key, NULL, NULL);
	*/



	//从文件中读取DSA签名
	string sign_string;
	ifstream fin(sign_file);
	if (!fin.is_open())
	{
		cout << "WARNING! NOT FOUND CHECKED FILE,PLEASE CONTACT TO THE ADMISTRATOR!!!" << endl;
		return 0;
	}
	fin >> sign_string;
	fin.close();



	//用DSA公钥解签并验证是否跟验证信息一致
	int is_valid_signature = DSA_verify(0, (const unsigned char*)verify_string.data(), verify_string.length(),
		(const unsigned char*)sign_string.data(), sign_string.length(), dsa_public_key);


	//返回一个bool型变量
	if (is_valid_signature == 1)return true;
	else return false;
}



int main()
{
	if (DSA_verify_general("checked_file.txt", "BFEBFBFF000906E9E0D55EC381F0"))
	{
		cout << "VERIFY SUCCESSFULLY!!!" << endl;
	}
	else
	{
		cout << "VERIFY FAIL!!!" << endl;
	}
	system("pause");
	return 0;
}

用DSA公钥验证解签后的信息是否与机器码一致

关于如何获取运行机器的机器码请参考我这篇文章C++在windows下获得运行主机的硬件信息:CPU序列号、MAC地址、硬盘序列号、主板序列号

#pragma warning(disable:4996)
#include <iostream>
#include <fstream>
#include <string>
#include "get hardware information/get_hardware_information.h"
#include <openssl/dsa.h>
#include <openssl/pem.h>
using namespace std;


/*
函数名:my_get_hardware_information
函数功能:获得本机硬件信息:CPU序列号,Mac地址,主板序列号,硬盘序列号
输入参数:None
返回参数:string类型,CPU序列号+Mac地址
作者:Leo Ma
时间:2019.09.15
*/
string my_get_hardware_information()
{
	char lpszCpu[128] = "";
	char lpszMac[128] = "";
	char lpszBaseBoard[128] = "";
	char lpszHDSerial[128] = "";

	GetCpuSerialByCmd(lpszCpu, 128);
	GetMacByCmd(lpszMac, 128);
	GetBaseBoardByCmd(lpszBaseBoard, 128);
	GetHDSerialByCmd(lpszHDSerial);
	//cout << "CPU ID is " << lpszCpu << endl;
	//cout << "MAC ID is " << lpszMac << endl;
	//cout << "Base Board ID is " << lpszBaseBoard << endl;
	//cout << "HD Serial is " << lpszHDSerial << endl;

	string string_return;
	string_return.assign(lpszCpu);
	string_return.append(lpszMac);
	//string_return.append("\0");
	//string_return.append(lpszBaseBoard);
	//string_return.append(lpszHDSerial);

	//cout << string_return << endl;
	return string_return;
}




/*
函数名:DSA_verify_machine
函数功能:用openssl的DSA接口验证当前机器码是否与解签的机器码一致
输入参数:None
返回参数:bool 若验证通过,返回true,否则返回false
作者:Leo Ma
时间:2019.09.15
*/

bool DSA_verify_machine()
{

	//读取本机机器码
	string hardware_info;
	hardware_info = my_get_hardware_information();//只获取CPU ID和Mac地址
	//hardware_info = get_hardware_information("check_file.txt");//获取CPU ID、Mac地址、主板序列号、硬盘序列号,并且生成文件
	

	//将DSA公钥写死在文件中
	//把DSA公钥文件内容复制到下面的字符串里,将多余的空格(这是VS编辑器的问题)删掉,换行符遵循UNIX(LF)格式,即使用\n换行
	string dsa_public_key_string = "-----BEGIN PUBLIC KEY-----\nMIIBtjCCASsGByqGSM44BAEwggEeAoGBAKenvEftAsim1cta2VzV+SCSiwJZFq6v\n+UvbdOTkM+mB9vq4NcV1mOEgPyXczgb+dU/Mn7Y0Zi5CkvCe18YyjaillKfGjVCt\n9ftffA/ML3axyfxsiPAuX0I1GRWJk6qbFaXImUAKUAGUiK1yKtJPnXIbnIQBYxji\nAXUcV6Pa6KwfAhUAhHBmLP8lIBIso865Bz7z5XQMKmkCgYBZiMp+oVyEuzZ8eS1d\nBWvbyKk/gnvGMxv10o/X1dWiD7JlT9iUl6J1kh8BnEIB6D4+zXStgq/g/t8+j/fK\nbwmin/zTC5LrzPtvzS2nsCEM4jL5UJzil24k3F8AQX5PUCt+TOwA0DudLz59K+An\nME+CxAPrCkcjg2tmco1RwO1QhQOBhAACgYA3ix4u+0IrSKDI605qCUgmBBbD3YlK\niACidMGZpzZnTaY3GzFt5m/Yn8o2mRfU2/Ouhssuum7uKXep4N9OhidM76ErPbpV\nIHP7n6fWx09MdLz5HkOmomVI8s9gQEog89Y9sw5KMymxln44QGyJPRqnRIKK+h58\ncX/Uar7hPftz7A==\n-----END PUBLIC KEY-----\n";
	ofstream public_key_file;
	public_key_file.open("dsa_public_key.pem");
	public_key_file << dsa_public_key_string;
	public_key_file.close();
	
	
	//从刚写好的文件中读取DSA公钥
	DSA* dsa_public_key = DSA_new();
	BIO *pubio;//Binary IO
	pubio = BIO_new_file("dsa_public_key.pem", "rb");
	dsa_public_key = PEM_read_bio_DSA_PUBKEY(pubio, &dsa_public_key, NULL, NULL);
	/*
	//另一种读取DSA公钥的方法
	DSA* dsa_public_key = DSA_new();
	FILE *pubio;
	pubio = fopen("dsa_public_key.pem", "rb");
	dsa_public_key = PEM_read_DSA_PUBKEY(pubio, &dsa_public_key, NULL, NULL);
	*/
	


	//从文件中读取DSA签名
	string sign_string;
	ifstream fin("checked_file.txt");
	if (!fin.is_open())
	{
		cout << "WARNING! NOT FOUND CHECKED FILE,PLEASE CONTACT TO THE ADMISTRATOR!!!" << endl;
		return 0;
	}
	fin >> sign_string;
	fin.close();
	


	//用DSA公钥验证签名和读取机器码是否一致
	int is_valid_signature = DSA_verify(0, (const unsigned char*)hardware_info.data(), hardware_info.length(),
											(const unsigned char*)sign_string.data(), sign_string.length(), dsa_public_key);


	//返回一个bool型变量
	if (is_valid_signature == 1)return true;
	else return false;
}



int main()
{
	if (DSA_verify_machine())
	{
		cout << "验证通过,假装执行代码" << endl;
	}
	else
	{
		cout << "CHECKED FAIL!PLEASE CONTACT THE ADMINASTOR!!!" << endl;
	}
	system("pause");
	return 0;
}
C++与Java之RSA签名与验签 最近一段时间一直被一个事情困扰:支付相关RSA签名与验证签名,服务器使用java,客户端是c++的程序,在C++端验证签名的时候,试用了很多方法都无法签名通过。在java中,签名和验证签名很容易调用现有的类实现,但是在c++中却是不太容易。      采用openssl原生的c++程序,不行;      在网上搜索了很久,也翻墙google了,试用了很多,也不行;      用了其他网友借鉴 阅读详情

相关推荐

openssl DSA 数字签名与签名验证

与RSA不同,DSA的主要功能是做数字签名与签名验证。DSA不具备对数据进行加密解密的功能,因此在使用上面请注意。往往有很多使用者误认为非对称加密算法都可以对数据进行加解密处理,其实不然,DSA就是一个很好的例子。 使用C/C++ 调用openssl/crypt库进行DSA测试,代码如下: /* gcc -o test test_dsa.c ../../libcrypto.a

天剑 3181

实战篇-OpenSSL之RSA算法-加密与数字签名

本文属于《OpenSSL加密算法库使用系列教程》之一,欢迎查看其它文章。 实战篇-OpenSSL之RSA算法-加密与数字签名一、RSA简介二、OFB64模式1、命令行操作2、函数说明3、编程实现(1)特别注意(2)实现OFB64模式加解密(3)测试代码 一、RSA简介 RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

百里杨的博客 1万+

基于GMP和Openssl实现DSA对任意文件签名验证

DSA具体过程:需要特别说明的是全局参数p,q的生成方法,不要只想着先求素数p再求p-1的素因子,官方的方法是先生成素数q,再用素数q去反过来求p.stackoverflow的解决方案:https://stackoverflow.com/questions/8350568/dsa-how-to-generate-the-subprimeC++代码实现:dsa.cpp#include<iostream

jaymegs的博客 1029

C++ openssl ECDSA签名

1、Openssl库安装及交叉编译 下载openssl库,https://www.openssl.org/source/ 将库文件解压到本地文件后进行配置: a、config配置: 进入解压后的目录,执行 ./config shared --prefix=/usr/local/openssl --openssldir=/usr/local/ 其中 shared 为生成动态连...

arm89782的博客 1507

Openssl C++ API

Openssl C++ API Base64 将8位二进制信息编码为ASCII码。 openssl命令 base64 or-enc base64 EVP API EVP_EncodeBlock((unsigned char *)encodeData,sourceDta,16); EVP EVP_PKEY 对象用于存储公钥和(可选)私钥,以及相关的算法和参数。 支持的类型有: EVP_PKEY_EC:椭圆曲线密钥(用于 ECDSA 和 ECDH) - 支持签名/验证操作和密钥派生 EVP_PKEY_RS

一只菜鸟的练级路 1068

纯C++实现APK签名提取:摆脱keytool依赖,深入解析PKCS#7证书

在Android应用安全分析和自动化构建中,应用签名是验证APK来源和完整性的核心机制。其原理基于非对称加密和数字证书技术,通过私钥对应用内容进行签名,公钥则嵌入APK的证书中用于验证。这项技术的价值在于确保应用未被篡改,并确认开发者身份,是构建可信应用生态的基石。在自动化安全审计、CI/CD流水线及嵌入式设备等应用场景中,传统依赖Java环境调用keytool或apksigner命令行工具的方式存在环境配置复杂、进程调用开销大等局限。本文聚焦于使用纯C++方案,通过libzip库直接读取APK的ZIP结构

weixin_31421613的博客 313

C++ 使用OpenSSL 基于SHA1摘要的RSA签名及验签 与Java平台互通

文章目录准备C++JavaRSASignature.javaRSAEncrypt.javaBase64.java 准备 配置OpenSSL环境 配置VS2015环境 生成公私秘钥 C++ #pragma once #include <openssl/evp.h> #include <openssl/err.h> #include <openssl/rsa.h>...

宋旭升的博客 2038

C++ openssl ECDSA签名

1、Openssl库安装及交叉编译 下载openssl库,https://www.openssl.org/source/ 将库文件解压到本地文件后进行配置: a、config配置: 进入解压后的目录,执行 ./config shared --prefix=/usr/local/openssl --openssldir=/usr/local/ 其中 shared 为生成动态连接库,后续程序接口调用需要的引用这些动态库。 --prefix=/usr/local/openssl 为配置opens..

啊渊的专栏 2600

openssl

title: ECDSA加密算法 date: 2021-12-31 16:42:15 categories: tags: - openssl - c/c++ 1、ECDSA简述 ECDSA的全名是Elliptic Curve DSA,即椭圆曲线DSA。它是Digital Signature Algorithm (DSA)应用了椭圆曲线加密算法的变种。椭圆曲线算法的原理很复杂,但是具有很好的公开密钥算法特性,通过公钥无法逆向获得私钥。 ECDSA算法用于数字签名,是ECC与DSA的结合,整个签名过程与DSA

qq_41987680的博客 1148

DSA数字签名与SHA摘要:openssl密钥生成到验签实战

数字签名与消息摘要是保障数据完整性和身份认证的核心技术。DSA作为基于离散对数难题的数字签名算法,与SHA系列哈希函数配合,可确保报文在传输过程中未被篡改且来源可信。SHA-256的单向散列特性使其不存在“解密”操作,而openssl工具提供了标准的密钥生成、签名与验签能力。从DSA参数结构、密钥对生成、openssl dgst命令实践出发,厘清DER与raw签名格式差异,规避随机数复用、摘要长度匹配等工程陷阱,有助于开发者在老系统改造、Web服务防篡改及自有验签链路等场景中正确落地,避免“在线加密”类误读

weixin_33646989的博客 218

OpenSSL ECDSA签名验证实战:密钥生成、DER编码与避坑指南

数字签名是保障数据传输完整性、身份真实性与防抵赖的核心技术,在API安全、软件授权、固件防篡改等领域广泛应用。ECDSA(椭圆曲线数字签名算法)基于椭圆曲线离散对数难题,相比RSA具有密钥更短、运算效率高的优势,尤其适合资源受限的物联网与嵌入式场景。在工程实践中,使用OpenSSL库实现ECDSA签名与验证已成为行业标准做法,其EVP接口封装了密钥生成、摘要计算、签名与验签的完整流程,并支持跨平台互操作。然而,开发者常因不了解DER编码格式、随机数k复用风险或跨语言签名格式不一致而踩坑。本文基于OpenSS

weixin_42706667的博客 248

openssl基本原理 + 生成证书 + 使用实例

公司一个项目要进行交易数据传输,因为这个项目银行那边也是刚刚开始启动,所有的支持只有一个传输字段的说明文档,好吧,总的有人做事不是嘛,于是接口开发正式展开,第一步的难点就是加密解密,我选择使用OpenSSL. OpenSSL初接触的人恐怕最难的在于先理解各种概念   公钥/私钥/签名/验证签名/加密/解密/非对称加密   我们一般的加密是用一个密码加密文件,然后解密也用同样的密码.这很好理解,这个是对称加密.而有些加密时,加密用的一个密码,而解密用另外一组密码,这个叫非对称加密,意思就是加密解密的密码

huang714的专栏 3678

实现数字签名算法(DSA),Hash算法的实现C语言

1)利用C\C++语言实现DSA算法。 2)DSA中的Hash函数采用SHA算法。 (1)消息填充:因为我们存储的时候是以字节为单位存储的,所以消息的长度(单位:位)一定是 8 的倍数。而我们填充的时候也一定是 8 位、8 位地来填充。也即不可能只填充一个二进制位,至少是 8 个二进制位(一个字节)。因此最少填充 1 个字节,最多填充 64 个字节(64*8=512)。 在SHA1中,为了HASH小于2^64长度的输入消息,先对消息m的长度进行处理,判断补0后是512位的多少倍。 (2)大整数:因为涉及到几百位的大整数运算,如这里规定p是512位,先封装一个大整数类BigNumber,BigNumber的成员变量有sign, length, uint32_t number[MAXLENGTH], MAXLENGTH规定为128,也就是说这个BigNumber最多可以由128个uint32_t型的数拼起来。

openssl 生成证书及生成过程

1. 基本原理 公司一个项目要进行交易数据传输,因为这个项目银行那边也是刚刚开始启动,所有的支持只有一个传输字段的说明文档,好吧,总的有人做事不是嘛,于是接口开发正式展开,第一步的难点就是加密解密,我选择使用OpenSSL. OpenSSL初接触的人恐怕最难的在于先理解各种概念 公钥/私钥/签名/验证签名/加密/解密/非对称加密 我们一般的加密是用一个密码加密文件,然后解密也用同样的密码....

nsaygl的专栏 5155

DSA 数字签名 /C++/源代码

DSA 数字签名 本程序实现DSA数字签名 是学习和研究密码学的良好资源 DSA 数字签名 本程序实现DSA数字签名 是学习和研究密码学的良好资源

基于ResNet-50的猫狗分类训练

基于ResNet-50的猫狗分类python训练

weixin_46352497的博客 1004

数字签名技术以及RSA算法的原理实现

数字签名技术 1)对称加密与非对称加密 对称加密:对文件的加密和解密采用的都是同一个密钥,有IDEA和DES两种加密算法 非对称加密:有一对公钥和私钥 如果我们使用公钥加密,必须得用私钥解密;如果使用私钥加密,则必须使用公钥解密。 当使用的加密和解密式两种不同的密钥 我们称之为非对称加密 2)信息摘要: 对数据进行处理,得到一段固定长度的结果。 一般在进行数字签名的时候我...

羊の博客 8367

一人公司 CEO × Accio Work:最大能效实战指南

作为一人公司 CEO,你需要同时扮演市场、销售、运营、财务、产品等多个角色。Accio Work 本质上就是你的。

华盛AI的博客 2103

OpenSSL 4.x ML-DSA 后量子签名实现详解:FIPS 204 参数表、密钥内存布局、签名 API 与常量时间设计

本文基于 OpenSSL 仓库的设计文档 [doc/designs/ml-dsa.md](https://link.gitcode.com/i/b170731bc070dcaa0c945ededb433a18) 展开,结合当前仓库(VERSION.dat 显示为 4.1.0-dev)的实际源码,完整剖析 OpenSSL 对 FIPS 204 模块-格签名算法 ML-DSA-44/65/87 的实现

gitblog_00129的博客 15

从零到一构建一个智能爬虫 Agent 完整教程

本文将打造一个具备自主决策、动态解析、反反爬、持久化能力的智能爬虫 Agent

数据知道的博客 4798

基于matlab实现的水果识别系统设计

本项目针对多种常见水果混合的图像,利用 Matlab 软件,对水果的识别进行研究。根据水果和背景的差别选取阈值,对去噪增强对比度后的图像进行二值化处理。再对图像进行边缘检测,选定连通区域,标记后再对不同种水果的颜色,形状,大小等特征进行快速识别水果,从而实现对水果的正确分拣。:水果识别、MATLAB、数字图像处理。

热情、奔放、快乐编程! 4862
上一篇: C++在windows下获得运行主机的硬件信息:CPU序列号、MAC地址、硬盘序列号、主板序列号
下一篇: Ubuntu 16.04安装nvidia显卡驱动
Leo-Ma
博客等级 码龄11年 1701粉丝 124原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值