只做我网银的支付接口
最近一周做银联的交易接口,踩了不少的坑。现在把代码全部奉上。
#include <io.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <tchar.h>
#include <winsock2.h>
#include <Windows.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include "WjCryptLib_Sha1.h"
#include "WjCryptLib_Sha256.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <openssl/applink.c>
#ifdef __cplusplus
}
#endif
//static char basis_64[203] = "/012FGpqrWXY39abPQRBjkcdNOwxCHIJefgzAvMKLhistuSTUVylmnoDEZ45678+???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";
static char basis_64[203] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";
static char Pad64 = '=';
int Base64Encode(src, srclength, target, targsize)
unsigned char const *src;
size_t srclength;
char *target;
size_t targsize;
{
size_t datalength = 0;
unsigned char input[3];
unsigned char output[4];
int i;
while (2 < srclength) {
input[0] = *src++;
input[1] = *src++;
input[2] = *src++;
srclength -= 3;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
output[3] = input[2] & 0x3f;
if (datalength + 4 > targsize)
return (-1);
target[datalength++] = basis_64[output[0]];
target[datalength++] = basis_64[output[1]];
target[datalength++] = basis_64[output[2]];
target[datalength++] = basis_64[output[3]];
}
/* Now we worry about padding. */
if (0 != srclength) {
/* Get what's left. */
input[0] = input[1] = input[2] = '\0';
for (i = 0; i < srclength; i++)
input[i] = *src++;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
if (datalength + 4 > targsize)
return (-1);
target[datalength++] = basis_64[output[0]];
target[datalength++] = basis_64[output[1]];
if (srclength == 1)
target[datalength++] = Pad64;
else
target[datalength++] = basis_64[output[2]];
target[datalength++] = Pad64;
}
if (datalength >= targsize)
return (-1);
target[datalength] = '\0'; /* Returned value doesn't count \0. */
return (datalength);
}
int Base64Decode(src, target, targsize)
char const *src;
unsigned char *target;
size_t targsize;
{
int tarindex, state, ch;
unsigned char nextbyte;
char *pos;
state = 0;
tarindex = 0;
while ((ch = (unsigned char)*src++) != '\0') {
if (isspace(ch)) /* Skip whitespace anywhere. */
continue;
if (ch == Pad64)
break;
pos = strchr(basis_64, ch);
if (pos == 0) /* A non-base64 character. */
return (-1);
switch (state) {
case 0:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] = (pos - basis_64) << 2;
}
state = 1;
break;
case 1:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] |= (pos - basis_64) >> 4;
nextbyte = ((pos - basis_64) & 0x0f) << 4;
if (tarindex + 1 < targsize)
target[tarindex+1] = nextbyte;
else if (nextbyte)
return (-1);
}
tarindex++;
state = 2;
break;
case 2:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] |= (pos - basis_64) >> 2;
nextbyte = ((pos - basis_64) & 0x03) << 6;
if (tarindex + 1 < targsize)
target[tarindex+1] = nextbyte;
else if (nextbyte)
return (-1);
}
tarindex++;
state = 3;
break;
case 3:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] |= (pos - basis_64);
}
tarindex++;
state = 0;
break;
}
}
/*
* We are done decoding Base-64 chars. Let's see if we ended
* on a byte boundary, and/or with erroneous trailing characters.
*/
if (ch == Pad64) { /* We got a pad char. */
ch = (unsigned char)*src++; /* Skip it, get next. */
switch (state) {
case 0: /* Invalid = in first position */
case 1: /* Invalid = in second position */
return (-1);
case 2: /* Valid, means one byte of info */
/* Skip any number of spaces. */
for (; ch != '\0'; ch = (unsigned char)*src++)
if (!isspace(ch))
break;
/* Make sure there is another trailing = sign. */
if (ch != Pad64)
return (-1);
ch = (unsigned char)*src++; /* Skip the = */
/* Fall through to "single trailing =" case. */
/* FALLTHROUGH */
case 3: /* Valid, means two bytes of info */
/*
* We know this char is an =. Is there anything but
* whitespace after it?
*/
for (; ch != '\0'; ch = (unsigned char)*src++)
if (!isspace(ch))
return (-1);
/*
* Now make sure for cases 2 and 3 that the "extra"
* bits that slopped past the last full byte were
* zeros. If we don't check them, they become a
* subliminal channel.
*/
if (target && tarindex < targsize &&
target[tarindex] != 0)
return (-1);
}
} else {
/*
* We ended by seeing the end of the string. Make sure we
* have no partial bytes lying around.
*/
if (state != 0)
return (-1);
}
return (tarindex);
}
//用openssl自家的base64输出值。
int base64_encode(char *in_str, int in_len, char *out_str)
{
BIO *b64, *bio;
BUF_MEM *bptr = NULL;
size_t size = 0;
if (in_str == NULL || out_str == NULL)
return -1;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_write(bio, in_str, in_len);
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bptr);
memcpy(out_str, bptr->data, bptr->length);
out_str[bptr->length] = '\0';
size = bptr->length;
BIO_free_all(bio);
return size;
}
int base64_decode(char *in_str, int in_len, char *out_str)
{
BIO *b64, *bio;
BUF_MEM *bptr = NULL;
int counts;
int size = 0;
if (in_str == NULL || out_str == NULL)
return -1;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bio = BIO_new_mem_buf(in_str, in_len);
bio = BIO_push(b64, bio);
size = BIO_read(bio, out_str, in_len);
out_str[size] = '\0';
BIO_free_all(bio);
return size;
}
char *_btoa(char *ecode)
{
static int inlen,outlen;
static char outstr[4096];
inlen=strlen(ecode);
outlen=sizeof(outstr);
Base64Encode(ecode, inlen, outstr, &outlen);
return outstr;
}
char *_atob(char *dcode)
{
static int outlen;
static char outstr[4096];
Base64Decode(dcode, outstr, &outlen);
return outstr;
}
//有点bug 输出大于4096自己改
char *urlencorder(char *ecode)
{
int i,n,len;
static char outstr[4096];
len = strlen(ecode);
for(i=n=0;n<len;n++)
{
//如果是数字或字母0-9 or a-z or A-Z
if((ecode[n]>='0' && ecode[n]<='9') ||
(ecode[n]>='a' && ecode[n]<='z') ||
(ecode[n]>='A' && ecode[n]<='Z'))
{
outstr[i] = ecode[n];
i++;
}
//否则就要加上%及字符16进制码
else
{
sprintf(outstr+i, "%%%02x", ecode[n]&0xff);
i += 3;
}
}
outstr[i] = 0;
return outstr;
}
//https://www.php.net/manual/zh/function.hash.php
char *sha256Abstract(char *string)
{
char *ptr;
static char sha_str[64];
Sha256Context sha256Context;
SHA256_HASH sha256Hash;
uint16_t i,j;
Sha256Initialise( &sha256Context );
Sha256Update( &sha256Context, (unsigned char*)string, (uint32_t)strlen(string) );
Sha256Finalise( &sha256Context, &sha256Hash );
for( i=0,j=0; i<sizeof(sha256Hash); i++ )
{
j = i*2;
ptr = &sha_str[j];
sprintf( ptr,"%02x", sha256Hash.bytes[i] );
}
printf( "%s\n",sha_str );
return sha_str;
}
char *sha11Abstract(char *string)
{
char *ptr;
static char sha_str[40];
Sha1Context sha1Context;
SHA1_HASH sha1Hash;
uint16_t i,j;
Sha1Initialise( &sha1Context );
Sha1Update( &sha1Context, (unsigned char*)string, (uint32_t)strlen(string) );
Sha1Finalise( &sha1Context, &sha1Hash );
for( i=0,j=0; i<sizeof(sha1Hash); i++ )
{
j = i*2;
ptr = &sha_str[j];
sprintf( ptr,"%02x", sha1Hash.bytes[i] );
}
printf( "%s\n",sha_str );
return sha_str;
}
RSA *getRSAPrivateKey(char *file)
{
BIO *priio;
RSA *prikey = RSA_new();
priio = BIO_new_file(file, "rb");
prikey = PEM_read_bio_RSAPrivateKey(priio, &prikey, NULL, NULL);
BIO_free(priio);
return prikey;
}
RSA *getRSAPublicKey(char *file)
{
BIO *pubio;
RSA *pubkey = RSA_new();
pubio = BIO_new_file(file, "rb");
pubkey = PEM_read_bio_RSAPublicKey(pubio, &pubkey, NULL, NULL);
BIO_free(pubio);
return pubkey;
}
//https://pay.weixin.qq.com/wiki/doc/api/index.html //微信支付都要跳转到微信平台
//https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay
//https://open.unionpay.com/tjweb/acproduct/APIList?acpAPIId=334&apiservId=453&version=V2.2&bussType=0
//application/x-www-form-urlencoded空格要转换成'+'
//
//openssl md5 -sha256 -sign acp_test_sign.key -out zsign1.txt zfile.txt
//openssl base64 -in zsign1.txt -out zbase64sign1.txt
char *Payment(SSL *fd)
{
int i,len;
time_t now;
struct tm *tp;
BIO *priio,*pubio;
RSA *pubkey = RSA_new();
RSA *prikey = RSA_new();
char *ptr,*msg,buf[4096],temp[1024],sha[64];
char version[] = "5.1.0";//版本号
char encoding[16] = "utf-8";//"utf-8";//编码方式
char txnType[] = "01";//交易类型
char txnSubType[] = "01";//交易子类
char bizType[] = "000201";//业务类型
char backUrl[64] = "http://www.specialUrl.com";//后台通知地址 可以固定上送http://www.specialUrl.com
char signMethod[] = "01";//"RSA-SHA256";//签名方法 RSA-SHA256:表示采用RSA签名,摘要算法用SHA256
unsigned char signature[1024];//报文签名BASE64
char channelType[] = "08";//渠道类型,07-PC,08-手机
char accessType[] = "0";//接入类型
char currencyCode[] ="156";//交易币种,境内商户固定156
char merId[20] = "777290058183095";//商户代码 仅支持数字和字母
char orderId[40] = "SJ20200827170201";//商户订单号 仅支持数字和字母
char txnTime[20] = "20200827170201";//订单发送时间,格式为YYYYMMDDhhmmss,取北京时间
char txnAmt[10] = "22";//交易金额,单位分
char payTimeout[20];//超过超时时间就算交易成功钱也会返回到用户卡上date('YmdHis', strtotime('+15 minutes'))
//char riskRateInfo[] = "{commodityName=测试商品名称}";//风控信息字段
//char merCertId[128];//商户签名私钥证书的Serial Number(十进制)仅支持数字
//char nonceStr[32] = "asdfasdf";//随机字符串
//char bizMethod[128] = "acp.unified.pay";//"acp.trade.pay";//业务接口
//char bizContent[10240];//
char certId[128] = "68759663125";//"1002653215";//证书ID
char frontUrl[64] = "http://api.mysxlive.com/";//前台通知地址
priio = BIO_new_file("./acp_test_sign.key", "rb");
prikey = PEM_read_bio_RSAPrivateKey(priio, &prikey, NULL, NULL);
pubio = BIO_new_file("./acp_test_public_sign.key", "rb");
pubkey = PEM_read_bio_RSAPublicKey(pubio, &pubkey, NULL, NULL);
now = time( (time_t*) 0 );
tp = localtime(&now);
//订单发送时间
sprintf(txnTime,"%d%02d%02d%02d%02d%02d",1900 + tp->tm_year,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
//tp->tm_min+1 超过五分钟没有交易成功的就超时
sprintf(payTimeout,"%d%02d%02d%02d%02d%02d",1900 + tp->tm_year,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min+5,tp->tm_sec);
//商户订单号
sprintf(orderId,"SJ%s",txnTime);
//注意了这里哪部分内容拿去前面银联demo也没有说清楚!
//sprintf(buf,"version=%s&encoding=%s&txnType=%s&txnSubType=%s&bizType=%s&backUrl=%s&channelType=%s&accessType=%s¤cyCode=%s&merId=%s&orderId=%s&txnTime=%s&txnAmt=%s&payTimeout=%s&certId=%s&frontUrl=%s&signMethod=%s",version,encoding,txnType,txnSubType,bizType,backUrl,channelType,accessType,currencyCode,merId,orderId,txnTime,txnAmt,payTimeout,certId,frontUrl,signMethod);
sprintf(buf,"accessType=%s&backUrl=%s&bizType=%s&certId=%s&channelType=%s¤cyCode=%s&encoding=%s&frontUrl=%s&merId=%s&orderId=%s&payTimeout=%s&signMethod=%s&txnAmt=%s&txnSubType=%s&txnTime=%s&txnType=%s&version=%s",accessType,backUrl,bizType,certId,channelType,currencyCode,encoding,frontUrl,merId,orderId,payTimeout,signMethod,txnAmt,txnSubType,txnTime,txnType,version);
len = 0;
//"360d9f5272fce05908375f25a5c398c747eccea87bd1f485105fa2a6b3f504c9"
//strcpy(sha,sha256Abstract("accessType=0&backUrl=http://www.specialUrl.com&bizType=000201&certId=68759663125&channelType=08¤cyCode=156&encoding=utf-8&frontUrl=http://api.mysxlive.com/&merId=777290058183095156&orderId=SJ20200902094006&payTimeout=20200902094506&signMethod=01&txnAmt=22&txnSubType=01&txnTime=20200902094006&txnType=01&version=5.1.0"));
//strcpy(sha,"360d9f5272fce05908375f25a5c398c747eccea87bd1f485105fa2a6b3f504c9");
strcpy(sha,sha256Abstract(buf));
msg = sha;
//有点蛋疼的交互接口,php$params_str = createLinkString ( $params, true, false );不需要$value = urlencode ( $value );的!...
strcpy(encoding,"utf%2d8");
strcpy(backUrl,"http%3a%2f%2fwww%2especialUrl%2ecom");
strcpy(frontUrl,"http%3a%2f%2fapi%2emysxlive%2ecom%2f");
sprintf(buf,"accessType=%s&backUrl=%s&bizType=%s&certId=%s&channelType=%s¤cyCode=%s&encoding=%s&frontUrl=%s&merId=%s&orderId=%s&payTimeout=%s&signMethod=%s&txnAmt=%s&txnSubType=%s&txnTime=%s&txnType=%s&version=%s",accessType,backUrl,bizType,certId,channelType,currencyCode,encoding,frontUrl,merId,orderId,payTimeout,signMethod,txnAmt,txnSubType,txnTime,txnType,version);
printf("===============000================\n");
EVP_MD_CTX *mdctx; //摘要算法上下文变量
EVP_PKEY *evpKey=NULL,*evpubkey=NULL; //EVP KEY结构体变量
mdctx = EVP_MD_CTX_create();
evpKey = EVP_PKEY_new();//新建一个EVP_PKEY变量
if(evpKey == NULL)
{
printf("EVP_PKEY_new err\n");
goto endPayment;
}
if(EVP_PKEY_set1_RSA(evpKey,prikey) != 1) //保存prikey结构体到EVP_PKEY结构体
{
printf("EVP_PKEY_set1_RSA err\n");
EVP_PKEY_free(evpKey);
goto endPayment;
}
//以下是计算签名代码
EVP_MD_CTX_init(mdctx);//初始化摘要上下文
printf("===============001================\n");
if(!EVP_SignInit_ex(mdctx, EVP_sha256(), NULL))//签名初始化,设置摘要算法EVP_md5()
{
printf("err\n");
EVP_PKEY_free(evpKey);
goto endPayment;
}
printf("===============002================\n");
if(!EVP_SignUpdate(mdctx, msg, strlen(msg)))//计算签名(摘要)Update
{
printf("err\n");
EVP_PKEY_free(evpKey);
goto endPayment;
}
printf("===============003================\n");
if(!EVP_SignFinal(mdctx,signature,&len,evpKey)) //签名输出
{
printf("err\n");
EVP_PKEY_free(evpKey);
goto endPayment;
}
printf("===============004================\n");
//printf("消息\"%s\"的签名值是: \n",mess1);
for(i = 0; i < len; i++)
{
if(i%16==0)
printf("\n%08xH: ",i);
printf("%02x ", signature[i]);
}
printf("\n");
//EVP_MD_CTX_cleanup(&mdctx);
/*if(RSA_sign(NID_sha256, msg, strlen(msg),signature,&len, prikey)!=1)
{
printf("RSA_sign err!\n");
RSA_free(prikey);
return -1;
}*/
//以下是验证签名代码
evpubkey = EVP_PKEY_new();//新建一个EVP_PKEY变量
if(EVP_PKEY_set1_RSA(evpubkey,pubkey) != 1) //保存prikey结构体到EVP_PKEY结构体
{
printf("EVP_PKEY_set1_RSA err\n");
EVP_PKEY_free(evpubkey);
goto endPayment;
}
EVP_MD_CTX_init(mdctx);//初始化摘要上下文
if(!EVP_VerifyInit_ex(mdctx, EVP_sha256(), NULL))//验证初始化,设置摘要算法。
{
printf("EVP_VerifyInit_ex err\n");
EVP_PKEY_free(evpubkey);
goto endPayment;
}
if(!EVP_VerifyUpdate(mdctx, msg, strlen(msg)))//验证签名(摘要)Update
{
printf("err\n");
EVP_PKEY_free(evpubkey);
goto endPayment;
}
if(!EVP_VerifyFinal(mdctx,signature,len,evpubkey))//验证签名
{
printf("verify err\n");
EVP_PKEY_free(evpubkey);
goto endPayment;
}
else
{
printf("Verify that the signature is correct.\n");
}
#if 0
if(RSA_sign(NID_sha256,msg,strlen(msg),signature,&len,prikey)!=1)
{
printf("RSA_sign err!\n");
goto endPayment;
}
for(i = 0; i < len; i++)
{
if(i%16==0)
printf("\n%08xH: ",i);
printf("%02x ", signature[i]);
}
#endif
printf("[[strlen(buf)=%d]][len=%d]\n",strlen(buf),len);
char outstr[4096];
int outlen = sizeof(outstr);
//
Base64Encode(signature, len, outstr, &outlen);
//base64_encode(signature,len,outstr);
printf("[[outlen=%d]]|%s\n",outlen,"go in");
printf("\n[[%s]]\n\n",outstr);
ptr = &buf[strlen(buf)];
sprintf(ptr,"&signature=%s",urlencorder(outstr));//urlencorder 有bug.
printf("[len=%d][outlen=%d][[%s]]\n",len,outlen,buf);
//int vret = RSA_verify(NID_sha1, msg, strlen(msg), sinDat, sinLen, pubkey);
//printf("[%s]|sinLen=%d|sign_verify=%d\n",sinDat,sinLen,vret);
len = strlen(buf);
sprintf(temp,"POST /gateway/api/frontTransReq.do HTTP/1.1\n\
Host: gateway.test.95516.com\n\
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0\n\
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\n\
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\n\
Accept-Encoding: gzip, deflate\n\
Content-Type: application/x-www-form-urlencoded\n\
Content-Length: %d\n\
Connection: keep-alive\n\
Upgrade-Insecure-Requests: 1\n\n",len);
int clen = SSL_write(fd,temp,strlen(temp));
if (clen < 0)
{
printf("message send fail!error code is %d,error info'%s'\n",errno, strerror(errno));
exit(0);
}
clen = SSL_write(fd,buf,len);
if (clen < 0)
{
printf("message send fail!error code is %d,error info'%s'\n",errno, strerror(errno));
exit(0);
}
//接收返回数据
//...
int nfile=open("./zresult.txt",O_WRONLY|O_TRUNC|O_CREAT,00700);
if(nfile==-1)exit( 1 );
do
{
len = SSL_read(fd, buf, 4096);
buf[len] = 0;
if(len)printf("[len=%d][\n%s]\n",len,buf);
//for(i=0;i<len;i++)putc(buf[i],stdout);
if(len)write(nfile,buf,len);//debug
}while(0);
close(nfile);
//提取验签,其实也可以不验签直接处理结果即可!
//...
endPayment:
EVP_MD_CTX_destroy(mdctx);
RSA_free(pubkey);
BIO_free(pubio);
RSA_free(prikey);
BIO_free(priio);
return buf;
}
#if 0
//ConsumeuUndo
{
char version[] = "6.0.0";//版本号
char encoding[] = "utf-8";//编码方式
char txnType[] = "31";//交易类型
char txnSubType[] = "00";//交易子类
char bizType[] = "000201";//业务类型
char backUrl[] = "http://www.specialUrl.com";//后台通知地址 可以固定上送http://www.specialUrl.com
char signMethod[32];//签名方法 RSA-SHA256:表示采用RSA签名,摘要算法用SHA256
char signature[1024];//报文签名
char channelType[] = "08";//渠道类型,07-PC,08-手机
char accessType[] = "0";//接入类型
char merId[15] = "123456789";//商户代码 仅支持数字和字母
char orderId[40] = "SJ20200827170201";//商户订单号 仅支持数字和字母
char origQryId[] = "";//原消费的queryId,可以从查询接口或者通知接口中获取
char txnTime[] = "20200827170201";//订单发送时间,格式为YYYYMMDDhhmmss,取北京时间
char txnAmt[] = "";//交易金额,单位分
}
//如果返回处理中,还需要定时刷新查询状态,如果状态未知还要进行冲正操作。
//Query
{
char version[] = "6.0.0";//版本号
char encoding[] = "utf-8";//编码方式
char txnType[] = "00";//交易类型
char txnSubType[] = "00";//交易子类
char bizType[] = "000000";//业务类型
char backUrl[] = "http://www.specialUrl.com";//后台通知地址 可以固定上送http://www.specialUrl.com
char signMethod[32];//签名方法 RSA-SHA256:表示采用RSA签名,摘要算法用SHA256
char signature[1024];//报文签名
char channelType[] = "08";//渠道类型,07-PC,08-手机
char accessType[] = "0";//接入类型
char merId[15] = "123456789";//商户代码 仅支持数字和字母
char orderId[40] = "SJ20200827170201";//商户订单号 仅支持数字和字母
char txnTime[] = "20200827170201";//订单发送时间,格式为YYYYMMDDhhmmss,取北京时间
}
#endif
void testRsa()
{
/*BIO *priio,*pubio;
RSA *pubkey = RSA_new();
RSA *prikey = RSA_new();
priio = BIO_new_file("./acp_test_sign_pkcs8.key", "rb");
prikey = PEM_read_bio_RSAPrivateKey(priio, &prikey, NULL, NULL);
pubio = BIO_new_file("./acp_test_public_sign_pkcs8.key", "rb");
pubkey = PEM_read_bio_RSAPublicKey(pubio, &pubkey, NULL, NULL);
RSA_print_fp(stdout, pubkey, 0);
printf("====================================\n");
RSA_print_fp(stdout, prikey, 0);
RSA_free(pubkey);
BIO_free(pubio);
RSA_free(prikey);
BIO_free(priio);*/
FILE *prif,*pubf;
RSA *pubkey = RSA_new();
RSA *prikey = RSA_new();
prif = fopen("./acp_test_sign.key", "rb");
prikey = PEM_read_RSAPrivateKey(prif, &prikey, NULL, NULL);
pubf = fopen("./acp_test_public_sign.key", "rb");
pubkey = PEM_read_RSAPublicKey(pubf, &pubkey, NULL, NULL);
RSA_print_fp(stdout, pubkey, 0);
printf("====================================\n");
RSA_print_fp(stdout, prikey, 0);
fclose(pubf);
fclose(prif);
RSA_free(pubkey);
RSA_free(prikey);
}
#if 0
int send_headers(SSL *fd,int len)
{
char buf[1024];
sprintf(buf,"POST /Monitor HTTP/1.1\n\
Host: 110.87.159.245:443\n\
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0\n\
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\n\
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\n\
Accept-Encoding: gzip, deflate\n\
Content-Type: application/x-www-form-urlencoded\n\
Content-Length: %d\n\
Connection: keep-alive\n\
Upgrade-Insecure-Requests: 1\n\n",len);
return SSL_write(fd,buf,strlen(buf));//send(fd, buf, strlen(buf), 0);
}
#endif
//
//
//
//
//110.87.159.245
#if 0
int main(int argc, char * argv[])
{
//testRsa();
/*RSA *prikey,*pubkey;
char *msg = "0123456789";
char *sinDat = malloc(4096);
int sinLen = 0;
//prikey = getRSAPrivateKey("./acp_test_sign.key");
prikey = getRSAPrivateKey("./acp_test_sign_pkcs8.key");
pubkey = getRSAPublicKey("./acp_test_public_sign.key");
//pubkey = getRSAPublicKey("./acp_test_public_sign_pkcs8.key");
RSA_sign(NID_sha1, msg,strlen(msg),sinDat,&sinLen, prikey);
int vret = RSA_verify(NID_sha1, msg, strlen(msg), sinDat, sinLen, pubkey);
printf("[%s]|sinLen=%d|sign_verify=%d\n",sinDat,sinLen,vret);
RSA_free(pubkey);
RSA_free(prikey);*/
//sha256Abstract("my name is samuel...");
int tp;
char *pemCert;
struct stat fst;
X509 *x509;
EVP_PKEY *pkey;
RSA *pubkey;
tp=open("./server.crt",O_RDONLY|O_BINARY,00700);
if(tp==-1)
{
return printf("open file error!\n");
}
fstat( tp, &fst );
lseek( tp, 0, SEEK_SET );//再定位文件指针到文件头
pemCert = malloc(fst.st_size);
read(tp,pemCert,fst.st_size);
close(tp);
printf("\n[%s]\n=======================================\n",pemCert);
//BIO *b = BIO_new_mem_buf(pemCert, fst.st_size);
BIO *b = BIO_new_file("./server.crt", "r");
if (NULL == b){
return -1001;
}
printf("\n=====0==================================\n");
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
if (NULL == x509){
BIO_free(b), b=NULL;
X509_free(x509), x509=NULL;
ERR_print_errors_fp(stderr);
exit(1);
}
printf("\n=====1==================================\n");
/* Get public key - eay */
pkey=X509_get_pubkey(x509);
if (pkey == NULL) {
ERR_print_errors_fp (stderr);
exit (1);
}
printf("\n=====2==================================\n");
pubkey = EVP_PKEY_get1_RSA(pkey);
int i,len;
EVP_MD_CTX *mdctx; //摘要算法上下文变量
EVP_PKEY *evpubkey=NULL; //EVP KEY结构体变量
char signature[4096],*msg = "360d9f5272fce05908375f25a5c398c747eccea87bd1f485105fa2a6b3f504c9";
/*evpubkey = EVP_PKEY_new();//新建一个EVP_PKEY变量
if(EVP_PKEY_set1_RSA(evpubkey,pubkey) != 1) //保存prikey结构体到EVP_PKEY结构体
{
printf("EVP_PKEY_set1_RSA err\n");
EVP_PKEY_free(evpubkey);
exit(1);
}
//以下是计算签名代码
mdctx = EVP_MD_CTX_create();
EVP_MD_CTX_init(mdctx);//初始化摘要上下文
printf("===============001================\n");
if(!EVP_SignInit_ex(mdctx, EVP_sha256(), NULL))//签名初始化,设置摘要算法EVP_md5()
{
printf("err\n");
EVP_PKEY_free(evpubkey);
exit(1);
}
printf("===============002================\n");
if(!EVP_SignUpdate(mdctx, msg, strlen(msg)))//计算签名(摘要)Update
{
printf("err\n");
EVP_PKEY_free(evpubkey);
exit(1);
}
printf("===============003================\n");
if(!EVP_SignFinal(mdctx,signature,&len,evpubkey)) //签名输出
{
printf("err\n");
EVP_PKEY_free(evpubkey);
exit(1);
}
printf("===============004================\n");*/
printf("\n=====2.5==================================\n");
if(RSA_sign(NID_sha256, msg, strlen(msg),signature,&len, pubkey)!=1)
{
printf("RSA_sign err!\n");
RSA_free(pubkey);
return -1;
}
//printf("消息\"%s\"的签名值是: \n",mess1);
for(i = 0; i < len; i++)
{
if(i%16==0)
printf("\n%08xH: ",i);
printf("%02x ", signature[i]);
}
printf("\n=====3==================================\n");
RSA_print_fp(stdout, pubkey, 0);
printf("\n=====4==================================\n");
X509 *cert;
char *line;
cert = x509;
//cert = SSL_get_peer_certificate(ssl);
if (cert != NULL)
{
printf("数字证书信息:\n");
//line = X509_NAME_oneline(X509_get_serialNumber(cert), 0, 0);
//printf("证书: %s\n", line);
ASN1_INTEGER *bs = NULL;
char *res = NULL;
BIGNUM *bn = NULL;
bs = X509_get_serialNumber(cert);
if (bs->length == 0) {
printf("X509_get_serialNumber() length=0 error!\n");
return -1;
}
bn = ASN1_INTEGER_to_BN(bs, NULL);
res = BN_bn2hex(bn);
printf("serial = %s\n", res);
OPENSSL_free(res);
res = NULL;
BN_free(bn);
bn = NULL;
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("证书: %s\n", line);
OPENSSL_free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("颁发者: %s\n", line);
OPENSSL_free(line);
X509_free(cert);
}
free(pemCert);
return 1;
}
#endif
int main(int argc, char * argv[])
{
WSADATA wsaData;
int sockfd,len,ret;
struct sockaddr_in dest;
char buffer[10240];
//这里用了服务端一样的证书和KEY所以连接不成功的
//char *certificate_file="./3453137_classify.mysxlive.com.pem";
//char *PrivateKey_file="./3453137_classify.mysxlive.com.key";
SSL_CTX *ctx;
SSL *ssl;
/* SSL 库初始化,参看 ssl-server.c 代码 */
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(TLSv1_2_client_method());
if (ctx == NULL) {
ERR_print_errors_fp(stdout);
exit(1);
}
#if 0
// 双向验证
// SSL_VERIFY_PEER---要求对证书进行认证,没有证书也会放行
// SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
// 设置信任根证书
if (SSL_CTX_load_verify_locations(ctx, "./ca.cer",NULL)<=0){
printf("Failed to load CA digital certificate\n");
return -1;
}
/* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
if (SSL_CTX_use_certificate_file(ctx, certificate_file, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 载入用户私钥 */
if (SSL_CTX_use_PrivateKey_file(ctx, PrivateKey_file, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 检查用户私钥是否正确 */
if (!SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stdout);
exit(1);
}
#endif
//MAKEWORD(1, 1)1.1版只支持TCP/IP协议MAKEWORD(2, 2)2.2版只支持TCP/IP协议
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("WSAStartup failed\n");
return -1;
}
/* 创建一个 socket 用于 tcp 通信 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket");
exit(errno);
}
printf("socket created\n");
/* 初始化服务器端(对方)的地址和端口信息 */
memset(&dest,0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(443);
dest.sin_addr.S_un.S_addr = inet_addr("58.220.73.190");//先向我们自己的https服务器发送测试请求106.122.254.99 118.123.233.174 42.81.147.159 58.220.73.190 112.47.14.66 23.217.207.13 150.138.170.33
printf("address created\n");
/* 连接服务器 */
if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
perror("Connect ");
exit(errno);
}
printf("server connected\n");
/* 基于 ctx 产生一个新的 SSL */
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
/* 建立 SSL 连接 */
if (SSL_connect(ssl) == -1)
ERR_print_errors_fp(stderr);
else {
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
//ShowCerts(ssl);//客户端验证证书...
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL)
{
printf("数字证书信息:\n");
//line = X509_NAME_oneline(X509_get_serialNumber(cert), 0, 0);
//printf("证书: %s\n", line);
ASN1_INTEGER *bs = NULL;
char *res = NULL;
BIGNUM *bn = NULL;
bs = X509_get_serialNumber(cert);
if (bs->length == 0) {
printf("X509_get_serialNumber() length=0 error!\n");
return -1;
}
bn = ASN1_INTEGER_to_BN(bs, NULL);
res = BN_bn2hex(bn);
printf("serial = %s\n", res);
OPENSSL_free(res);
res = NULL;
BN_free(bn);
bn = NULL;
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("证书: %s\n", line);
OPENSSL_free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("颁发者: %s\n", line);
OPENSSL_free(line);
X509_free(cert);
}
//x509.get_serial_number
/*int x509_get_serial_num(X509 *x)
{
ASN1_INTEGER *bs = NULL;
char *res = NULL;
BIGNUM *bn = NULL;
bs = X509_get_serialNumber(x);
if (bs->length == 0) {
printf("X509_get_serialNumber() length=0 error!\n");
return -1;
}
bn = ASN1_INTEGER_to_BN(bs, NULL);
res = BN_bn2hex(bn);
printf("serial = %s\n", res);
OPENSSL_free(res);
res = NULL;
BN_free(bn);
bn = NULL;
return 0 ;
}*/
}
Payment(ssl);
#if 0
//发送一个GET/POST请求
memset(buffer,0, MAXBUF);
strcpy(buffer, "GET /web/index.html HTTP/1.1\n"
"Host: 127.0.0.1\n"
"User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\n"
"Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\n"
"Accept-Encoding: gzip, deflate\n"
"Connection: keep-alive\n"
"Cookie: Hm_lvt_fe3b7a223fc08c795f0f4b6350703e6f=1582005872,1582069091\n"
"Upgrade-Insecure-Requests: 1\n"
"Cache-Control: max-age=0\n\n");
/* 发消息给服务器 */
len = SSL_write(ssl, buffer, strlen(buffer));
if (len < 0)
{
printf
("message'%s'send fail!error code is %d,error info'%s'\n",
buffer, errno, strerror(errno));
exit(0);//要退出否则会一直与服务器保持while SSL_read,服务端要超时踢出设置,不然端口占完就拒绝服务了!试一下服务器有没有超时踢并关闭释放端口,另外是不是客户端这边自己死循环了。
}
else
printf("message'%s'send success,A total of %d bit!\n",
buffer, len);
/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
memset(buffer,0, MAXBUF);
//简单的HTTPS爬虫模型!
/* 接收服务器来的消息 ,可以用循环因为服务器回复完数据后就会把端口关闭,所以这个循环就会立即退出。这样看来客户端获取的包与上传到服务端上的包还不一样,如果不循环数据就读一点 就没有下文了!*/
while(SSL_read(ssl, buffer, MAXBUF))
{
printf(buffer);
memset(buffer,0, MAXBUF);
}
#endif
/*len = SSL_read(ssl, buffer, MAXBUF);
if (len > 0)
printf("reve message success :'%s',total %d bit data\n",
buffer, len);
else {
printf
("message rev faill!error code%d,error info'%s'\n",
errno, strerror(errno));
goto finish;
}*/
printf("out ....\n");
finish:
/* 关闭连接 */
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockfd);
SSL_CTX_free(ctx);
WSACleanup();
return 0;
}
最后会有一个 Location: https://cashier.test.95516.com/b2c/api/unifiedOrder.action?.......的302地址调转,转到银联的支付平台......。
代码下载: unionpay.zip