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

示例代码已经放出!请移步使用delphi+intraweb进行微信开发1~4代码示例进行下载,虽为示例代码但是是从我项目中移出来的,封装很完备适于自行扩展和修改。

 

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

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

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

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

参考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.ClassesSystem.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(codeLenInteger): string
    function AES_decrypt(const sEncodingAESKeysMsgEncryptstringvar cpidstring): string
    function AES_encrypt(const sEncodingAESKeysMsgcpidstring): 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 sTokensTimeStampsNoncesMsgEncryptsSigture
      sAppIDsEncodingAESKeystringvar sMsgstring): 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 sMsgsTokensAppIDsEncodingAESKeystring
      var sMsgEncryptstring): WXBizMsgCryptErrorCode
  end

implementation 

uses 
  CnSHA1EncdDecdCnAESSystem.MathuWxGlobal

{ TWxMsgCrypt } 

function TWxMsgCrypt.AES_decrypt(const sEncodingAESKeysMsgEncryptstringvar cpidstring): string
var 
  aEncodingAESKeyStrsInputAnsiString
  aEncodingAESKeyBtsIvBtsInputBtsTBytes
  InputStreamDecodeStreamTMemoryStream
  AesKeyTAESKey256
  IvTAESBuffer
  PPByteArray
  iLeniDecodeDataLenInteger
  bMsgbAppidTBytes

  function GetRealDataLenWithoutKCS7BytesInteger
  var 
    lstBtByte
    block_sizeAllKCS7ByteCountInteger
  begin 
    block_size := 32
    lstBt := P^[DecodeStream.Size 1]; 
    AllKCS7ByteCount := block_size (block_size Ord(lstBt)); 
    if (AllKCS7ByteCount 0and (AllKCS7ByteCount DecodeStream.Sizethen 
    begin 
      if P^[DecodeStream.Size AllKCS7ByteCountlstBt 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(IvBts16); 
    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], AesKeyLength(aEncodingAESKeyBts)); 
      Move(IvBts[0], IvLength(IvBts)); 
      InputStream.Position := 0
      DecryptAESStreamCBC(InputStream0
        AesKeyIvDecodeStream); 
      := PByteArray(DecodeStream.Memory); 
      iDecodeDataLen := GetRealDataLenWithoutKCS7Bytes;       

iLen := P^[16 shl24 P^[17 shl16 P^[18 shlP^[19];       
SetLength(bMsgiLen);       
SetLength(bAppidiDecodeDataLen 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
 raiseException.Create('2');   
end
end

 functionTWxMsgCrypt.AES_encrypt( constsEncodingAESKeysMsg,   
cpidstring): string
   var
aEncodingAESKeyStrAnsiString;   
aEncodingAESKeyBtsIvBtsbRandbAppidbtmpMsgbMsgbMsgLenmsgpadTBytes;   
Randcodestring;   
AesKeyTAESKey256;   
IvTAESBuffer;   
InputStreamOutputStreamTMemoryStream;   

 functionCreateRandCode(codeLenInteger): string;   
     var
codeSerialcodestring;     
strLstTStringList;     
randValueiInteger;   
     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:=  tocodeLen        do
         begin
randValue := Random(strLst.Count);         
code := code strLst[randValue];       
end;       
Result := code;     
       finally
strLst.Free;     
end;   
end;   

 functionKCS7Encoder(text_lengthInteger): TBytes;   
     var
block_sizeamount_to_padInteger;     
pad_chrChar;     
tmpstring;     
iInteger;   
     begin
block_size := 32;     
     // 计算需要填充的位数
amount_to_pad := block_size (text_length  modblock_size);     
 if(amount_to_pad 0       then
amount_to_pad := block_size;     
     // 获得补位所用的字符
pad_chr := Chr(amount_to_pad);     
tmp := '';     
 for:=  toamount_to_pad        do
tmp := tmp pad_chr;     
Result := BytesOf(tmp);   
end

   begin
aEncodingAESKeyStr := AnsiString(sEncodingAESKey '=');   
aEncodingAESKeyBts := DecodeBase64(aEncodingAESKeyStr);   

SetLength(IvBts16);   
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(bMsgLen4);   
bMsgLen[0:= (Length(btmpMsg shr24 and$FF;   
bMsgLen[1:= (Length(btmpMsg shr16 and$FF;   
bMsgLen[2:= (Length(btmpMsg shr8 and$FF;   
bMsgLen[3:= Length(btmpMsg and$FF;   

SetLength(bMsgLength(bRandLength(bAppidLength(btmpMsgLength(bMsgLen));   
Move(bRand[0], bMsg[0], Length(bRand));   
Move(bMsgLen[0], bMsg[Length(bRand)], Length(bMsgLen));   
Move(btmpMsg[0], bMsg[Length(bRandLength(bMsgLen)], Length(btmpMsg));   
Move(bAppid[0], bMsg[Length(bRandLength(bMsgLenLength(btmpMsg)], Length(bAppid)); 

{$REGION '自己进行PKCS7补位'}   
SetLength(msgLength(bMsg32 Length(bMsg mod32);   
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], AesKeyLength(aEncodingAESKeyBts));   
Move(IvBts[0], IvLength(IvBts));   
InputStream := TMemoryStream.Create;   
OutputStream := TMemoryStream.Create;   
     try
InputStream.Write(msg[0], Length(msg));     
InputStream.Position := 0;     
EncryptAESStreamCBC(InputStream0AesKeyIvOutputStream);     
Result := string(EncodeBase64(OutputStream.MemoryOutputStream.Size));   
     finally
InputStream.Free;     
OutputStream.Free;   
end
end

 functionTWxMsgCrypt.CreateRandCode(codeLenInteger): string
   var
codeSerialcodestring;   
strLstTStringList;   
randValueiInteger
   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:=  tocodeLen      do
       begin
randValue := Random(strLst.Count);       
code := code strLst[randValue];     
end;     
Result := code;   
     finally
strLst.Free;   
end
end

 functionTWxMsgCrypt.DecryptMsg( constsTokensTimeStampsNoncesMsgEncryptsSigture,   
sAppIDsEncodingAESKeystring varsMsgstring): WXBizMsgCryptErrorCode
   var
retWXBizMsgCryptErrorCode;   
cpidstring;   

 functionVerifySignatureWXBizMsgCryptErrorCode;   
     var
hashstring;     
aStrAnsiString;     
ALTStringList;     
iInteger;   
     begin
AL := TStringList.Create;     
       try
AL.Add(sToken);       
AL.Add(sTimeStamp);       
AL.Add(sNonce);       
AL.Add(sMsgEncrypt);       
AL.Sort;       
hash := '';       
 for:=  toAL.Count          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(sEncodingAESKeysMsgEncryptcpid);   
     except
 onEException      do
       begin
 ifE. 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

 functionTWxMsgCrypt.EncryptMsg( constsMsgsTokensAppIDsEncodingAESKeystring;   
 varsMsgEncryptstring): WXBizMsgCryptErrorCode
   var
hashwxDtwxNonceEncryptFieldstring;   

 functionGenSignaturestring;   
     var
hashstring;     
aStrAnsiString;     
ALTStringList;     
iInteger;   
     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:=  toAL.Count          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(sEncodingAESKeysMsgsAppID);   
     except
 onEException      do
       begin
 ifE. 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.

呵呵,看看效果图吧:

posted @ 2016-05-15 19:07  零下工作室  阅读(135)  评论(0编辑  收藏  举报