您的位置:首页 > 移动开发 > Android开发

ANDROID接入华为SDK遇到的SHA256withRSA的问题解决

2016-12-27 11:21 405 查看
华为的SDK在登录时原来的实现思路是:

1、C->SDK-C

2、SDK-C->C

3、C->S

4、S->SDK-S

5、SDK-S->S

6、S->C

新版本中,把步骤4和步骤5省了,官方原文:

游戏可以将gameAuthSign、 playerId、 appId和ts提交到游戏自己的服务器,使用文档附
录一中的公钥进行验签,验签方式如下:
a. 将 appId、 ts 和 playerId 的值进行连接;
b. 将 a 的值使用 SHA256 计算 HASH;
c. 将 b 的值使用公钥采用 RSA 算法进行验签,公钥值请参考本文档的“ 7.1 登录鉴权
签名的验签公钥”。
官方java核心代码:

public static boolean verify(byte[] data, String publicKey, String sign)
throws Exception
{
byte[] keyBytes = Base64Util.decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicK = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicK);
signature.update(data);
return signature.verify(Base64Util.decode(sign));
}
官方php核心代码:(参考http://club.huawei.com/thread-5443470-1-1-2851.html)
$pubKey = @file_get_contents($filename);
$openssl_public_key = @openssl_get_publickey($pubKey);

$ok = @openssl_verify($content, base64_decode($sign), $openssl_public_key, OPENSSL_ALGO_SHA256);
@openssl_free_key($openssl_public_key);

if($ok)
{
$result = "0";// success
}
else
{
$result = "1";// failure
}
当时为了这个SHA256+RSA弄的焦头烂额,写了JAVA的测试用例,确定算法正确,可是这8行JAVA代码看不完全懂,逆出来的JAVART的代码又十分难看。写了PHP的测试用例,确定算法正确。于是乎我钻了牛角尖,一定要用C++实现出来。找代码,尝试OPENSSL,尝试所有的参数,尝试其他数学库......于是乎浪费了两天时间。在第二天快结束的时候,我终于放弃了,直接用PHP吧,C++给PHP发个消息不是什么难事。于是乎2个小时不到问题解决。(用的PHP) 

后来华为接完了,接酷派的时候,拿到一个旧版本的酷派的文档,对比了一下PHP与华为的PHP,十分类似,而酷派居然提供了c++的samples!太良心了!下次我手机坏了一定要买COOLPAD!顺便吐槽一下华为的技术,问什么问题他们都会回:你们是JAVA吗?你们是PHP吗?(前面已经说过一百遍了他们还会继续问)。

酷派的旧版本sample的c++代码(我简化了一下):

#ifndef CRYPTOHELPER_H_
#define CRYPTOHELPER_H_

#include <openssl/md5.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <iostream>
#include <stdint.h>
#include <string.h>

namespace UTILS
{

enum EkeyType
{
kPubKey = 0,
kPrivateKey = 1,
};

class CryptHelper
{
private:
CryptHelper(){}
~CryptHelper(){}
public:

static EVP_PKEY* getKeyByPKCS1(const std::string &key, const int32_t keyType, std::string& strError)
{
RSA* rsa = getRsaKey(key, keyType);
if (!rsa)
{
strError = "getRsaKey failed ! ";
return NULL;
}
EVP_PKEY* pkey = EVP_PKEY_new();
if (1 != EVP_PKEY_assign_RSA(pkey, rsa))
{
strError = "EVP_PKEY_assign_RSA failed !";
RSA_free(rsa);
EVP_PKEY_free(pkey);
return NULL;
}
return pkey;
}

static RSA* getRsaKey(const std::string &key, const int32_t keyType, std::string& strError)
{
uint8_t *keyBuf;
uint8_t *p;

keyBuf = (uint8_t *)alloca(key.length());

size_t keyLen(0);
keyLen = base64Decode(keyBuf, (const uint8_t *)key.c_str(), key.length(), strError);
if (0 > keyLen)
{
strError = "base64Decode key failed !";
return NULL;
}

//d2i_RSA_PUBKEY
p = keyBuf;
RSA *rsa =
(keyType == 0) ?
d2i_RSA_PUBKEY(NULL, (const uint8_t **)&p, keyLen, strError) :
d2i_RSAPrivateKey(NULL, (const uint8_t **)&p, keyLen, strError);
return rsa;
}

static void freeKey(RSA* key) { if (key) RSA_free(key); }

static void freeKey(EVP_PKEY* key){ if (key) EVP_PKEY_free(key); }

static int32_t signWithRsa(const std::string &data, const EVP_MD *type, EVP_PKEY* key, std::string &sign, std::string strError)
{
EVP_MD_CTX mdCtx;
EVP_SignInit(&mdCtx, type);
EVP_SignUpdate(&mdCtx, data.c_str(), data.length());

uint32_t signLen(EVP_PKEY_size(priKey)), outLen(0);
uint8_t* signBuf = (uint8_t *)OPENSSL_malloc(signLen);
uint8_t* outBuf = (uint8_t *)OPENSSL_malloc(signLen * 2);

int32_t ret = EVP_SignFinal(&mdCtx, signBuf, &signLen, priKey);
if (1 != ret)
{
strError = "EVP_SignFinal failed ! ";
}
else
{
if (0 > (outLen = EVP_EncodeBlock(outBuf, signBuf, signLen)))
{
ret = -1;
strError = "EVP_EncodeBlock failed! ";
}
else
{
sign.assign((char*)outBuf, outLen);
}
}

OPENSSL_free(signBuf);
OPENSSL_free(outBuf);

return 1 == ret ? 0 : -1;
}

static int32_t verifySignWithRsa(const std::string &data, const std::string &sign, const EVP_MD *type, EVP_PKEY* key, std::string& strError)
{
EVP_MD_CTX mdCtx;
uint8_t* signSrc = (uint8_t *)OPENSSL_malloc(sign.length());
int32_t signSrcLen = base64Decode(signSrc, (const uint8_t *)sign.c_str(), sign.length(), strError);
if (0 > signSrcLen)
{
strError = "sign base64Decode failed";
OPENSSL_free(signSrc);
return -1;
}

EVP_VerifyInit(&mdCtx, type);
EVP_VerifyUpdate(&mdCtx, data.c_str(), data.length());
int32_t ret = EVP_VerifyFinal(&mdCtx, signSrc, signSrcLen, pubKey);

OPENSSL_free(signSrc);
return 1 == ret ? 0 : -2;
}

static int32_t md5WithRsa(const std::string &data, std::string &sign, EVP_PKEY* priKey, std::string& strError)
{
return signWithRsa(data, EVP_md5(), priKey, sign, strError);
}

static int32_t verifyMd5WithRsa(const std::string &data, const std::string &sign, EVP_PKEY* pubKey, , std::string& strError)
{
return verifySignWithRsa(data, sign, EVP_md5(), pubKey, strError);
}

static int32_t base64Encode(uint8_t *out, const uint8_t *in, int32_t inl, , std::string& strError)
{
int32_t outl(0);

outl = EVP_EncodeBlock(out, in, inl);
if (0 > outl)
{
strError = "EVP_EncodeBlock failed\n";
return -1;
}
return outl;
}

static int32_t base64Decode(uint8_t *out, const uint8_t *in, int32_t inl, , std::string& strError)
{
int32_t outl(0), ret(0);

if ('=' == in[inl - 1])
{
ret++;
}
if ('=' == in[inl - 2])
{
ret++;
}
outl = EVP_DecodeBlock(out, in, inl);
if (0 > outl)
{
strError = "EVP_DecodeBlock failed\n";
return -1;
}
out[outl - ret] = '\0';
return outl - ret;
}

};

}  //end namespace UTILS

#endif /* CRYPTOHELPER_H_ */
然后,爱钻牛角尖的我,拿这个算法(verifySignWithRsa)去验证了一把华为的SDK,居然验证通过了!原来华为用的根本不是SHA256,而是SHA1!真是无力吐槽!害我加班到十一点半的华为!

顺便贴上新版COOLPAD的代码我整理的下载链接(他们的技术居然把这个算法不知道是从OPENSSL还是从LIBCRYPTO还是哪个数学库中扒了出来,真是太良心了!!!下次一定要买COOLPAD手机!)  :

SHA1withRSA算法不依赖第三方库COOLPAD提供uniqs整理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐