使用delphi+intraweb进行微信开发4—微信消息加解密

栏目: 编程语言 · 发布时间: 6年前

内容简介:使用delphi+intraweb进行微信开发4—微信消息加解密

在上一讲当中我做了个简单的微信文本消息回显应用,当时是以微信明文方式实现的,其实微信推荐的是消息应该加密传输以增加安全性,所以这讲说说微信消息的加解密。

在微信的 帮助页面 上可以下载微信消息加解密的例程,可惜的是没有Delphi语言的示例,网上搜索一番,没有人贡献出写好的Delphi版的微信加解密算法单元,好在有官方示例的C#版的,那就按照C#的改一个吧。

微信消息是以AES算法进行的加密处理,而遗憾的是Delphi并没有内置的AES算法单元,必须找第三方实现的,不过一般第三方实现的算法都因为种种原因并不完善,需要使用者酌情修改,所以在基础算法支持上Delphi确实和.net以及 java 这类的开发语言比不了。

呵呵,上网找Delphi版AES算法吧。在在这里要感谢 cnpack开发组 ,他们不但推出一流的delphi开发环境增强组件还有开源组件包cnvcl,这个组件包中有SHA1、AES、MD5等多种算法单元,我打开AES算法单元查看,发现封装的很完美,ECB、CBC模式均支持,呵呵,幸福了 使用delphi+intraweb进行微信开发4—微信消息加解密

参考C#示例代码一通修改测试,省略几昼夜苦干的吐槽终于开花结果:

呵呵,这个或者是网上目前唯一的开源的Delphi版的微信加解密算法单元吧,激动!

{*****************************************************************************}
{                                                                             }
{       泛思微平台                                                            }
{                                                                             }
{       版权所有 (C) 2016 石家庄泛思电子商务有限公司                          }
{                                                                             }
{       微信消息加密解密单元,使用DelphiXE2版本编写                           }
{       作者:Delphi力量                                                      }
{       QQ:404328970                                                         }
{       EMail: heblxy@163.com                                                 }
{       Blog:www.cnblogs.com/dpower                                          }
{       参考链接:                                                            }
{       http://mp.weixin.qq.com/wiki/14/70e73cedf9fd958d2e23264ba9333ad2.html }
{                                                                             }
{*****************************************************************************}
unit   uWxMsgCrypt ;
interface
uses
  System . Classes ,   System . SysUtils ;
type
  WXBizMsgCryptErrorCode   =   (
    WXBizMsgCrypt_OK   =   0 ,
    WXBizMsgCrypt_ValidateSignature_Error   =   - 40001 ,
    WXBizMsgCrypt_ParseXml_Error   =   - 40002 ,
    WXBizMsgCrypt_ComputeSignature_Error   =   - 40003 ,
    WXBizMsgCrypt_IllegalAesKey   =   - 40004 ,
    WXBizMsgCrypt_ValidateAppid_Error   =   - 40005 ,
    WXBizMsgCrypt_EncryptAES_Error   =   - 40006 ,
    WXBizMsgCrypt_DecryptAES_Error   =   - 40007 ,
    WXBizMsgCrypt_IllegalBuffer   =   - 40008 ,
    WXBizMsgCrypt_EncodeBase64_Error   =   - 40009 ,
    WXBizMsgCrypt_DecodeBase64_Error   =   - 40010
  );
  /// <summary>
  /// 提供微信加密消息解密和微信明文消息加密功能
  /// </summary>
  TWxMsgCrypt   =   class
  private
    function   CreateRandCode ( codeLen :   Integer ):   string ;
    function   AES_decrypt ( const   sEncodingAESKey ,   sMsgEncrypt :   string ;   var   cpid :   string ):   string ;
    function   AES_encrypt ( const   sEncodingAESKey ,   sMsg ,   cpid :   string ):   string ;
  public
    /// <summary>
    /// 解密微信消息
    /// </summary>
    /// <param name="sToken">Token,看公众号设置</param>
    /// <param name="sTimeStamp">时间戳,随微信消息一起传入,可以通过Url参数获取</param>
    /// <param name="sNonce">随机字符串,随微信消息一起传入,可以通过Url参数获取</param>
    /// <param name="sMsgEncrypt">微信消息xml的Encrypt字段内容</param>
    /// <param name="sSigture">签名,随微信消息一起传入,可以通过Url参数获取</param>
    /// <param name="sAppID">AppID,看公众号设置</param>
    /// <param name="sEncodingAESKey">EncodingAESKey,看公众号设置</param>
    /// <param name="sMsg">sMsg: 解密后的Encrypt字段内容原文,当return返回WXBizMsgCrypt_OK时有效</param>
    /// <returns>成功WXBizMsgCrypt_OK,失败返回对应的错误码</returns>
    function   DecryptMsg ( const   sToken ,   sTimeStamp ,   sNonce ,   sMsgEncrypt ,   sSigture ,
      sAppID ,   sEncodingAESKey :   string ;   var   sMsg :   string ):   WXBizMsgCryptErrorCode ;
    /// <summary>
    /// 加密微信消息
    /// </summary>
    /// <param name="sMsg">全部xml内容(明文)</param>
    /// <param name="sAppID">AppID,看公众号设置</param>
    /// <param name="sEncodingAESKey">EncodingAESKey,看公众号设置</param>
    /// <param name="sMsgEncrypt">sMsgEncrypt: 输出的是加密后的全部xml(可以直接发送无需再编组xml),当return返回WXBizMsgCrypt_OK时有效</param>
    /// <returns>成功WXBizMsgCrypt_OK,失败返回对应的错误码</returns>
    function   EncryptMsg ( const   sMsg ,   sToken ,   sAppID ,   sEncodingAESKey :   string ;
      var   sMsgEncrypt :   string ):   WXBizMsgCryptErrorCode ;
  end ;
implementation
uses
  CnSHA1 ,   EncdDecd ,   CnAES ,   System . Math ,   uWxGlobal ;
{ TWxMsgCrypt }
function   TWxMsgCrypt . AES_decrypt ( const   sEncodingAESKey ,   sMsgEncrypt :   string ;   var   cpid :   string ):   string ;
var
  aEncodingAESKeyStr ,   sInput :   AnsiString ;
  aEncodingAESKeyBts ,   IvBts ,   InputBts :   TBytes ;
  InputStream ,   DecodeStream :   TMemoryStream ;
  AesKey :   TAESKey256 ;
  Iv :   TAESBuffer ;
  P :   PByteArray ;
  iLen ,   iDecodeDataLen :   Integer ;
  bMsg ,   bAppid :   TBytes ;
  function   GetRealDataLenWithoutKCS7Bytes :   Integer ;
  var
    lstBt :   Byte ;
    block_size ,   AllKCS7ByteCount :   Integer ;
  begin
    block_size   :=   32 ;
    lstBt   :=   P ^[ DecodeStream . Size   -   1 ];
    AllKCS7ByteCount   :=   block_size   -   ( block_size   -   Ord ( lstBt ));
    if   ( AllKCS7ByteCount   >   0 )   and   ( AllKCS7ByteCount   <   DecodeStream . Size )   then
    begin
      if   P ^[ DecodeStream . Size   -   AllKCS7ByteCount ]   =   lstBt   then
        Result   :=   DecodeStream . Size   -   AllKCS7ByteCount
      else
        Result   :=   DecodeStream . Size ;
    end   else
      Result   :=   DecodeStream . Size ;
  end ;
begin
  try
    aEncodingAESKeyStr   :=   AnsiString ( sEncodingAESKey   +   '=' );
    aEncodingAESKeyBts   :=   DecodeBase64 ( aEncodingAESKeyStr );
  except
    raise   Exception . Create ( '1' );
  end ;
  try
    SetLength ( IvBts ,   16 );
    Move ( aEncodingAESKeyBts [ 0 ],   IvBts [ 0 ],   16 );
    // aes.KeySize = 256; aes.BlockSize = 128; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None;
    sInput   :=   AnsiString ( sMsgEncrypt );
    InputBts   :=   DecodeBase64 ( sInput );
    InputStream   :=   TMemoryStream . Create ;
    DecodeStream   :=   TMemoryStream . Create ;
    try
      InputStream . Write ( InputBts [ 0 ],   Length ( InputBts ));
      Move ( aEncodingAESKeyBts [ 0 ],   AesKey ,   Length ( aEncodingAESKeyBts ));
      Move ( IvBts [ 0 ],   Iv ,   Length ( IvBts ));
      InputStream . Position   :=   0 ;
      DecryptAESStreamCBC ( InputStream ,   0 ,
        AesKey ,   Iv ,   DecodeStream );
      P   :=   PByteArray ( DecodeStream . Memory );
      iDecodeDataLen   :=   GetRealDataLenWithoutKCS7Bytes ;
      iLen   :=   P ^[ 16 ]   shl   24   +   P ^[ 17 ]   shl   16   +   P ^[ 18 ]   shl   8   +   P ^[ 19 ];
      SetLength ( bMsg ,   iLen );
      SetLength ( bAppid ,   iDecodeDataLen   -   20   -   iLen );
      Move ( P ^[ 20 ],   bMsg [ 0 ],   iLen );
      Move ( P ^[ 20   +   iLen ],   bAppid [ 0 ],   iDecodeDataLen   -   20   -   iLen );
      Result   :=   TEncoding . UTF8 . GetString ( bMsg );
      cpid   :=   TEncoding . UTF8 . GetString ( bAppid );
    finally
      InputStream . Free ;
      DecodeStream . Free ;
    end ;
  except
    raise   Exception . Create ( '2' );
  end ;
end ;
function   TWxMsgCrypt . AES_encrypt ( const   sEncodingAESKey ,   sMsg ,
  cpid :   string ):   string ;
var
  aEncodingAESKeyStr :   AnsiString ;
  aEncodingAESKeyBts ,   IvBts ,   bRand ,   bAppid ,   btmpMsg ,   bMsg ,   bMsgLen ,   msg ,   pad :   TBytes ;
  Randcode :   string ;
  AesKey :   TAESKey256 ;
  Iv :   TAESBuffer ;
  InputStream ,   OutputStream :   TMemoryStream ;
  function   CreateRandCode ( codeLen :   Integer ):   string ;
  var
    codeSerial ,   code :   string ;
    strLst :   TStringList ;
    randValue ,   i :   Integer ;
  begin
    codeSerial   :=   '2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z' ;
    if   ( codeLen   =   0 )   then
      codeLen   :=   16 ;
    strLst   :=   TStringList . Create ;
    try
      ExtractStrings ([ ',' ],   [],   PChar ( codeSerial ),   strLst );
      code   :=   '' ;
      Randomize ;
      for   i   :=   0   to   codeLen   -   1   do
      begin
        randValue   :=   Random ( strLst . Count );
        code   :=   code   +   strLst [ randValue ];
      end ;
      Result   :=   code ;
    finally
      strLst . Free ;
    end ;
  end ;
  function   KCS7Encoder ( text_length :   Integer ):   TBytes ;
  var
    block_size ,   amount_to_pad :   Integer ;
    pad_chr :   Char ;
    tmp :   string ;
    i :   Integer ;
  begin
    block_size   :=   32 ;
    // 计算需要填充的位数
    amount_to_pad   :=   block_size   -   ( text_length   mod   block_size );
    if   ( amount_to_pad   =   0 )   then
      amount_to_pad   :=   block_size ;
    // 获得补位所用的字符
    pad_chr   :=   Chr ( amount_to_pad );
    tmp   :=   '' ;
    for   i   :=   0   to   amount_to_pad   -   1   do
      tmp   :=   tmp   +   pad_chr ;
    Result   :=   BytesOf ( tmp );
  end ;
begin
  aEncodingAESKeyStr   :=   AnsiString ( sEncodingAESKey   +   '=' );
  aEncodingAESKeyBts   :=   DecodeBase64 ( aEncodingAESKeyStr );
  SetLength ( IvBts ,   16 );
  Move ( aEncodingAESKeyBts [ 0 ],   IvBts [ 0 ],   16 );
  Randcode   :=   CreateRandCode ( 16 );
  bRand   :=   TEncoding . UTF8 . GetBytes ( Randcode );
  bAppid   :=   TEncoding . UTF8 . GetBytes ( cpid );
  btmpMsg   :=   TEncoding . UTF8 . GetBytes ( sMsg );
  SetLength ( bMsgLen ,   4 );
  bMsgLen [ 0 ]   :=   ( Length ( btmpMsg )   shr   24 )   and   $FF ;
  bMsgLen [ 1 ]   :=   ( Length ( btmpMsg )   shr   16 )   and   $FF ;
  bMsgLen [ 2 ]   :=   ( Length ( btmpMsg )   shr   8 )   and   $FF ;
  bMsgLen [ 3 ]   :=   Length ( btmpMsg )   and   $FF ;
  SetLength ( bMsg ,   Length ( bRand )   +   Length ( bAppid )   +   Length ( btmpMsg )   +   Length ( bMsgLen ));
  Move ( bRand [ 0 ],   bMsg [ 0 ],   Length ( bRand ));
  Move ( bMsgLen [ 0 ],   bMsg [ Length ( bRand )],   Length ( bMsgLen ));
  Move ( btmpMsg [ 0 ],   bMsg [ Length ( bRand )   +   Length ( bMsgLen )],   Length ( btmpMsg ));
  Move ( bAppid [ 0 ],   bMsg [ Length ( bRand )   +   Length ( bMsgLen )   +   Length ( btmpMsg )],   Length ( bAppid ));
{$REGION '自己进行PKCS7补位'}
  SetLength ( msg ,   Length ( bMsg )   +   32   -   Length ( bMsg )   mod   32 );
  Move ( bMsg [ 0 ],   msg [ 0 ],   Length ( bMsg ));
  pad   :=   KCS7Encoder ( Length ( bMsg ));
  Move ( pad [ 0 ],   msg [ Length ( bMsg )],   Length ( pad ));
{$ENDREGION}
  //aes.KeySize = 256; aes.BlockSize = 128; aes.Padding = PaddingMode.None; aes.Mode = CipherMode.CBC;
  Move ( aEncodingAESKeyBts [ 0 ],   AesKey ,   Length ( aEncodingAESKeyBts ));
  Move ( IvBts [ 0 ],   Iv ,   Length ( IvBts ));
  InputStream   :=   TMemoryStream . Create ;
  OutputStream   :=   TMemoryStream . Create ;
  try
    InputStream . Write ( msg [ 0 ],   Length ( msg ));
    InputStream . Position   :=   0 ;
    EncryptAESStreamCBC ( InputStream ,   0 ,   AesKey ,   Iv ,   OutputStream );
    Result   :=   string ( EncodeBase64 ( OutputStream . Memory ,   OutputStream . Size ));
  finally
    InputStream . Free ;
    OutputStream . Free ;
  end ;
end ;
function   TWxMsgCrypt . CreateRandCode ( codeLen :   Integer ):   string ;
var
  codeSerial ,   code :   string ;
  strLst :   TStringList ;
  randValue ,   i :   Integer ;
begin
  codeSerial   :=   '2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z' ;
  if   ( codeLen   =   0 )   then
    codeLen   :=   16 ;
  strLst   :=   TStringList . Create ;
  try
    ExtractStrings ([ ',' ],   [],   PChar ( codeSerial ),   strLst );
    code   :=   '' ;
    Randomize ;
    for   i   :=   0   to   codeLen   -   1   do
    begin
      randValue   :=   Random ( strLst . Count );
      code   :=   code   +   strLst [ randValue ];
    end ;
    Result   :=   code ;
  finally
    strLst . Free ;
  end ;
end ;
function   TWxMsgCrypt . DecryptMsg ( const   sToken ,   sTimeStamp ,   sNonce ,   sMsgEncrypt ,   sSigture ,
  sAppID ,   sEncodingAESKey :   string ;   var   sMsg :   string ):   WXBizMsgCryptErrorCode ;
var
  ret :   WXBizMsgCryptErrorCode ;
  cpid :   string ;
  function   VerifySignature :   WXBizMsgCryptErrorCode ;
  var
    hash :   string ;
    aStr :   AnsiString ;
    AL :   TStringList ;
    i :   Integer ;
  begin
    AL   :=   TStringList . Create ;
    try
      AL . Add ( sToken );
      AL . Add ( sTimeStamp );
      AL . Add ( sNonce );
      AL . Add ( sMsgEncrypt );
      AL . Sort ;
      hash   :=   '' ;
      for   i   :=   0   to   AL . Count   -   1   do
        hash   :=   hash   +   AL [ i ];
      aStr   :=   AnsiString ( hash );
      hash   :=   LowerCase ( SHA1Print ( SHA1StringA ( aStr )));
    finally
      AL . Free ;
    end ;
    if   ( hash   =   sSigture )   then
      Result   :=   WXBizMsgCrypt_OK
    else
      Result   :=   WXBizMsgCrypt_ValidateSignature_Error ;
  end ;
begin
  sMsg   :=   '' ;
  if   ( Length ( sEncodingAESKey )   <>   43 )   then
  begin
    Result   :=   WXBizMsgCryptErrorCode . WXBizMsgCrypt_IllegalAesKey ;
    Exit ;
  end ;
  //verify signature
  ret   :=   VerifySignature ;
  if   ( ret   <>   WXBizMsgCrypt_OK )   then
  begin
    Result   :=   ret ;
    Exit ;
  end ;
  //decrypt
  cpid   :=   '' ;
  try
    sMsg   :=   AES_decrypt ( sEncodingAESKey ,   sMsgEncrypt ,   cpid );
  except
    on   E :   Exception   do
    begin
      if   E . Message   =   '1'   then
        Result   :=   WXBizMsgCrypt_DecodeBase64_Error
      else
        Result   :=   WXBizMsgCrypt_DecryptAES_Error ;
      Exit ;
    end ;
  end ;
  if   ( cpid   <>   sAppID )   then
  begin
    Result   :=   WXBizMsgCrypt_ValidateAppid_Error ;
    Exit ;
  end ;
  Result   :=   WXBizMsgCrypt_OK ;
end ;
function   TWxMsgCrypt . EncryptMsg ( const   sMsg ,   sToken ,   sAppID ,   sEncodingAESKey :   string ;
  var   sMsgEncrypt :   string ):   WXBizMsgCryptErrorCode ;
var
  hash ,   wxDt ,   wxNonce ,   EncryptField :   string ;
  function   GenSignature :   string ;
  var
    hash :   string ;
    aStr :   AnsiString ;
    AL :   TStringList ;
    i :   Integer ;
  begin
    AL   :=   TStringList . Create ;
    try
      AL . Add ( sToken );
      AL . Add ( EncryptField );
      wxDt   :=   GetWxNowStr ;
      AL . Add ( wxDt );
      wxNonce   :=   CreateRandCode ( 10 );
      AL . Add ( wxNonce );
      AL . Sort ;
      hash   :=   '' ;
      for   i   :=   0   to   AL . Count   -   1   do
        hash   :=   hash   +   AL [ i ];
      aStr   :=   AnsiString ( hash );
      hash   :=   LowerCase ( SHA1Print ( SHA1StringA ( aStr )));
    finally
      AL . Free ;
    end ;
    Result   :=   hash ;
  end ;
begin
  sMsgEncrypt   :=   '' ;
  if   ( Length ( sEncodingAESKey )   <>   43 )   then
  begin
    Result   :=   WXBizMsgCryptErrorCode . WXBizMsgCrypt_IllegalAesKey ;
    Exit ;
  end ;
  //encrypt
  try
    EncryptField   :=   AES_encrypt ( sEncodingAESKey ,   sMsg ,   sAppID );
  except
    on   E :   Exception   do
    begin
      if   E . Message   =   '1'   then
        Result   :=   WXBizMsgCrypt_DecryptAES_Error
      else
        Result   :=   WXBizMsgCrypt_EncryptAES_Error ;
      Exit ;
    end ;
  end ;
  //gen signature
  try
    hash   :=   GenSignature ;
  except
    Result   :=   WXBizMsgCrypt_ComputeSignature_Error ;
    Exit ;
  end ;
  //xml
  sMsgEncrypt   :=   '<xml><Encrypt><![CDATA['   +   EncryptField   +   ']]></Encrypt>'   +
    '<MsgSignature><![CDATA['   +   hash   +   ']]></MsgSignature>'   +
    '<TimeStamp><![CDATA['   +   wxDt   +   ']]></TimeStamp>'   +
    '<Nonce><![CDATA['   +   wxNonce   +   ']]></Nonce></xml>' ;
  Result   :=   WXBizMsgCrypt_OK ;
end ;
end .

呵呵,看看效果图吧:

使用delphi+intraweb进行微信开发4—微信消息加解密

使用delphi+intraweb进行微信开发4—微信消息加解密


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

乔纳森传

乔纳森传

利恩德·卡尼 / 汪琪 岳卉 王文雅 / 中信出版社 / 2014-1-1 / 49

抛开苹果公司,单就设计行业来讲,乔纳森也是一个特殊的人物。他推动了设计行业的大变革:不再为产品增加看起来炫得多的配件,而是要去掉多余的东西。 ——陈向东 终于有一本书能够如此地接地气:它不再关注那位神一样的乔布斯,而是关注那位站在神的背后,同样具有神一样光环的乔纳森。 ——孙陶然 乔纳森•艾夫把他自己对科学、人文、艺术,乃至整个世界的感知尽数渗透进苹果的设计和审美之中,他是......一起来看看 《乔纳森传》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具