C++与Java之RSA签名与验签

5 篇文章 0 订阅

本文转载自博客:http://blog.csdn.net/richerg85/article/details/51723124

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

[java]  view plain  copy
 print ?
  1. <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端:

     

[java]  view plain  copy
 print ?
  1. /** 
  2.      * 用私钥对信息生成数字签名 
  3.      *  
  4.      * @param data 
  5.      *            加密数据 
  6.      * @param privateKey 
  7.      *            私钥 
  8.      *  
  9.      * @return 
  10.      * @throws Exception 
  11.      */  
  12.     public static String sign(byte[] data, String privateKey) throws Exception {  
  13.         // 解密由base64编码的私钥  
  14.         byte[] keyBytes = decryptBASE64(privateKey);  
  15.   
  16.         // 构造PKCS8EncodedKeySpec对象  
  17.         PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);  
  18.   
  19.         // KEY_ALGORITHM 指定的加密算法  
  20.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  21.   
  22.         // 取私钥匙对象  
  23.         PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);  
  24.   
  25.         // 用私钥对信息生成数字签名  
  26.         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
  27.         signature.initSign(priKey);  
  28.         signature.update(data);  
  29.   
  30.         return encryptBASE64(signature.sign());  
  31.     }  
  32.   
  33.     /** 
  34.      * 校验数字签名 
  35.      *  
  36.      * @param data 
  37.      *            加密数据 
  38.      * @param publicKey 
  39.      *            公钥 
  40.      * @param sign 
  41.      *            数字签名 
  42.      *  
  43.      * @return 校验成功返回true 失败返回false 
  44.      * @throws Exception 
  45.      *  
  46.      */  
  47.     public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {  
  48.   
  49.         // 解密由base64编码的公钥  
  50.         byte[] keyBytes = decryptBASE64(publicKey);  
  51.   
  52.         // 构造X509EncodedKeySpec对象  
  53.         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);  
  54.   
  55.         // KEY_ALGORITHM 指定的加密算法  
  56.         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
  57.   
  58.         // 取公钥匙对象  
  59.         PublicKey pubKey = keyFactory.generatePublic(keySpec);  
  60.   
  61.         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
  62.         signature.initVerify(pubKey);  
  63.         signature.update(data);  
  64.   
  65.         // 验证签名是否正常  
  66.         return signature.verify(decryptBASE64(sign));  
  67.     }  

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

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

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

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

     

[plain]  view plain  copy
 print ?
  1. OpenSSL> genrsa -out rsa_private_key.pem   1024  #生成私钥  
  2. OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem #Java开发者需要将私钥转换成PKCS8格式  
  3. OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥  
  4. OpenSSL> exit #退出OpenSSL程序  

     JAVA签名/验证签名测试:

[java]  view plain  copy
 print ?
  1. public static void main(String[] args) throws Exception {  
  2.         // 签名生成  
  3.         String content = "appId=23232323&&testestesjfijfe12";  
  4.           
  5.         String privateKey = "mbyKNDk+fpYHGZ8WMzGJjA6wWsFcWDxOtdIP4BR7W00Shvau/QJBALQxheLcK9s3CfnD+RtQK9MxKbk/oe0Pjf+UvmufUJOWzGNzThuwNA70EThKb0VBNMaXbeHxVicU0QquTdKQkH0CQG/VwLy00QjqwLv6oqZ+i6XpsSoCTlwe25Yp/pjsUrpq5+DnZ9mkw2s2WUi2sdwOpUogctQ5XlBbdjOLpoLhVjM=";  
  6.         String sign = sign(content.getBytes(), privateKey);  
  7.         String lastSign = URLEncoder.encode(sign.replace("\n"""), "UTF-8");  
  8.         System.out.println("签名内容:" + content);  
  9.         System.out.println("最终签名:" + lastSign);  
  10.   
  11.         // 签名验证  
  12.         String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA";  
  13.         boolean bverify = verify(content.getBytes(), publicKey, URLDecoder.decode(xiaoySign, "UTF-8"));  
  14.           
  15.         System.out.println("验证结果:" + bverify +";decode sign="+URLDecoder.decode(xiaoySign, "UTF-8"));  
  16.   
  17.     }  

     c++实现,直接代码:

    工具类中,需要工具:

   

[cpp]  view plain  copy
 print ?
  1.        static std::string url_encode(const std::string& szToEncode);  
  2. static std::string url_decode(const std::string& szToDecode);  
  3. static bool verify_rsa(RSA *rsa ,const std::string &content, const std::string &sign);  
实现:

[cpp]  view plain  copy
 print ?
  1. std::string common_tool::url_encode(const std::string& szToEncode)  
  2. {  
  3.     std::string src = szToEncode;  
  4.     char hex[] = "0123456789ABCDEF";  
  5.     std::string dst;  
  6.   
  7.     for (size_t i = 0; i < src.size(); ++i)  
  8.     {  
  9.         unsigned char cc = src[i];  
  10.         if (isascii(cc))  
  11.         {  
  12.             if (cc == ' ')  
  13.             {  
  14.                 dst += "%20";  
  15.             }  
  16.             else  
  17.                 dst += cc;  
  18.         }  
  19.         else  
  20.         {  
  21.             unsigned char c = static_cast<unsigned char>(src[i]);  
  22.             dst += '%';  
  23.             dst += hex[c / 16];  
  24.             dst += hex[c % 16];  
  25.         }  
  26.     }  
  27.     return dst;  
  28. }  
  29.   
  30. std::string common_tool::url_decode(const std::string &SRC) {  
  31.     std::string ret;  
  32.     char ch;  
  33.     int i, ii;  
  34.     for (i=0; i<SRC.length(); i++) {  
  35.             if (int(SRC[i])==37) {  
  36.                     sscanf(SRC.substr(i+1,2).c_str(), "%x", &ii);  
  37.                     ch=static_cast<char>(ii);  
  38.                     ret+=ch;  
  39.                     i=i+2;  
  40.             } else {  
  41.                     ret+=SRC[i];  
  42.             }  
  43.     }  
  44.     return (ret);  
  45. }  
  46. bool common_tool::verify_rsa(/*const char *public_key*/RSA *rsa ,  
  47.                         const std::string &content, const std::string &sign) {  
  48.     BIO *bufio = NULL;  
  49.     EVP_PKEY *evpKey = NULL;  
  50.     bool verify = false;  
  51.     EVP_MD_CTX ctx;  
  52.     int result = 0;  
  53.     std::string decodedSign = common_tool::base64_decode(sign);  
  54.     char *chDecodedSign = const_cast<char*>(decodedSign.c_str());  
  55.   
  56.     if (rsa == NULL) {  
  57.             printf("PEM_read_bio_RSA_PUBKEY failed");  
  58.             goto safe_exit;  
  59.     }  
  60.   
  61.     evpKey = EVP_PKEY_new();  
  62.     if (evpKey == NULL) {  
  63.             printf("EVP_PKEY_new failed");  
  64.             goto safe_exit;  
  65.     }  
  66.   
  67.     if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {  
  68.             printf("EVP_PKEY_set1_RSA failed");  
  69.             goto safe_exit;  
  70.     }  
  71.   
  72.     EVP_MD_CTX_init(&ctx);  
  73.   
  74.     if (result == 1 && (result = EVP_VerifyInit_ex(&ctx,  
  75.                                     EVP_md5(), NULL)) != 1) {  
  76.             printf("EVP_VerifyInit_ex failed");  
  77.     }  
  78.   
  79.     if (result == 1 && (result = EVP_VerifyUpdate(&ctx,  
  80.                                     content.c_str(), content.size())) != 1) {  
  81.             printf("EVP_VerifyUpdate failed");  
  82.     }  
  83.   
  84.     if (result == 1 && (result = EVP_VerifyFinal(&ctx,  
  85.                                     (unsigned char*)chDecodedSign,  
  86.                                     decodedSign.size(), evpKey)) != 1) {  
  87.             printf("EVP_VerifyFinal failed");  
  88.     }  
  89.     if (result == 1) {  
  90.             verify = true;  
  91.     } else {  
  92.             printf("verify failed");  
  93.     }  
  94.   
  95.     EVP_MD_CTX_cleanup(&ctx);  
  96.   
  97.     safe_exit:  
  98.     if (rsa != NULL) {  
  99.             RSA_free(rsa);  
  100.             rsa = NULL;  
  101.     }  
  102.   
  103.     if (evpKey != NULL) {  
  104.             EVP_PKEY_free(evpKey);  
  105.             evpKey = NULL;  
  106.     }  
  107.   
  108.     if (bufio != NULL) {  
  109.             BIO_free_all(bufio);  
  110.             bufio = NULL;  
  111.     }  
  112.   
  113.     return verify;  
  114. }  

其中,

[cpp]  view plain  copy
 print ?
  1. verify_rsa 是整个验证的核心。  

参数准备:

[cpp]  view plain  copy
 print ?
  1. RSA *rsa  

RSA

[cpp]  view plain  copy
 print ?
  1. BIO *key = NULL;   
  2.       RSA *r = NULL;   
  3.       key = BIO_new(BIO_s_file());   
  4.       BIO_read_filename(key, "rsa_public_key.pem");   
  5.       r = PEM_read_bio_RSAPublicKey(key, NULL, NULL, NULL);   
  6.       BIO_free_all(key);  

content为加密的字符串

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

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


时间有限,不多写了。

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

[cpp]  view plain  copy
 print ?
  1. static std::string sign(const char *private_key,   
  2.             const std::string &content) {  
  3.         BIO *bufio = NULL;  
  4.         RSA *rsa = NULL;  
  5.         EVP_PKEY *evpKey = NULL;  
  6.         bool verify = false;  
  7.         EVP_MD_CTX ctx;  
  8.         int result = 0;  
  9.         unsigned int size = 0;  
  10.         char *sign = NULL;  
  11.         std::string signStr = "";  
  12.   
  13.         //bufio = BIO_new_mem_buf((void*)private_key, -1);  
  14.         //if (bufio == NULL) {  
  15.         //  ERR("BIO_new_mem_buf failed");  
  16.         //  goto safe_exit;  
  17.         //}  
  18.         bufio = BIO_new(BIO_s_file());  
  19.                 BIO_read_filename(bufio, "rsa_private_key_pkcs8.pem");  
  20.                 //BIO_read_filename(bufio, "rsa_private_key.pem");  
  21.   
  22.         rsa = PEM_read_bio_RSAPrivateKey(bufio, NULL, NULL, NULL);  
  23.         if (rsa == NULL) {  
  24.             ERR("PEM_read_bio_RSAPrivateKey failed");  
  25.             goto safe_exit;  
  26.         }  
  27.   
  28.         evpKey = EVP_PKEY_new();  
  29.         if (evpKey == NULL) {  
  30.             ERR("EVP_PKEY_new failed");  
  31.             goto safe_exit;  
  32.         }  
  33.   
  34.         if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {  
  35.             ERR("EVP_PKEY_set1_RSA failed");  
  36.             goto safe_exit;  
  37.         }  
  38.   
  39.         EVP_MD_CTX_init(&ctx);  
  40.   
  41.         if (result == 1 && (result = EVP_SignInit_ex(&ctx,   
  42.                         EVP_md5(), NULL)) != 1) {  
  43.             ERR("EVP_SignInit_ex failed");  
  44.         }  
  45.   
  46.         if (result == 1 && (result = EVP_SignUpdate(&ctx,   
  47.                         content.c_str(), content.size())) != 1) {  
  48.             ERR("EVP_SignUpdate failed");  
  49.         }  
  50.   
  51.         size = EVP_PKEY_size(evpKey);  
  52.         sign = (char*)malloc(size+1);  
  53.         memset(sign, 0, size+1);  
  54.           
  55.         if (result == 1 && (result = EVP_SignFinal(&ctx,   
  56.                         (unsigned char*)sign,  
  57.                         &size, evpKey)) != 1) {  
  58.             ERR("EVP_SignFinal failed");  
  59.         }  
  60.   
  61.         if (result == 1) {  
  62.             verify = true;  
  63.         } else {  
  64.             ERR("verify failed");  
  65.         }  
  66.   
  67.         signStr = common_tool::base64_encode((const unsigned char*)sign, size);  
  68.         EVP_MD_CTX_cleanup(&ctx);  
  69.         free(sign);  
  70.   
  71. safe_exit:  
  72.         if (rsa != NULL) {  
  73.             RSA_free(rsa);  
  74.             rsa = NULL;  
  75.         }  
  76.   
  77.         if (evpKey != NULL) {  
  78.             EVP_PKEY_free(evpKey);  
  79.             evpKey = NULL;  
  80.         }  
  81.   
  82.         if (bufio != NULL) {  
  83.             BIO_free_all(bufio);  
  84.             bufio = NULL;  
  85.         }  
  86.   
  87.         return signStr;  
  88.         //return sign;  
  89.     }  

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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值