54 CHEN

在c/c++中解决SHA1WithRSA/ras/X509的过程记录

这里记录了一种简单的办法,在遇到写c/c++找不到答案时的最简单找代码办法。

方法是:找php怎么解决,大把的答案,然后看php源代码是怎么用c实现的。

提出

我们server端在对接 play的时候,遇到了ras加密来验证参数的实现,官方也没有什么c的参考代码,java和php的网上倒是能找到一堆。

java怎么解决

java的实现和本文要说的内容无关,只是随带一列:

public static boolean verify(String publicKey, byte[] data, String sign) throws Exception {
  
          // 解密由base64编码的公钥
          byte[] keyBytes = Base64.decodeBase64(publicKey);
          System.out.println(keyBytes.length);
          // 构造X509EncodedKeySpec对象
          X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
  
          // KEY_ALGORITHM 指定的加密算法
          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  
          // 取公钥匙对象
          PublicKey pubKey = keyFactory.generatePublic(keySpec);
  
          Signature signature = Signature.getInstance("SHA1WithRSA");
          signature.initVerify(pubKey);
          signature.update(data);
  
          // 验证签名是否正常
          return signature.verify(Base64.decodeBase64(sign));
      }

php怎么解决

php的实现比较多,随便找了一个,没有验证过:

<?php
  $public_key = file_get_contents(dirname(__FILE__)./rsa_public_key.pem’);
  $pkeyid = openssl_pkey_get_public($public_key);
  $data = ‘abc’;
  $sign = ‘WkMaSsx9Fbj9/YyjoM1X0SLYvaFbsz9VmMaxc42fXxamEEIj5AfqQLrygEZRq0gkLNT4heIwOiSWEAWbfD4imaERKk07ANXEtZJ9jPJvyvg70IVvaYMKAr7bX0dJXmYw4aHnkcWR1kz27Drr6fxPmchB9WCsRmi4VfhVoF1+HRFOp28nIVReGRcbwbW1/bcMisXbitirz9Wq396vY88GUSgbgNdhFXX/kzjRBTjnG+CIhXq4HPdOWovqtPhQoxmK55+V+vxNZk9OPPHHaN3vVswk062NOs2/05yNVObL+PWeg/m43buXYalmkrwEhemdGfjIdNEoSO2D4gikvm43cg==;
  $sign = base64_decode($sign);
  if ($pkeyid) {
  $verify = openssl_verify($data, $sign, $pkeyid, OPENSSL_ALGO_MD5);
  openssl_free_key($pkeyid);
  }
  var_dump($verify);
  ?>

找到代码

看到上面的php代码,关键的几个函数有:openssl_pkey_get_public openssl_verify

然后转战最新的php源代码,https://github.com/php/php-src/blob/80f91fd9d513b83ca88345a2a8c76523e0164789/ext/openssl/openssl.c

别问为什么可以定位到这个文件的,github可以直接搜一个库里的源文件。

忽略一切以zend、zval开头的逻辑,直接找上面关键的函数。

于是就找到了关键的头文件:

/* OpenSSL includes */
  #include <openssl/evp.h>
  #include <openssl/bn.h>
  #include <openssl/rsa.h>
  #include <openssl/dsa.h>
  #include <openssl/dh.h>
  #include <openssl/x509.h>
  #include <openssl/x509v3.h>
  #include <openssl/crypto.h>
  #include <openssl/pem.h>
  #include <openssl/err.h>
  #include <openssl/conf.h>
  #include <openssl/rand.h>
  #include <openssl/ssl.h>
  #include <openssl/pkcs12.h>

关键的一行key生成代码:

key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);

关键的核心逻辑:

EVP_VerifyInit (&md_ctx, mdtype);
      EVP_VerifyUpdate (&md_ctx, data, data_len);
      err = EVP_VerifyFinal(&md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey);
      EVP_MD_CTX_cleanup(&md_ctx);
  
      if (keyresource == NULL) {
          EVP_PKEY_free(pkey);
      }

这么些核心代码都找到了,还不会抄吗?

最终解决

通过上面的过程,进行自己的组装,已经再简单不过了,为了让写c/c++的同学们快速得到 play的rsa验证代码,特别贴一下:

int verifiedByRsaPublicKey(const string& publicKey, const string& signature, char* message) {
      char *chPublicKey = const_cast<char *>(publicKey.c_str());
  
      BIO* mem_bio = NULL;
      if ((mem_bio = BIO_new_mem_buf(chPublicKey, -1)) == NULL) {        //从字符串读取RSA公钥
          BIO_free(mem_bio);
          return -3;
      }
      EVP_PKEY *publicRsa = PEM_read_bio_PUBKEY(mem_bio, NULL, NULL, NULL);
      unsigned int slen = signature.length();
  
      EVP_MD_CTX md_ctx;
      EVP_VerifyInit(&md_ctx, EVP_sha1());
      EVP_VerifyUpdate(&md_ctx, message, strlen(message));
      int err = EVP_VerifyFinal(&md_ctx, (unsigned char*) signature.data(), slen, publicRsa);
      EVP_MD_CTX_cleanup(&md_ctx);
      EVP_PKEY_free(publicRsa);
      return err;
  }

结论

通过php的解决方案去找到php的实现,是写c/c++同学们不可不学的一招。

EOF

English Version

a story about SHA1WithRSA/ras/X509 with c/c++

This is a simple solution when you do not know how to develop with c/c++.

The solution is,it will have lots of answer ho to do with php in ,and then look up the desgin in the source codes of php.

Question

We need to verify the parameters by RSA when we devlop the Play apps.There is not any reference codes with c/c++.There are so many java or php examples in the internet.

How to resolve in java

It no connection with what we are talking about.Just BTW:

public static boolean verify(String publicKey, byte[] data, String sign) throws Exception {
  
          // 解密由base64编码的公钥
          byte[] keyBytes = Base64.decodeBase64(publicKey);
          System.out.println(keyBytes.length);
          // 构造X509EncodedKeySpec对象
          X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
  
          // KEY_ALGORITHM 指定的加密算法
          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  
          // 取公钥匙对象
          PublicKey pubKey = keyFactory.generatePublic(keySpec);
  
          Signature signature = Signature.getInstance("SHA1WithRSA");
          signature.initVerify(pubKey);
          signature.update(data);
  
          // 验证签名是否正常
          return signature.verify(Base64.decodeBase64(sign));
      }

How to resolve in php

There are too many php examples.Find one without checking:

<?php
  $public_key = file_get_contents(dirname(__FILE__)./rsa_public_key.pem);
  $pkeyid = openssl_pkey_get_public($public_key);
  $data = abc;
  $sign = WkMaSsx9Fbj9/YyjoM1X0SLYvaFbsz9VmMaxc42fXxamEEIj5AfqQLrygEZRq0gkLNT4heIwOiSWEAWbfD4imaERKk07ANXEtZJ9jPJvyvg70IVvaYMKAr7bX0dJXmYw4aHnkcWR1kz27Drr6fxPmchB9WCsRmi4VfhVoF1+HRFOp28nIVReGRcbwbW1/bcMisXbitirz9Wq396vY88GUSgbgNdhFXX/kzjRBTjnG+CIhXq4HPdOWovqtPhQoxmK55+V+vxNZk9OPPHHaN3vVswk062NOs2/05yNVObL+PWeg/m43buXYalmkrwEhemdGfjIdNEoSO2D4gikvm43cg==;
  $sign = base64_decode($sign);
  if ($pkeyid) {
  $verify = openssl_verify($data, $sign, $pkeyid, OPENSSL_ALGO_MD5);
  openssl_free_key($pkeyid);
  }
  var_dump($verify);
  ?>

Find the codes

The key functions are openssl_pkey_get_public and openssl_verify in the codes above mentioned.

Go to the newest php source codes, https://github.com/php/php-src/blob/80f91fd9d513b83ca88345a2a8c76523e0164789/ext/openssl/openssl.c

Dont ask me how to find the file.You can search the file in a project at Github.

Ignore all the codes start with zval and zend.Go to the codes which include the key functions.

And then there is the key header file:

/* OpenSSL includes */
  #include <openssl/evp.h>
  #include <openssl/bn.h>
  #include <openssl/rsa.h>
  #include <openssl/dsa.h>
  #include <openssl/dh.h>
  #include <openssl/x509.h>
  #include <openssl/x509v3.h>
  #include <openssl/crypto.h>
  #include <openssl/pem.h>
  #include <openssl/err.h>
  #include <openssl/conf.h>
  #include <openssl/rand.h>
  #include <openssl/ssl.h>
  #include <openssl/pkcs12.h>

The key codes generated pkey:

key = PEM_read_bio_PrivateKey(in, NULL,NULL, passphrase);

The core logic:

EVP_VerifyInit (&md_ctx, mdtype);
      EVP_VerifyUpdate (&md_ctx, data, data_len);
      err = EVP_VerifyFinal(&md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey);
      EVP_MD_CTX_cleanup(&md_ctx);
  
      if (keyresource == NULL) {
          EVP_PKEY_free(pkey);
      }

All of the codes are ready,cant you copy?

Final

It is easy to merge the codes above mentioned.

To get the codes faster for any other people,there are the codes about play Ras verifying:

int verifiedByRsaPublicKey(const string& publicKey, const string& signature, char* message) {
      char *chPublicKey = const_cast<char *>(publicKey.c_str());
  
      BIO* mem_bio = NULL;
      if ((mem_bio = BIO_new_mem_buf(chPublicKey, -1)) == NULL) {        //从字符串读取RSA公钥
          BIO_free(mem_bio);
          return -3;
      }
      EVP_PKEY *publicRsa = PEM_read_bio_PUBKEY(mem_bio, NULL, NULL, NULL);
      unsigned int slen = signature.length();
  
      EVP_MD_CTX md_ctx;
      EVP_VerifyInit(&md_ctx, EVP_sha1());
      EVP_VerifyUpdate(&md_ctx, message, strlen(message));
      int err = EVP_VerifyFinal(&md_ctx, (unsigned char*) signature.data(), slen, publicRsa);
      EVP_MD_CTX_cleanup(&md_ctx);
      EVP_PKEY_free(publicRsa);
      return err;
  }

Conclusion

Is is a good solution for c/c++ developer.You can find the answer in php at first,go to see the c source codes of php at last.

原创文章如转载,请注明:转载自五四陈科学院[http://www.54chen.com]

Posted by 54chen c

« 过去六年在小米搞(wa)错(keng)的几个技术细节 微信支付api.mch.weixin.qq.com域名解析慢原因:ipv6 »