半年前做的功能了,由于是第一次在没有任何前辈帮忙,独立完成的功能开发,颇具纪念意义,因此写下博文以供大家参考。
准备工作:《支付宝条码支付开发文档.docx》
开发环境:qt 5.4.0
sdk or demo:no
寻求帮助途径:上网百度,其次就是在支付宝开发者平台那里提问了。。。。。
开发加自测周期:两周
其实,做这个功能最大的障碍在于进行RSA签名和验签,其它代码都很好实现。支付宝开发
者平台未提供相应的sdk,只是提供了c++的,c++的直接引用到程序中也不好使,因此,只
能通过上网查资料的方式来完成RSAwithSHA的签名和验签,关键问题时,网上关于qt开发
支付宝直连的资料不仅少之又少,而且找来的一些资料大多无用,最后在一篇技术贴中找到
了密钥读取以及签名和验签的函数实现。
直接贴代码:
密钥读取函数
bool getKey(QString &errMsg)
{
//私钥读取
QByteArray ba;
BIO * key_pri = NULL;
priKey = NULL;
QString filename_pri=QDir::currentPath()+"/ali_rsa_private_key.pem";
ba=(filename_pri.replace("/","\\")).toLatin1();
const char* filename_pri_formed=ba.data();
key_pri = BIO_new(BIO_s_file());
BIO_read_filename(key_pri, filename_pri_formed );
priKey = PEM_read_bio_RSAPrivateKey(key_pri, NULL, NULL, NULL);
if(priKey==NULL)
{
errMsg=QObject::tr("私钥指针为空");
return false;
}
BIO_free_all(key_pri);
//公钥读取
BIO * key_pub=NULL;
pubKey=NULL;
QString filename_pub=QDir::currentPath()+"/ali_rsa_public_key.pem";
ba.clear();
ba =(filename_pub.replace("/","\\")).toLatin1();
const char *filename_pub_formed = ba.data();
key_pub = BIO_new(BIO_s_file());
BIO_read_filename(key_pub, filename_pub_formed );
pubKey =PEM_read_bio_RSA_PUBKEY(key_pub, NULL, NULL, NULL);//
PEM_read_bio_RSA_PUBKEY
if(pubKey==NULL)
{
errMsg=QObject::tr("支付宝公钥指针为空");
return false;
}
BIO_free_all(key_pub);
return true;
}
以上函数便是对密钥读取的函数,需要加载静态链接库:libeay32.lib和ssleay.lib,并
且包含相应的头文件文件夹openssl。
签名函数:
QString rsaSign(const QString &content, RSA *p_rsa)
{
QString signed_str;
QByteArray ba;
if (p_rsa != NULL) {
ba.clear();
ba=content.toUtf8();
const char *cstr = ba.data();
unsigned char hash[SHA_DIGEST_LENGTH] = {0};
SHA1((unsigned char *)cstr, strlen(cstr), hash);
unsigned char sign[XRSA_KEY_BITS / 8] = {0};
unsigned int sign_len = sizeof(sign);
int r = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sign,
&sign_len, p_rsa);
if (0 != r && sizeof(sign) == sign_len)
{
signed_str = QString::fromStdString(base64Encode(sign,
sign_len));
}
}
RSA_free(p_rsa);
return signed_str;
}
验签函数:
bool rsaVerify(const QString &content, const QString &sign, RSA *p_rsa)
{
bool result = false;
QByteArray ba;
if (p_rsa != NULL) {
ba.clear();
ba=content.toUtf8();
const char *cstr = ba.data();
unsigned char hash[SHA_DIGEST_LENGTH] = {0};
SHA1((unsigned char *)cstr, strlen(cstr), hash);
unsigned char sign_cstr[XRSA_KEY_BITS / 8] = {0};
int len = XRSA_KEY_BITS / 8;
base64Decode(sign, sign_cstr, len);
unsigned int sign_len = XRSA_KEY_BITS / 8;
int r = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, (unsigned
char *)sign_cstr, sign_len, p_rsa);
if (r > 0) {
result = true;
}
}
RSA_free(p_rsa);
return result;
}
值得注意的是:使用这种方式进行密钥读取时,需要在每次进行签名和验签之前进行
getkey调用,如果签名和验签采用同一个指针,则可能造成崩溃。
其它需要注意的地方都是一些小的细节,比如base64的encode和decode以及url的encode和decode。
这些处理不好,有可能造成验签不过的情况。
程序写好之后,可以使用支付宝提供的沙箱进行测试,具体的测试方式以及步骤,参考支付
宝开发者平台。