添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">      最近一段时间一直被一个事情困扰:支付相关RSA签名与验证签名,服务器使用java,客户端是c++的程序,在C++端验证签名的时候,试用了很多方法都无法签名通过。在java中,签名和验证签名很容易调用现有的类实现,但是在c++中却是不太容易。</span>

采用openssl原生的c++程序,不行;

在网上搜索了很久,也翻墙google了,试用了很多,也不行;

用了其他网友借鉴PHP的代码的,也不行;

所有RSA流程总规则: 私钥签名,公钥验签

最后,在参考了一个貌似支付宝验证的alipay.h的文件后,今天终于通过验证签名,现把代码贴出,以便以后再有用,也对遇到同等类型的问题的朋友有个参考。

JAVA端:

* 用私钥对信息生成数字签名 * @param data * 加密数据 * @param privateKey * 私钥 * @return * @throws Exception public static String sign(byte[] data, String privateKey) throws Exception { // 解密由base64编码的私钥 byte[] keyBytes = decryptBASE64(privateKey); // 构造PKCS8EncodedKeySpec对象 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取私钥匙对象 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); // 用私钥对信息生成数字签名 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(priKey); signature.update(data); return encryptBASE64(signature.sign()); * 校验数字签名 * @param data * 加密数据 * @param publicKey * 公钥 * @param sign * 数字签名 * @return 校验成功返回true 失败返回false * @throws Exception public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { // 解密由base64编码的公钥 byte[] keyBytes = decryptBASE64(publicKey); // 构造X509EncodedKeySpec对象 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取公钥匙对象 PublicKey pubKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(pubKey); signature.update(data); // 验证签名是否正常 return signature.verify(decryptBASE64(sign));

看着很简单吧(不纠结于RSA实现过程)

PHP端也很好找,不在贴部分代码,网上很多;

通过上面JAVA代码通过私钥签名的字符串,如何通过c++验证签名的正确呢?

先把公私钥的openssl生成过程,给贴出来(我是linux服务器测试的,终端中输入openssl):

OpenSSL> genrsa -out rsa_private_key.pem   1024  #生成私钥
OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem #Java开发者需要将私钥转换成PKCS8格式
OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥
OpenSSL> exit #退出OpenSSL程序

JAVA签名/验证签名测试:

public static void main(String[] args) throws Exception {
		// 签名生成
		String content = "appId=23232323&&testestesjfijfe12";
		String privateKey = "mbyKNDk+fpYHGZ8WMzGJjA6wWsFcWDxOtdIP4BR7W00Shvau/QJBALQxheLcK9s3CfnD+RtQK9MxKbk/oe0Pjf+UvmufUJOWzGNzThuwNA70EThKb0VBNMaXbeHxVicU0QquTdKQkH0CQG/VwLy00QjqwLv6oqZ+i6XpsSoCTlwe25Yp/pjsUrpq5+DnZ9mkw2s2WUi2sdwOpUogctQ5XlBbdjOLpoLhVjM=";
		String sign = sign(content.getBytes(), privateKey);
		String lastSign = URLEncoder.encode(sign.replace("\n", ""), "UTF-8");
		System.out.println("签名内容:" + content);
		System.out.println("最终签名:" + lastSign);
		// 签名验证
		String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA";
		boolean bverify = verify(content.getBytes(), publicKey, URLDecoder.decode(xiaoySign, "UTF-8"));
		System.out.println("验证结果:" + bverify +";decode sign="+URLDecoder.decode(xiaoySign, "UTF-8"));

     c++实现,直接代码:

    工具类中,需要工具:

        static std::string url_encode(const std::string& szToEncode);
	static std::string url_decode(const std::string& szToDecode);
	static bool verify_rsa(RSA *rsa ,const std::string &content, const std::string &sign);
实现:
std::string common_tool::url_encode(const std::string& szToEncode)
    std::string src = szToEncode;
    char hex[] = "0123456789ABCDEF";
    std::string dst;
    for (size_t i = 0; i < src.size(); ++i)
        unsigned char cc = src[i];
        if (isascii(cc))
            if (cc == ' ')
                dst += "%20";
                dst += cc;
            unsigned char c = static_cast<unsigned char>(src[i]);
            dst += '%';
            dst += hex[c / 16];
            dst += hex[c % 16];
    return dst;
std::string common_tool::url_decode(const std::string &SRC) {
	std::string ret;
	char ch;
	int i, ii;
	for (i=0; i<SRC.length(); i++) {
			if (int(SRC[i])==37) {
					sscanf(SRC.substr(i+1,2).c_str(), "%x", &ii);
					ch=static_cast<char>(ii);
					ret+=ch;
					i=i+2;
			} else {
					ret+=SRC[i];
	return (ret);
bool common_tool::verify_rsa(/*const char *public_key*/RSA *rsa ,
                        const std::string &content, const std::string &sign) {
	BIO *bufio = NULL;
	EVP_PKEY *evpKey = NULL;
	bool verify = false;
	EVP_MD_CTX ctx;
	int result = 0;
	std::string decodedSign = common_tool::base64_decode(sign);
	char *chDecodedSign = const_cast<char*>(decodedSign.c_str());
	if (rsa == NULL) {
			printf("PEM_read_bio_RSA_PUBKEY failed");
			goto safe_exit;
	evpKey = EVP_PKEY_new();
	if (evpKey == NULL) {
			printf("EVP_PKEY_new failed");
			goto safe_exit;
	if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {
			printf("EVP_PKEY_set1_RSA failed");
			goto safe_exit;
	EVP_MD_CTX_init(&ctx);
	if (result == 1 && (result = EVP_VerifyInit_ex(&ctx,
									EVP_md5(), NULL)) != 1) {
			printf("EVP_VerifyInit_ex failed");
	if (result == 1 && (result = EVP_VerifyUpdate(&ctx,
									content.c_str(), content.size())) != 1) {
			printf("EVP_VerifyUpdate failed");
	if (result == 1 && (result = EVP_VerifyFinal(&ctx,
									(unsigned char*)chDecodedSign,
									decodedSign.size(), evpKey)) != 1) {
			printf("EVP_VerifyFinal failed");
	if (result == 1) {
			verify = true;
	} else {
			printf("verify failed");
	EVP_MD_CTX_cleanup(&ctx);
	safe_exit:
	if (rsa != NULL) {
			RSA_free(rsa);
			rsa = NULL;
	if (evpKey != NULL) {
			EVP_PKEY_free(evpKey);
			evpKey = NULL;
	if (bufio != NULL) {
			BIO_free_all(bufio);
			bufio = NULL;
	return verify;
verify_rsa 是整个验证的核心。

参数准备:

RSA *rsa
  BIO *key = NULL; 
        RSA *r = NULL; 
        key = BIO_new(BIO_s_file()); 
        BIO_read_filename(key, "rsa_public_key.pem"); 
        r = PEM_read_bio_RSAPublicKey(key, NULL, NULL, NULL); 
        BIO_free_all(key);

content为加密的字符串

sign为java生成的签名,java中这个签名urlencode了,传入之前,需要先urldecode一下。

在此函数中,sign还需要base64 decode一下,这样就没问题了。

时间有限,不多写了。

把c++签名过程再贴一下:

static std::string sign(const char *private_key, 
			const std::string &content) {
		BIO *bufio = NULL;
		RSA *rsa = NULL;
		EVP_PKEY *evpKey = NULL;
		bool verify = false;
		EVP_MD_CTX ctx;
		int result = 0;
		unsigned int size = 0;
		char *sign = NULL;
		std::string signStr = "";
		//bufio = BIO_new_mem_buf((void*)private_key, -1);
		//if (bufio == NULL) {
		//	ERR("BIO_new_mem_buf failed");
		//	goto safe_exit;
		bufio = BIO_new(BIO_s_file());
                BIO_read_filename(bufio, "rsa_private_key_pkcs8.pem");
                //BIO_read_filename(bufio, "rsa_private_key.pem");
		rsa = PEM_read_bio_RSAPrivateKey(bufio, NULL, NULL, NULL);
		if (rsa == NULL) {
			ERR("PEM_read_bio_RSAPrivateKey failed");
			goto safe_exit;
		evpKey = EVP_PKEY_new();
		if (evpKey == NULL) {
			ERR("EVP_PKEY_new failed");
			goto safe_exit;
		if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {
			ERR("EVP_PKEY_set1_RSA failed");
			goto safe_exit;
		EVP_MD_CTX_init(&ctx);
		if (result == 1 && (result = EVP_SignInit_ex(&ctx, 
						EVP_md5(), NULL)) != 1) {
			ERR("EVP_SignInit_ex failed");
		if (result == 1 && (result = EVP_SignUpdate(&ctx, 
						content.c_str(), content.size())) != 1) {
			ERR("EVP_SignUpdate failed");
		size = EVP_PKEY_size(evpKey);
		sign = (char*)malloc(size+1);
		memset(sign, 0, size+1);
		if (result == 1 && (result = EVP_SignFinal(&ctx, 
						(unsigned char*)sign,
						&size, evpKey)) != 1) {
			ERR("EVP_SignFinal failed");
		if (result == 1) {
			verify = true;
		} else {
			ERR("verify failed");
		signStr = common_tool::base64_encode((const unsigned char*)sign, size);
		EVP_MD_CTX_cleanup(&ctx);
		free(sign);
safe_exit:
		if (rsa != NULL) {
			RSA_free(rsa);
			rsa = NULL;
		if (evpKey != NULL) {
			EVP_PKEY_free(evpKey);
			evpKey = NULL;
		if (bufio != NULL) {
			BIO_free_all(bufio);
			bufio = NULL;
		return signStr;
		//return sign;

sign的过程,如果需要和verify一样,需要改一下传入参数。 https://wiki.openssl.org/index.php/EVP_Signing_and_Verifying http://www.codeguru.com/cpp/cpp/algorithms/strings/article.php/c12759/URI-Encoding-and-Decoding.htm http://blog.csdn.net/lazyclough/article/details/7646696 最近一段时间一直被一个事情困扰:支付相关RSA签名与验证签名,服务器使用java,客户端是c++的程序,在C++端验证签名的时候,试用了很多方法都无法签名通过。在java中,签名和验证签名很容易调用现有的类实现,但是在c++中却是不太容易。     采用openssl原生的c++程序,不行;     在网上搜索了很久,也翻墙google了,试用了很多,也不行;     用了其他网友借鉴 // v_pwszFilePath --- 程序的全路径 // v_pwszSign --- 用于返回数字签名的缓冲区,如果为NULL, // 那么将会需要的缓冲区大小
一、RSA是一种非对称加密算法,一般在数据加密的过程中会使用公钥加密,私钥解密,在签名生成和验证过程中会使用私钥加密,公钥解密。 二、使用openssl生成公钥和私钥 1、生成私钥,保存在文件rsa_private_key.pem里面 openssl genrsa -out rsa_private_key.pem 1024 2、通过私钥生成公钥,保存在文件rsa_private_key.p...
RSA是使用RSA公钥对数字签名进行验证的过程。在Go语言中,可以使用标准库"crypto/rsa"中的VerifyPKCS1v15函数来实现RSA。使用方法如下: 1. 使用x509标准库解析公钥文件得到公钥结构体 2. 使用sha256.New()得到hash.Hash接口 3. 使用hash.Write(originalData)对需要验的数据进行hash 4. 使用rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hashed, signData)来验,其中pubKey为公钥结构体,hashed为第3步得到的hash值,signData为签名数据 示例代码: package main import ( "crypto" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" "fmt" "io/ioutil" func main() { // 读取公钥文件 pubKeyData, _ := ioutil.ReadFile("public.pem") block, _ := pem.Decode(pubKeyData) pubKey, _ := x509.ParsePKIXPublicKey(block.Bytes) // 读取签名数据 signData, _ := ioutil.ReadFile("sign.txt") signData, _ = base64.StdEncoding.DecodeString(string(signData)) // 读取需要验的数据 originalData, _ := ioutil.ReadFile("data.txt") // 计算hash值 hasher := sha256.New() hasher.Write(originalData) hashed := hasher.Sum(nil) // 验 err := rsa.VerifyPKCS1v15(pubKey.(*rsa.PublicKey), crypto.SHA256, hashed, signData) if err != nil { fmt.Println("验失败") } else { fmt.Println("验成功") 上面代码展示