DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: cuiqingbo
今日帖子: 20
在线用户: 13
导航: 论坛 -> 开源项目 斑竹:joki,ralf_jones  
作者:
男 wangpingdejiejie (linuxping) ★☆☆☆☆ -
普通会员
2020/11/26 11:14:59
标题:
用delphi开发微信支付和支付宝支付 浏览:6260
加入我的收藏
楼主:  不管是微信支付还是支付宝支付, 3个最棘手的问题是:
1,如何生成签名
2,支付请求如何提交
3, 如何验证签名

下面就围绕这三个问题来讲。

我使用的是XE3.

先看微信支付:
1,微信支付的签名生成

 uses IdHashMessageDigest, NatvieXml; //我使用的是NativeXml4.07

function TMicroPayParamBuilder.GetSign: string;
var
  Digest: TIdHashMessageDigest5;
  utf8: UTF8String;
begin
  utf8 := '';

  Assert(FAppId <> '', '公众账号ID 不能为空!');
  utf8 := utf8 + 'appid=' + FAppId;
  
  if FAttach <> '' then
    utf8 := utf8 + '&attach=' + FAttach;

  Assert(FAuthCode <> '', '授权码 不能为空!');
  utf8 := utf8 + '&auth_code=' + FAuthCode;

  Assert(FBody <> '', '商品描述 不能为空!');
  utf8 := utf8 + '&body=' + FBody;

  if FDetail <> '' then
    utf8 := utf8 + '&detail=' + BuildDetail();

  Assert(FMchId <> '', '商户号 不能为空!');
  utf8 := utf8 + '&mch_id=' + FMchId;
  
  utf8 := utf8 + '&non_ce_str=' + FNonceStr;

  Assert(FOutTradeNo <> '', '商户订单号 不能为空!');
  utf8 := utf8 + '&out_trade_no=' + FOutTradeNo;

  Assert(FSpbillCreateIp <> '', '终端IP 不能为空!');
  utf8 := utf8 + '&spbill_create_ip=' + FSpbillCreateIp;

  Assert(FTotalFee > 0, '订单金额 必须大于0!');
  utf8 := utf8 + '&total_fee=' + IntToStr(FTotalFee);

  Assert(Fkey <> '', '密钥 不能为空!');
  utf8 := utf8 + '&key=' + Fkey;
 
 //计算签名
  try
    Digest:= TIdHashMessageDigest5.Create;
    Result := Digest.HashBytesAsHex(BytesOf(utf8));
  finally
    Digest.Free;
  end;
end;   

//生成Http Post请求的数据
function TMicroPayParamBuilder.BuildParam: string;
var
  xml : TNativeXml;
  Node: TXmlNode;
begin
  xml := TNativeXml.CreateEx(nil, False, False, True, 'xml');
  try
    Node:= xml.NodeNewTextType('appid', FAppId , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewTextType('mch_id', FMchId , xeElement);
    xml.Root.NodeAdd(Node);


    Node:= xml.NodeNewTextType('nonce_str', FNonceStr , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewTextType('sign', Sign , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewTextType('body', FBody , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewTextType('out_trade_no', FOutTradeNo , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewIntType('total_fee', FTotalFee , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewTextType('spbill_create_ip', FSpbillCreateIp , xeElement);
    xml.Root.NodeAdd(Node);

    Node:= xml.NodeNewTextType('auth_code', FAuthCode , xeElement);
    xml.Root.NodeAdd(Node);

    if FAttach <> '' then
    begin
      Node:= xml.NodeNewTextType('attach', FAttach , xeElement);
      xml.Root.NodeAdd(Node);
    end;

    if FDetail <> '' then
    begin
      Node:= xml.NodeNewTextType('detail', BuildDetail() , xeElement);
      xml.Root.NodeAdd(Node);
    end;

    Result := xml.WriteToString;

  finally
    xml.Free;
  end;
end;  

2, 微信支付请求如何提交 
class function TTencentSSLHttpPost.Post(URL: string;
  Builder: TBaseParamBuilder): string;
var
  ssl:TIdSSLIOHandlerSocketOpenSSL;
  http: TIdHttp;
  inStrm, outStrm: TStringStream;
begin
  http:= TIdHttp.Create(nil);
  ssl:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  ssl.SSLOptions.Method := sslvSSLv23;
  http.IOHandler := ssl;
  inStrm:= TStringStream.Create(Builder.BuildParam, TEncoding.UTF8);
  outStrm:= TStringStream.Create('',  TEncoding.UTF8);
  try
    try
      http.Post(URL, inStrm, outStrm);
      Result := outStrm.DataString;

    except
      Result := '';
    end;
  finally
    ssl.Free;
    http.Free;
    outStrm.Free;
    inStrm.Free;
  end;
end;

const URL_MicroPay  = 'https://api.mch.weixin.qq.com/pay/micropay&#39;;

 class function TMicroPayHttpUtils.PostRequest(  Builder: TMicroPayParamBuilder): string;
begin  Result := TTencentSSLHttpPost.Post(URL_MicroPay, Builder);
end; 

微信如何验证签名:

TStringsHelper = class helper for TStrings
  function Join(const Splitter: string): string;
end;

{ TMyStrings}

function TStringsHelper.Join(const Splitter: string): string;
var
  I : Integer;
begin
  Result := '';
  for I := 0 to Self.Count - 1 do
    Result := Result + Splitter + Self.Names[I] + '=' +  Self.ValueFromIndex[I];

  if Result <> '' then  System.Delete(result, 1, Length(Splitter));
end;
 
function MyStringListSort(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := CompareStr(List.Names[Index1], List.Names[Index2]);
end;

function VerifyResponseSign(xml: TNativeXml; Key: string): Boolean; overload;
var
  sign: string;
  I : Integer;
  List : TStringList;
  utf8: UTF8String;
  Digest: TIdHashMessageDigest5;
begin
  Result := False;

  if xml.Root.FindNode('sign') = nil then Exit;
  sign := xml.Root.FindNode('sign').ValueUnicode;

  List := TStringList.Create;
  try
    for I := 0 to xml.Root.NodeCount - 1 do
    begin
      if (xml.Root.Nodes[I].NameUnicode <> 'sign')  and (xml.Root.Nodes[I].NameUnicode <> 'WhiteSpace')
        and (xml.Root.Nodes[I].ValueUnicode <> '') then
        List.Add(xml.Root.Nodes[I].NameUnicode + '=' + xml.Root.Nodes[I].ValueUnicode);
    end;
    List.CustomSort(MyStringListSort);

    utf8 := List.Join('&') + '&key=' + key;

    try
      Digest:= TIdHashMessageDigest5.Create;
      Result := SameText(Digest.HashBytesAsHex(BytesOf(utf8)), sign);
    finally
      Digest.Free;
    end;
  finally
    List.Free;
  end;
end;

function VerifyResponseSign(xml: TNativeXml): Boolean; overload;
begin
  Result := VerifyResponseSign(xml, GetKey());
end;

function VerifyResponseSign(Response: string; Key: string): Boolean; overload;
var
  xml: TNativeXml;
begin
  Result := False;
  xml := TNativeXml.Create(nil);
  try
    try
      xml.ReadFromString(Response);
    except
      Exit;
    end;

    Result := VerifyResponseSign(xml, Key);
  finally
    xml.Free;
  end;
end; 
 
 

那么如何发送一个刷卡支付请求呢?
procedure TForm4.btn4Click(Sender: TObject);
var
  Param: TMicroPayParamBuilder;
  Response: string;
begin
  Param:= TMicroPayParamBuilder.Create;
  try
    if edBody.Text <> '' then
      Param.Body := edBody.Text;
    Param.OutTradeNo:= edTradeNo.Text;
    Param.SpbillCreateIp:=idpwtch1.LocalIP;
    param.AuthCode:= edAuthCode.Text;
    Param.Attach := edAttach.Text;
    Param.TotalFee := 10;

    param.AppId := edAppId.Text;
    Param.MchId := edMch_id.Text;
    Param.Key := edKey.Text;

    mmo1.Lines.Append(Param.BuildParam);
    mmo1.Lines.Append('----------');

    Response := TMicroPayHttpUtils.PostRequest(Param);

    if Response = '' then
    begin
      ShowMessage('请求出错! 可能是网络不通!');
      Exit;
    end;

    mmo1.Lines.Append(Response);

  if not VerifyResponseSign(Response, GetKey()) then 
      showmessage('签名验证不通过!');  

  finally
    Param.Free;
  end;
end;






支付宝支付:
1,支付宝支付的签名生成
uses IdSSLOpenSSL, IdSSLOpenSSLHeaders;

//生成RSA签名
//参数:
//PrivateKey:私钥
//Content: 要签名的内容
function GenerateRSASign(const PrivateKey:AnsiString; const Content: AnsiString): AnsiString;
var
  rsaPrivate:PRSA;
  rsa_out_len: Cardinal;
  bio:PBIO;
  hIdCrypto:HMODULE;
  hash:array[0..SHA_DIGEST_LENGTH - 1] of AnsiChar;

type
  TRSA_sign = function (_type : LongInt; const m : PAnsiChar; m_length : LongWord;
      sigret : PAnsiChar; var siglen : Cardinal; const rsa : PRSA) : LongInt; cdecl;

  TSHA1 = function (d: PAnsiChar; n:Cardinal; md:PAnsiChar):PAnsiChar; cdecl;

  function LoadFunctionCLib(const FceName: string; const ACritical : Boolean = True): Pointer;
  begin
    Result := GetProcAddress(hIdCrypto, PChar(FceName));
  end;

var
  RSA_sign : TRSA_sign;
  SHA1: TSHA1;
  bytes: TBytes;

begin
LoadOpenSSLLibrary;  
  hIdCrypto := LoadLibrary('libeay32.dll');
  Assert(hIdCrypto <> 0, '无法加载libeay32.dll');
  try
    bio :=   BIO_new_mem_buf(@PrivateKey[1], Length(PrivateKey));

    rsaPrivate :=PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil) ;
    Assert(rsaPrivate <> nil, GetErrorMssage());

    FillChar(hash[0], SHA_DIGEST_LENGTH, 0);
    SHA1:= LoadFunctionCLib('SHA1');
    Assert(@SHA1 <> nil, 'SHA1加载失败');

    SHA1(@Content[1], Length(Content), @hash[0]);
    RSA_sign:= LoadFunctionCLib('RSA_sign');
    Assert(@RSA_sign <> nil, 'RSA_sign加载失败');

    SetLength(bytes, 1024);
    RSA_sign(EVP_sha1()._type, @hash[0], SHA_DIGEST_LENGTH, @bytes[0], rsa_out_len, rsaPrivate);

    SetLength(bytes, rsa_out_len);

    Result := TIdEncoderMIME.EncodeBytes(bytes);

    BIO_free(bio);
    RSA_free(rsaPrivate);
  finally
    UnLoadOpenSSLLibrary   
    FreeLibrary(hIdCrypto);
  end;
end;

//生成条码支付的签名
//TAliPayRequestParamBuilder继承自TBaseAliPayParamBuilder
function TAliPayRequestParamBuilder.GetSign: string; 
var
  utf8: UTF8String;
  ansi: AnsiString;
  List: TStringList;
begin
  List := TStringList.Create;
  try
    Assert(AppId <> '' , '开发者的应用ID 不能为空!');
    List.Add('app_id=' + AppId);

    List.Add('biz_con_tent=' + BizContent.AsString); //BizContent: ISuperObject

    Assert(Charset <> '' , '编码格式 不能为空!');
    List.Add('charset=' + Charset);

    Assert(Method <> '' , '接口名称 不能为空!');
    List.Add('method=' + Method);

    if NotifyUrl <> '' then
      List.Add('notify_url=' + NotifyUrl);

    Assert(SignType <> '' , '签名算法类型 不能为空!');
    List.Add('sign_type=' + SignType);

    Assert(Timestamp <> '' , '发送请求时间 不能为空!');
    List.Add('timestamp=' + Timestamp);

    Assert(Version <> '' , '接口版本 不能为空!');
    List.Add('version=' + Version);

    List.CustomSort(MyStringListSort);

    utf8 := List.Join('&');
    TLogger.Instance.Warn('TTradePayParamBuilder.GetSign:' + utf8);

    ansi := Utf8ToAnsi(utf8);

    Assert(PrivateKey <> '', '私钥 不能为空!');

    Result := GenerateRSASign(PrivateKey, ansi);//生成签名
  finally
    List.Free;
  end;
end;

//生成条码支付的请求参数
procedure TAliPayRequestParamBuilder.BuildParamList(Params: TStrings);
begin
  Params.Add('app_id=' + AppId);
  Params.Add('biz_con_tent=' + BizContent.AsString);
  Params.Add('charset=' + Charset);
  Params.Add('method=' + Method);
  if NotifyUrl <> '' then
    Params.Add('notify_url=' + NotifyUrl);
  Params.Add('sign_type=' + SignType);
  Params.Add('timestamp=' + Timestamp);
  Params.Add('version=' + Version);
  Params.Add('sign=' + GetSign());
end; 



2,支付宝如何提交请求
class function TAliPaySSLHttpPost.Post(const URL: string; Builder: TBaseAliPayParamBuilder): string;
var  
  ssl:TIdSSLIOHandlerSocketOpenSSL;  
  http: TIdHttp;  
  List: TStrings;
begin  
  http:= TIdHttp.Create(nil);  
  http.Request.Accept := 'text/xml,text/javascript,text/html,text/plain';  
  http.Request.UserAgent := 'aop-sdk-java';  
  http.HTTPOptions := [hoKeepOrigProtocol, hoForceEncodeParams];  
  ssl:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);  
  ssl.SSLOptions.Method := sslvTLSv1_2;  
  http.IOHandler := ssl;  
  List:= TStringList.Create;  
  Builder.BuildParamList(List); //把请求参数放到List里 
  TLogger.Instance.Warn('TAliPaySSLHttpPost.Post Params:' + List.Join('&'));  
  try    
    try      
      Result :=  http.Post(URL, List, TEncoding.ANSI);       
    except      
       on e: Exception do      
       begin        
          Result := '';      
       end;    
    end;  
  finally    
    ssl.Free;    
    http.Free;    
    List.Free;  
  end;
end;



3,支付宝支付如何验证签名

/验证RSA签名
//参数:
//PublicKey:支付宝公钥
//Content: 要签名的内容
//Sign: 签名
function VerifyRSASign(const PublicKey,Sign: AnsiString; const Content:AnsiString ): Boolean;
var
  rsaPub:PRSA;
  bioPub:PBIO;
  hIdCrypto:HMODULE;
  hash:array[0..SHA_DIGEST_LENGTH - 1] of AnsiChar;

type
  TSHA1 = function (d: PAnsiChar; n:Cardinal; md:PAnsiChar):PAnsiChar; cdecl;

  TRSA_verify = function (dtype : LongInt; const m : PAnsiChar; m_length : LongWord;
    sigret : PAnsiChar; siglen : Cardinal; const rsa : PRSA) : LongInt; cdecl;

  TPEM_read_bio_RSA_PUBKEY =function (bp:PBIO; x:PPRSA; cb: Ppem_password_cb; u:Pointer):PRSA;  cdecl;

  function LoadFunctionCLib(const FceName: string; const ACritical : Boolean = True): Pointer;
  begin
    Result := GetProcAddress(hIdCrypto, PChar(FceName));
  end;
var
  SHA1: TSHA1;
  RSA_verify:TRSA_verify;
  bytes: TBytes;
  PEM_read_bio_RSA_PUBKEY : TPEM_read_bio_RSA_PUBKEY;
begin
  hIdCrypto := LoadLibrary('libeay32.dll');
  Assert(hIdCrypto <> 0, '无法加载libeay32.dll');
  try
    bioPub :=   BIO_new_mem_buf(@PublicKey[1], Length(PublicKey));

    PEM_read_bio_RSA_PUBKEY := LoadFunctionCLib('PEM_read_bio_RSA_PUBKEY');
    Assert(@PEM_read_bio_RSA_PUBKEY <> nil , 'PEM_read_bio_RSA_PUBKEY加载失败');
    rsaPub := PEM_read_bio_RSA_PUBKEY(bioPub, nil, nil, nil) ;
    Assert(rsaPub <> nil, GetErrorMssage());

    FillChar(hash[0], SHA_DIGEST_LENGTH, 0);
    SHA1:= LoadFunctionCLib('SHA1');
    Assert(@SHA1 <> nil, 'SHA1加载失败');
    SHA1(@Content[1], Length(Content), @hash[0]);


    bytes := TIdDecoderMIME.DecodeBytes(Sign);

    RSA_verify:= LoadFunctionCLib('RSA_verify');
    Assert(@RSA_verify <> nil, 'RSA_sign加载失败');
    Result := RSA_verify(EVP_sha1()._type, @hash[0], SHA_DIGEST_LENGTH, @bytes[0], Length(bytes), rsaPub)>0;

    BIO_free(bioPub);
    RSA_free(rsaPub);
  finally
    FreeLibrary(hIdCrypto);
  end;
end;
----------------------------------------------
no
作者:
男 keymark (嬲) ▲▲▲△△ -
普通会员
2020/11/26 13:01:26
1楼: 按此在新窗口浏览图片
----------------------------------------------
[alias]  co = clone --recurse-submodules  up = submodule update --init --recursiveupd = pullinfo = statusrest = reset --hard懒鬼提速https://www.cctry.com/>http://qalculate.github.io/downloads.htmlhttps://www.cctry.com/
作者:
男 zyp1984 (小李他妈的飞刀) ★☆☆☆☆ -
普通会员
2020/11/26 13:08:59
1楼: 这个需要顶。
----------------------------------------------
山外青山楼外楼,能人背后有能人弄..
作者:
男 wntee (wntee) ▲▲▲▲△ -
普通会员
2020/11/26 13:15:22
2楼: MARK
----------------------------------------------
当前后左右都没有路时,命运一定是鼓励你向上飞了...
作者:
男 looper (keyo) ★☆☆☆☆ -
盒子活跃会员
2020/11/26 13:56:21
3楼: 感谢分享!
----------------------------------------------
虽千万人吾往矣!
作者:
男 FengLinYuShu (FengLinYuShu) ★☆☆☆☆ -
盒子活跃会员
2020/11/26 15:41:39
4楼: 先项一下! 
有人测试过可行吗?
----------------------------------------------
-delphi大富翁论坛http://www.delphibbs.com
作者:
男 delphiqiw (delphi我不会) ▲▲▲△△ -
普通会员
2020/11/26 19:19:10
5楼: d(ŐдŐ๑)
----------------------------------------------
-
作者:
男 vga (vga) ★☆☆☆☆ -
盒子活跃会员
2020/11/27 8:32:59
6楼: 标记一下
----------------------------------------------
-
作者:
女 wfymqj (wfymqj) ★☆☆☆☆ -
盒子活跃会员
2020/11/27 8:39:32
7楼: 收藏学习,感谢分享!
----------------------------------------------
-
作者:
男 kenliaoliao (ben) ★☆☆☆☆ -
普通会员
2020/11/27 9:15:20
8楼: 这个必须顶上去。
----------------------------------------------
-
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2020/11/27 10:04:08
9楼: 标记学习,上个DEMO能测试下就更完美了。
----------------------------------------------
简单做人,认真做事。
作者:
男 yxsoft (yxsoft) ★☆☆☆☆ -
盒子活跃会员
2020/11/27 13:34:07
10楼: 牛,有空试下,还没搞过这块,不过很有用
----------------------------------------------
Great!
作者:
男 hbug (hbug) ★☆☆☆☆ -
普通会员
2020/11/27 14:45:35
11楼: 收藏起来。
----------------------------------------------
专业提供人力资源软件、指纹考勤软件、POS进销存软件、酒店餐饮软件
http://www.winsoftcn.com
作者:
男 zhahongyi (如风) ★☆☆☆☆ -
普通会员
2020/11/27 20:24:18
12楼: 标记下,谢谢楼主的付出!
----------------------------------------------
-
作者:
男 abea (abea) ★☆☆☆☆ -
盒子活跃会员
2020/11/29 11:28:18
13楼: 感谢分享,顶上去。
----------------------------------------------
-
作者:
男 szyourname (szyourname) ★☆☆☆☆ -
盒子活跃会员
2020/11/29 13:11:33
14楼: 感谢分享
----------------------------------------------
-
作者:
男 xjia (xjia) ★☆☆☆☆ -
盒子活跃会员
2020/11/29 15:32:34
15楼: 感谢楼主分享~~
----------------------------------------------
-
作者:
男 star5 (星五) ★☆☆☆☆ -
盒子活跃会员
2020/11/29 20:24:43
16楼: 感谢楼主分享,我也在论坛里分享了微信的;
而支付宝和钉钉的,我是用其它语言实现,给DELPHI调用,因为我个人不太喜欢用DLL,而DEPLHI在算法这块资源太少了。
----------------------------------------------
博客 - http://offeu.com
脚本模型 - http://webpascal.com
需要短信接口的请联系我,可发行业与营销内容。
作者:
男 sxqwhxq (步惊云) ★☆☆☆☆ -
普通会员
2020/11/30 10:09:33
17楼: 如果用于手机开发,建议用FMX结合html5/javacscript或android studio,可以更好地实现支付功能。
----------------------------------------------
-
作者:
男 abencat (远离delphi的人) ★☆☆☆☆ -
盒子活跃会员
2020/11/30 15:10:10
18楼: markdown
----------------------------------------------
delphi爱好者
作者:
男 janker (janker) ★☆☆☆☆ -
盒子活跃会员
2020/11/30 19:40:41
19楼: 感谢楼主分享~~
----------------------------------------------
-
作者:
男 luckyrandom (luckyrandom) ★☆☆☆☆ -
普通会员
2020/12/1 12:56:02
20楼: 收藏。。感谢
----------------------------------------------
SQL SERVER DBA QQ:315054403 曾经的Delphier  缘在上海
作者:
男 yagzh (心不了情) ★☆☆☆☆ -
盒子活跃会员
2020/12/3 9:00:32
21楼: 感謝
----------------------------------------------
-
作者:
男 hean (hean) ★☆☆☆☆ -
普通会员
2021/2/3 17:19:18
22楼: 感谢楼主的分享。
----------------------------------------------
-
作者:
男 ksrsoft (cb168) ★☆☆☆☆ -
普通会员
2021/2/4 13:12:07
23楼: 支持感谢分享
----------------------------------------------
-
作者:
男 sunshinedelphi (sunshine) ★☆☆☆☆ -
盒子活跃会员
2021/2/5 11:52:42
24楼: 支持感谢分享
----------------------------------------------
-
作者:
男 delphisyy (小苏) ▲▲▲▲▲ -
普通会员
2021/6/18 14:30:36
25楼: 最重要的内容,微信退款需要加载证书怎么弄
----------------------------------------------
-
作者:
男 synnefo (Syn) ▲▲▲▲△ -
普通会员
2021/6/21 21:39:40
26楼: Windows操作系统下,直接安装证书就行了
使用TNetHTTPClient组件,注意研究一下TNetHTTPClient.OnNeedClientCertificate这个方法
----------------------------------------------
-
作者:
男 synnefo (Syn) ▲▲▲▲△ -
普通会员
2021/6/21 21:44:26
27楼: 无DLL的1024位Alipay签名,需要使用 CryptoBlackBox 组件
----------
unit Alipay.Signature;

interface

uses
  System.SysUtils, System.Classes, SBPublicKeyCrypto, SBConstants;

/// <summary>
///   支付宝签名
/// </summary>
function Sign(AParams, APrivateKey: string): string;

/// <summary>
///   支付宝验签
/// </summary>
function CheckSign(AParams, APublicKey, ASign: string): Boolean;

implementation

function Sign(AParams, APrivateKey: string): string;
var
  Pvkey: string;
  Crypto: TElRSAPublicKeyCrypto;
  KeyMaterial: TELRSAKeyMaterial;
  PrivateKeyStream: TStringStream;
  InputBuffer, SignBuffer: TMemoryStream;
  strIn, strSign: Ansistring;
begin
  Pvkey:= '-----BEGIN RSA PRIVATE KEY-----' + #13#10 +
          Trim(APrivateKey) + #13#10 +
          '-----END RSA PRIVATE KEY-----';
  PrivateKeyStream := TStringStream.Create(Pvkey.Trim, TEncoding.ASCII);
  Crypto := TElRSAPublicKeyCrypto.Create();
  KeyMaterial := TELRSAKeyMaterial.Create;
  InputBuffer := TMemoryStream.Create;
  SignBuffer := TMemoryStream.Create;
  try
    Crypto.InputEncoding := pkeBinary; // 指定需要签字的内容的编码格式:pkeBinary或pkeBase64
    Crypto.OutputEncoding := pkeBase64; // 指定输出结果的编码格式
    Crypto.HashAlgorithm := SB_ALGORITHM_DGST_SHA256; // 签字的HASH格式:这里选择SHA256

    KeyMaterial.LoadSecret(PrivateKeyStream); // 装载私钥
    Crypto.KeyMaterial := KeyMaterial; // 关联

    strIn := Ansistring(AParams); // 需要签字的内容
    strIn := AnsiToUtf8(strIn);   // 使用UTF8模式
    InputBuffer.Write(strIn[1], Length(strIn));
    InputBuffer.Position := 0;

    Crypto.SignDetached(InputBuffer, SignBuffer); // 签字
    SignBuffer.Position := 0;

    SetLength(strSign, SignBuffer.Size);
    SignBuffer.Read(strSign[1], SignBuffer.Size);
    Result:= String(strSign);
  finally
    FreeAndNil(PrivateKeyStream);
    FreeAndNil(Crypto);
    FreeAndNil(KeyMaterial);
    FreeAndNil(InputBuffer);
    FreeAndNil(SignBuffer);
  end;
end;

function CheckSign(AParams, APublicKey, ASign: string): Boolean;
var
  Pbkey: string;
  Crypto: TElRSAPublicKeyCrypto;
  KeyMaterial: TELRSAKeyMaterial;
  PublicKeyStream: TStringStream;
  InputBuffer, SignBuffer: TMemoryStream;
  strIn, strSign: Ansistring;

  R: TSBPublicKeyVerificationResult;
begin
  Pbkey:= '-----BEGIN RSA PUBLIC KEY-----' + #13#10 +
          Trim(APublicKey) + #13#10 +
          '-----END RSA PUBLIC KEY-----';
  PublicKeyStream := TStringStream.Create(Pbkey.Trim, TEncoding.ASCII);

  Crypto := TElRSAPublicKeyCrypto.Create();
  KeyMaterial := TELRSAKeyMaterial.Create;
  InputBuffer := TMemoryStream.Create;
  SignBuffer := TMemoryStream.Create;
  try
    Crypto.InputEncoding := pkeBase64; // 指定需要签字的内容的编码格式:pkeBinary或pkeBase64
    Crypto.HashAlgorithm := SB_ALGORITHM_DGST_SHA256; // 签字的HASH格式:这里选择SHA256

    // if KeyMaterial is TELRSAKeyMaterial then TELRSAKeyMaterial(KeyMaterial).Passphrase := '';
    KeyMaterial.LoadPublic(PublicKeyStream); // 装载公钥
    Crypto.KeyMaterial := KeyMaterial; // 关联

    strIn := AnsiString(AParams); // 需要签字的内容
    strIn := AnsiToUtf8(strIn);   // 使用UTF8模式
    InputBuffer.Write(strIn[1], Length(strIn));
    InputBuffer.Position := 0;

    strSign:= AnsiString(ASign);
    strSign := AnsiToUtf8(strSign);
    SignBuffer.Write(strSign[1], Length(strSign));
    SignBuffer.Position := 0;

    R:= Crypto.VerifyDetached(InputBuffer, SignBuffer); // 验证

    Result:= R = pkvrSuccess;
  finally
    FreeAndNil(PublicKeyStream);
    FreeAndNil(Crypto);
    FreeAndNil(KeyMaterial);
    FreeAndNil(InputBuffer);
    FreeAndNil(SignBuffer);
  end;
end;


end.
----------------------------------------------
-
作者:
男 ksrsoft (cb168) ★☆☆☆☆ -
普通会员
2021/6/22 12:32:05
28楼:
----------------------------------------------
-
作者:
男 lsuper (lsuper) ★☆☆☆☆ -
盒子活跃会员
2021/6/22 13:38:28
29楼: 大赞!一直基于 SBB SecureBlackbox,也支持跨平台 ~
----------------------------------------------
-
作者:
男 sxqwhxq (步惊云) ★☆☆☆☆ -
普通会员
2021/6/22 17:11:51
30楼: 学习学习
----------------------------------------------
-
作者:
男 magiewang (magiewang) ▲▲▲△△ -
普通会员
2021/7/7 15:11:35
33楼: 这个要能封装成一个控件就好了,学习了,谢谢楼主。
----------------------------------------------
-
作者:
男 ksrsoft (cb168) ★☆☆☆☆ -
普通会员
2021/7/8 13:52:05
34楼: 支持
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行195.3125毫秒 RSS