DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: fhc2004
今日帖子: 17
在线用户: 15
导航: 论坛 -> Web应用开发 斑竹:bodies  
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/16 11:56:36
标题:
支付宝签名不一致的问题 浏览:6071
加入我的收藏
楼主: Delphi2007,从网上找来的这样两个函数
function TMainForm.LoadPrivateKey(filename:string ): PEVP_PKEY;
var bp: PBIO;
  A, pkey: PEVP_PKEY;
begin
  a:=nil;
  bp := BIO_new(BIO_s_file()) ;
  BIO_read_filename(bp, PChar(filename));
  pkey := PEM_read_bio_PrivateKey(bp, a, nil,NIL);
  BIO_free(bp);
  Result:= pkey;
end;

function TMainForm.Sign(filename: string; msg: String): String;
var ctx: EVP_MD_CTX;
  buf_in: PAnsiChar;
  m_len, outl: cardinal;
  pkey: PEVP_PKEY;
  m, buf_out: array [0..1024] of AnsiChar;
  p: array [0..255] of AnsiChar;
  i: Integer;
begin
  buf_out := '';
  if filename = '' then
  begin
    Result := '';
    Exit;
  end;
  pKey := LoadPrivateKey(filename);
  buf_in := PAnsiChar(msg);
  EVP_MD_CTX_init(@ctx);          //初始化
  EVP_SignInit(@ctx,EVP_sha1());    //将需要使用的摘要算法存入ctxl中
  EVP_SignUpdate(@ctx,buf_in,Length(buf_in));//存入编码值
  EVP_DigestFinal(@ctx,m,m_len);    //求取编码的长度为m_len摘要值存入m中
  RSA_sign(EVP_sha1()._type,m,m_len,buf_out,@outl,pkey.pkey.rsa); //64为SHA1的NID
  EVP_MD_CTX_cleanup(@ctx);
  Result := EncodeString(StrPas(buf_out));
end;
和对应的libeay32.pas、libeay32.dll和ssleay32.dll,并在libeay32.pas中添加了相应的函数,把Indy升级到10.5.8,现在可以计算签名了,问题是有的字符串能计算对,有的字符串计算不对,对与错的比例在7:3左右,并且错的字符串和对的字符串前边的字符是完全一样的,只是错的字符串比对的字符串要短一些。如:
对的是mmfA7WgjdfS6xy2mJzXydNsrL8Z9cmLXDpV5YkVDl2ZLsWl22qkomrKnLmyP+/du0lyBE034OqgwaXURQ5HWUBl/qa2xafUW1EzFTIK5VXO59fI1gwBnyNS9hbjfUriWPsMGDWEeNyZs57L9nTa7IMmlGjrXwXJWpStl59jrf2w=
错的是mmfA7WgjdfS6xy2mJzXydNsrL8Z9cmLXDpV5YkVDl2ZLsWl22qkomrKnLmyP+/du0lyBE034OqgwaXURQ5HWUBl/qa2xafUW1EzFTIK5VXO59fI1gw==
初步分析应该是编码的问题,可是把要签名的字符串赋值给memo或者写到文件中再取出来都解决不了这种问题,有没有人遇到类似的情况呢
----------------------------------------------
-
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/16 12:17:30
1楼: 签名对的截图
此帖子包含附件:
JPEG 图像
大小:245.5K
----------------------------------------------
-
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/16 12:17:58
2楼: 签名不对的截图
此帖子包含附件:
JPEG 图像
大小:205.9K
----------------------------------------------
-
作者:
男 olddelphier (oldDelphier) ▲▲▲▲△ -
普通会员
2016/2/16 12:37:48
3楼: 用那个dll,签名不对经常遇到
同样的程序,大概1/3的机会,不知道原因

我是循环调用,直到遇到签名正确的
----------------------------------------------
-
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/16 13:17:09
4楼: 谢谢回复!我发现与需要签名的字符串有关,如果某个字符串的签名能计算对,则每次计算都是对的,也就是说同一个字符串每次计算出来的结果都是一样的
----------------------------------------------
-
作者:
男 olddelphier (oldDelphier) ▲▲▲▲△ -
普通会员
2016/2/16 13:34:50
5楼: 同样的字符集串,计算结果是一样的,不管签名正确或不正确
签名不正确,其他不变,就把timestamp修改一下,重新计算,就有可能正确了
----------------------------------------------
-
作者:
男 letianwuji (大器晚成) ▲▲▲▲▲ -
普通会员
2016/2/16 15:20:24
6楼: 问题核心:
1,是否存在提交跟上一次内容一致的内容
比如,随机函数,是可以两次都是2的,严格算伪随机算法;

2,时区判定
像时间戳的验证,多少为了基于地域安全判定——主要是跨国业务
通常会时间戳审核一次时间区

3,字符编码
有些函数的编码,对某些字符-单字节与双字节处理是有差异的。
遇到过AS3能识别的json,而用delphi直接提示有非法字符。
----------------------------------------------
相信自己,若自己都不相信,那还有谁可信。
作者:
男 letianwuji (大器晚成) ▲▲▲▲▲ -
普通会员
2016/2/16 15:21:31
7楼: 目测,可能是+/ 一类的非字母字符引发的。
----------------------------------------------
相信自己,若自己都不相信,那还有谁可信。
作者:
男 olddelphier (oldDelphier) ▲▲▲▲△ -
普通会员
2016/2/16 20:45:39
8楼: 支付宝客户的答复:

没有delphi程序员,没有delphi例程,也没人懂delphi
自己找原因

那么大一个公司,外包找人写个demo难道要很多钱吗?
----------------------------------------------
-
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/16 21:21:17
9楼: Delphi自从在2003年在国内的使用人数达到顶峰以后就开始每况愈下了,眼下还使用Delphi的估计绝大多数都是Old Delphier了,Delphi虽然新版本不断,但是却再也达不到以前辉煌的程度了,以前总是说聪明的程序员用Delphi.现在也不知道聪明的程序员用什么了
谢谢olddelphier,我决定暂时也使用您的那种方案,用一个二重循环来解决,一个是生成签名后先判断一下签名的长度,如果小于172就重新生成交易字符串和签名,如果支付宝返回无效的签名,就把整个过程重做一遍直到成功
出现这种问题的原因应该是libeay32.dll中的函数有缺陷,Delphi这种签名方法还只有0.9.8版的支持这种方法,返回的签名也不能直接用,需要赋值给memo,在通过memo.lines[0]+memo.lines[1]+memo.lines[2]得到的才可以用
----------------------------------------------
-
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 21:37:01
10楼:  uses IdHMACSHA1,IdGlobal;
用TIdHMACSHA1看看,这个签名算法Indy10实现了得.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 21:37:51
11楼: http://www.zystudios.cn/blog/post/65.Shtml
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 21:45:22
12楼: http://bbs.csdn.net/topics/390475548
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 21:54:21
13楼: XE8 下可以用System.Hash原生支持.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 delphiteacher (delphiteacher) ▲▲▲▲▲ -
普通会员
2016/2/16 22:40:45
14楼: 我在做IOS的支付宝支付的时候,也需要用到RSA签名,用的是SSL里面的函数,没问题
----------------------------------------------
欢迎加入OrangeUI的app开发阵营:http://www.orangeui.cn/,联系QQ452330643
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 23:24:07
15楼: openssl也是原生支持HMACSHA1的.
http://blog.csdn.net/yasi_xi/article/details/9066003
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 23:26:34
16楼: 这个例子更简单http://my.oschina.net/bobwei/blog/529776
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/16 23:27:46
17楼: IdSSLOpenSSLHeaders
----------------------------------------------
按此在新窗口浏览图片
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/17 12:33:00
18楼: 谢谢各位的支持!有些代码在Delphi2007下运行不了,上午修改了EncdDecd.pas中的procedure EncodeStream(Input, Output: TStream);屏蔽掉了一小段代码,现在签名可以直接使用了
procedure EncodeStream(Input, Output: TStream);
type
  PInteger = ^Integer;
var
  InBuf: array[0..509] of Byte;
  OutBuf: array[0..1023] of Char;
  BufPtr: PChar;
  I, J, K, BytesRead: Integer;
  Packet: TPacket;
begin
  K := 0;
  repeat
    BytesRead := Input.Read(InBuf, SizeOf(InBuf));
    I := 0;
    BufPtr := OutBuf;
    while I < BytesRead do
    begin
      if BytesRead - I < 3 then
        J := BytesRead - I
      else J := 3;
      Packet.i := 0;
      Packet.b0 := InBuf[I];
      if J > 1 then
        Packet.b1 := InBuf[I + 1];
      if J > 2 then
        Packet.b2 := InBuf[I + 2];
      EncodePacket(Packet, J, BufPtr);
      Inc(I, 3);
      Inc(BufPtr, 4);
      Inc(K, 4);
      {if K > 75 then   //2016-02-17 
      begin
        BufPtr[0] := #$0D;
        BufPtr[1] := #$0A;
        Inc(BufPtr, 2);
        K := 0;
      end;}   //2016-02-17 
    end;
    Output.Write(Outbuf, BufPtr - PChar(@OutBuf));
  until BytesRead = 0;
end;
现在遇到的问题是出错的签名比和正确的签名长度短,出错签名中的字符和正确签名中的是完全一样的.
TO delphiteacher:能不能把您使用的libeay32.dll和ssleay32.dll这两个文件发给我试一下呢 QQ:626323762
----------------------------------------------
-
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 19:54:31
19楼: 找到一个纯delphi实现的.https://github.com/fundamentalslib/fundamentals5.git
在flcHash.pas里实现了HMACSHA1
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 20:28:18
20楼: 楼主你的函数是不正确的.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 20:52:32
21楼: 这是windows API版.
function Hashhmacsha1(const Key, Value: AnsiString): AnsiString;
const
  KEY_LEN_MAX = 16;
var
  hCryptProvider: HCRYPTPROV;
  hHash: HCRYPTHASH;
  hKey: HCRYPTKEY;
  bHash: array[0..$7F] of Byte;
  dwHashLen: dWord;
  i: Integer;

  hPubKey : HCRYPTKey;
  hHmacHash: HCRYPTHASH;
  bHmacHash: array[0..$7F] of Byte;
  dwHmacHashLen: dWord;
  hmac_info : Wcrypt2.HMAC_INFO;

  keyBlob: record
    keyHeader: BLOBHEADER;
    keySize: DWORD;
    keyData: array[0..KEY_LEN_MAX-1] of Byte;
  end;
  keyLen : INTEGER;
begin
  dwHashLen := 32;
  dwHmacHashLen := 32;
  {get context for crypt default provider}
  if CryptAcquireContext(@hCryptProvider, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
  begin
    {create hash-object MD5}
    if CryptCreateHash(hCryptProvider, CALG_SHA1, 0, 0, @hHash) then
    begin

      {get hash from password}
      if CryptHashData(hHash, PByte(Key), Length(Key), 0) then
      begin

        // hHash is now a hash of the provided key, (SHA1)
        // Now we derive a key for it
        hPubKey := 0;

        FillChar(keyBlob, SizeOf(keyBlob), 0);
        keyBlob.keyHeader.bType := PLAINTEXTKEYBLOB;
        keyBlob.keyHeader.bVersion := CUR_BLOB_VERSION;
        keyBlob.keyHeader.aiKeyAlg := CALG_RC4;
        KeyBlob.keySize := KEY_LEN_MAX;

        if(Length(key) < (KEY_LEN_MAX))then
          KeyLen := Length(key)
        else
          KeyLen := KEY_LEN_MAX;
        Move(Key[1], KeyBlob.keyData[0], KeyLen );

        if CryptImportKey(hCryptProvider, @keyBlob, SizeOf(KeyBlob), hPubKey, 0, @hKey) then
        begin

          //hkey now holds our key. So we have do the whole thing over again
          ZeroMemory( @hmac_info, SizeOf(hmac_info) );
          hmac_info.HashAlgid := CALG_SHA1;
          if CryptCreateHash(hCryptProvider, CALG_HMAC, hKey, 0, @hHmacHash) then
          begin
          if CryptSetHashParam( hHmacHash, HP_HMAC_INFO, @hmac_info, 0) then
          begin

          if CryptHashData(hHmacHash, @Value[1], Length(Value), 0) then
          begin
          if CryptGetHashParam(hHmacHash, HP_HASHVAL, @bHmacHash[0], @dwHmacHashLen, 0) then
          begin
          for i := 0 to dwHmacHashLen-1 do
          Result := Result + IntToHex(bHmacHash[i], 2);
          end
          else
          WriteLn( 'CryptGetHashParam ERROR --> ' + SysErrorMessage(GetLastError)) ;
          end
          else
          WriteLn( 'CryptHashData ERROR --> ' + SysErrorMessage(GetLastError)) ;
          {destroy hash-object}
          CryptDestroyHash(hHmacHash);
          CryptDestroyKey(hKey);
          end
          else
          WriteLn( 'CryptSetHashParam ERROR --> ' + SysErrorMessage(GetLastError)) ;

          end
          else
          WriteLn( 'CryptCreateHash ERROR --> ' + SysErrorMessage(GetLastError)) ;
        end
        else
          WriteLn( 'CryptDeriveKey ERROR --> ' + SysErrorMessage(GetLastError)) ;

      end;
      {destroy hash-object}
      CryptDestroyHash(hHash);
    end;
    {release the context for crypt default provider}
    CryptReleaseContext(hCryptProvider, 0);
  end;
  Result := AnsiLowerCase(Result);
end;
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 21:01:50
22楼: 我个人建议你直接调用系统的API进行签名.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 21:13:44
23楼: 刚才看了下支付宝的开发文档签名算法是HMAC的,建议你直接调用系统API或者Openssl的HMAC接口.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 ly123 (ly123) ★☆☆☆☆ -
盒子活跃会员
2016/2/17 22:37:19
24楼: 衷心感谢 xuchuantao 的热心帮助,我会根据您的建议去试一下,还有一种方法就是利用支付宝的Demo中的方法用C#写了一个计算签名的dll,调用这个dll也能计算签名
  ole_rsa := CreateOleObject('ClassLibrary2.TestClass');
  rsa_sign := ole_rsa.RSASignCharSet(liststr_str,'rsa_private_key.pem','utf-8');
但是这种方法需要在客户的机器上注册这个dll,并且还要求装有高版本的Framework
----------------------------------------------
-
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 22:44:21
25楼: 如果要用快捷支付.可以使用tplockbox或者RAPWare EasyCrypt这两个控件组.这两个都是可以进行RSA的签名操作的.系统CryptSignHash这个API也可以进行签名操作.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/17 22:52:00
26楼: 刚才看了下fundamentalslib的代码也是可以进行RSA的签名的,不过推荐使用RAPWare EasyCrypt他是对系统CryptoAPI加密接口的封装,RSA签名微软是已经实现了的.例子在控件里有.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 138soft (138soft) ★☆☆☆☆ -
盒子活跃会员
2016/2/23 18:50:54
27楼: 上次也有个朋友来问这个问题。后来我用SecureBlackbox花了3分钟帮他解决了。免DLL。
----------------------------------------------
是你上错了车,还是我下错了站?
作者:
男 138soft (138soft) ★☆☆☆☆ -
盒子活跃会员
2016/2/23 18:55:01
28楼: 其实RSA加密是有一定标准的。例如C#之类的,加密后的内容肯定等于密钥位数除以8。不知道是否是明文规定。JAVA之类的,对数字证书则有一定要求,私钥的证书,ASN.1编码的时候前面需要再加两个Item---虽然没什么用,但你不加它就读取失败。
----------------------------------------------
是你上错了车,还是我下错了站?
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2016/2/23 23:24:40
29楼: 能共享一下SecureBlackbox吗谢谢.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 rmmis (rmmis) ★☆☆☆☆ -
普通会员
2016/3/25 20:01:52
30楼: UP
----------------------------------------------
-
作者:
男 eastroads (eastroads) ★☆☆☆☆ -
普通会员
2016/3/27 0:34:45
31楼: 很多热心的朋友,赞你们!
----------------------------------------------
-
作者:
女 blbz (冰力不足) ★☆☆☆☆ -
禁用账号
2016/4/6 18:16:17
32楼: ……
被禁用帐号,帖子内容自动屏蔽!
……

----------------------------------------------
SPAM
作者:
男 rmmis (rmmis) ★☆☆☆☆ -
普通会员
2016/4/23 22:58:22
33楼: UP
----------------------------------------------
-
作者:
男 adden_lian (寻秦冷雨夜) ★☆☆☆☆ -
盒子活跃会员
2016/5/13 13:49:08
34楼: 上面一些知道的人不说,弄了几天总算好了,现贴上代码:
function Sign(filename,msg : String):string;  
    var  
         ctx : EVP_MD_CTX   ;  
         buf_in:Pchar;  
         m_len : Cardinal;
         outl : Cardinal;
         pKey : PEVP_PKEY;
         m,buf_out:array   [0..1024]   of   char;
         i:Integer;

         s: TStringStream;
         d: TStringStream;
         iKeyLen: Integer;  
     begin  
     //buf_out:='';
     if filename='' then  
     begin
       Result:='';  
       Exit;  
     end;  
       pKey := LoadPrivateKey(filename);
       buf_in := PChar(msg);
       EVP_MD_CTX_init(@ctx);          //初始化
       EVP_SignInit(@ctx,EVP_sha1());    //将需要使用的摘要算法存入ctxl中
       EVP_SignUpdate(@ctx,buf_in,Length(buf_in));//存入编码值
       EVP_DigestFinal(@ctx,m,m_len);    //求取编码的长度为m_len摘要值存入m中
       rSA_sign(EVP_sha1()._type,m,m_len,buf_out,@outl,pkey.pkey.rsa); //64为SHA1的NID
       EVP_MD_CTX_cleanup(@ctx);

       s := TStringStream.Create('');
       d := TStringStream.Create('');

       iKeyLen := PCardinal(@outl)^;
       try
         s.WriteBuffer(buf_out, iKeyLen);
         s.Position := 0;
         EncodeStream(s, d);
         Result := d.DataString;
       finally
         s.Free;
         d.Free;
       end;

    end;
----------------------------------------------
-
作者:
男 olddelphier (oldDelphier) ▲▲▲▲△ -
普通会员
2016/5/13 14:28:57
35楼: 多谢老兄
用你的这个代码感觉成功率很高,试了10来次都没有出现那个签名错误的提示了
用以前网上下载的基本上3成会签名错误,要循环使用才能达到9成5以上成功率
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行101.5625毫秒 RSS