Indy10的base64加密算法与9不兼容,且中文有问题。

lyghe 2008-10-13 11:43:58
用indy9的效果一直很好。
cb2009升级到indy10后,好像加密得到的密文不一样,而且解密后中文变成乱码了。

使用indy9时的代码是这样的:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo2->Text = IdEncoderMIME1->Encode( Memo1->Text );
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Memo1->Text = IdDecoderMIME1->DecodeToString( Memo2->Text );
}

对“hello大家好”加密,密文是“aGVsbG+087zSusM=”。

升级到cb2009后,IdDecoderMIME没有DecodeToString函数,于是改成DecodeString。其他代码不变。

问题来了:加密后的密文是“aGVsbG8ntn0=”,密文不一样了。对这个密文解密,得到的是“hello'¶}”,中文变成乱码了。

我现在要用cb加密后,把密文送给java程序解密。密文居然不一样了,java程序岂不是要疯了?

究竟怎么回事?请达人帮忙看看能不能解决这个问题。谢谢!
...全文
1028 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
airspace 2010-12-08
  • 打赏
  • 举报
回复
唉,关注一下,被害惨了。。。。
obarapin 2010-06-23
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 waiting4you 的回复:]
由于白天在公司里没有BCB2009试验,现在终于发现这个Indy把这个编码设计成通过Unicode输入输出真是一大败笔.
下面是通过TBytes来传递,这样编解码时不会涉及Unicode编码转换的问题.

C/C++ code
void __fastcall TForm2::Button1Click(TObject *Sender)
{
TBytes as;
Ansi……
[/Quote]
BCB2010 测试可行
ezhimeng 2010-01-28
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 waiting4you 的回复:]
由于白天在公司里没有BCB2009试验,现在终于发现这个Indy把这个编码设计成通过Unicode输入输出真是一大败笔.
下面是通过TBytes来传递,这样编解码时不会涉及Unicode编码转换的问题.
C/C++ codevoid __fastcall TForm2::Button1Click(TObject*Sender)
{
TBytesas;
AnsiString str= Memo1->Text;as.Length= str.Length();
memcpy(&as[0],str.c_str(),as.Length);
Memo2->Text= IdEncoderMIME1->EncodeBytes(as);
}//---------------------------------------------------------------------------void __fastcall TForm2::Button2Click(TObject*Sender)
{
TBytes us= IdDecoderMIME1->DecodeBytes( Memo2->Text );
Memo1->Text= AnsiString((char*)&us[0],us.Length);
}//---------------------------------------------------------------------------
[/Quote]


此方法只在同一语言的系统上有效,不同语言系统还是会乱码!
laowang2 2009-03-30
  • 打赏
  • 举报
回复
upup
ezhimeng 2008-11-24
  • 打赏
  • 举报
回复
毛兄的方法在2009 不行呀!
没有别的方法了吗?
深宇 2008-11-21
  • 打赏
  • 举报
回复
留个脚印
僵哥 2008-10-13
  • 打赏
  • 举报
回复
从作者提供的注释看,我们不能把这个转换导致的问题称之为BUG,而只能称之为版本的不足.
僵哥 2008-10-13
  • 打赏
  • 举报
回复
引用
function ToBytes(const AValue: string; const ALength: Integer; const AIndex: Integer = 1;
const AEncoding: TIdEncoding = en7Bit): TIdBytes; overload;
var
LLength: Integer;
begin
ValidEncoding(AEncoding);
LLength := IndyLength(AValue, ALength, AIndex);
if LLength > 0 then
begin
if AEncoding = enUTF8 then begin
Result := StringToUTF8Bytes(AValue, AIndex, LLength);
end else
begin
// do just a byte to byte copy with no translation. VCL uses ANSI or MBCS.
// With MBCS we still map 1:1

SetLength(Result, LLength); //这里Result是一个Byte Array,而这个LLength的计量单位是Unicode字节,所以存在空间不足
CopyTIdString(AValue, AIndex, Result, 0, LLength,AEncoding);
end;
end else begin
SetLength(Result, 0);
end;
end;

procedure CopyTIdString(const ASource: String; const ASourceIndex: Integer;
var VDest: TIdBytes; const ADestIndex: Integer; const ALength: Integer = -1;
const AEncoding: TIdEncoding = en7Bit); overload;
{$IFDEF USEINLINE}inline;{$ENDIF}
var
LLength: Integer;
{$IFDEF DOTNET_OR_TEncoding}
i : Integer;
{$ENDIF}
begin
ValidEncoding(AEncoding);
LLength := IndyLength(ASource, ALength, ASourceIndex);
if LLength > 0 then
begin
{$IFDEF DOTNET_OR_TEncoding}
if AEncoding = en8bit then
begin
{
IMPORTANT!!!!

For en8bit, the chars from $80-$9F will wind up being translated to Unicode byte values
and not back to the original $80-$9F values. That might be a good thing if you have a string
that you want to print but that doesn't work too well for when the string is
binary data. The benefit of the Windows 1252 code page is that you always get
charactors you can print.

The routine itself could probably be optimized but I'm not sure how.
}

for i := 0 to LLength-1 do
begin
VDest[ADestIndex+i] := Ord(ASource[ASourceIndex+i]) and $FF; //特别要注意的是这里,对于所有双字节字符均只取低字节
end;
end
else
begin
GetEncoder(AEncoding).GetBytes(ASource, ASourceIndex{$IFDEF DOTNET}-1{$ENDIF}, LLength, VDest, ADestIndex);
end;
{$ELSE}
// TODO: support UTF8
if AEncoding = enUTF8 then begin
ToDo;
end;
Move(ASource[ASourceIndex], VDest[ADestIndex], LLength);
{$ENDIF}
end;
end;
僵哥 2008-10-13
  • 打赏
  • 举报
回复
其实代码当中都有注释说明
function ToBytes(const AValue: string; const ALength: Integer; const AIndex: Integer = 1;
const AEncoding: TIdEncoding = en7Bit): TIdBytes; overload;
var
LLength: Integer;
begin
ValidEncoding(AEncoding);
LLength := IndyLength(AValue, ALength, AIndex);
if LLength > 0 then
begin
if AEncoding = enUTF8 then begin
Result := StringToUTF8Bytes(AValue, AIndex, LLength);
end else
begin
// do just a byte to byte copy with no translation. VCL uses ANSI or MBCS.
// With MBCS we still map 1:1
SetLength(Result, LLength);
CopyTIdString(AValue, AIndex, Result, 0, LLength,AEncoding);
end;
end else begin
SetLength(Result, 0);
end;
end;

procedure CopyTIdString(const ASource: String; const ASourceIndex: Integer;
var VDest: TIdBytes; const ADestIndex: Integer; const ALength: Integer = -1;
const AEncoding: TIdEncoding = en7Bit); overload;
{$IFDEF USEINLINE}inline;{$ENDIF}
var
LLength: Integer;
{$IFDEF DOTNET_OR_TEncoding}
i : Integer;
{$ENDIF}
begin
ValidEncoding(AEncoding);
LLength := IndyLength(ASource, ALength, ASourceIndex);
if LLength > 0 then
begin
{$IFDEF DOTNET_OR_TEncoding}
if AEncoding = en8bit then
begin
{
IMPORTANT!!!!

For en8bit, the chars from $80-$9F will wind up being translated to Unicode byte values
and not back to the original $80-$9F values. That might be a good thing if you have a string
that you want to print but that doesn't work too well for when the string is
binary data. The benefit of the Windows 1252 code page is that you always get
charactors you can print.

The routine itself could probably be optimized but I'm not sure how.
}
for i := 0 to LLength-1 do
begin
VDest[ADestIndex+i] := Ord(ASource[ASourceIndex+i]) and $FF;
end;
end
else
begin
GetEncoder(AEncoding).GetBytes(ASource, ASourceIndex{$IFDEF DOTNET}-1{$ENDIF}, LLength, VDest, ADestIndex);
end;
{$ELSE}
// TODO: support UTF8
if AEncoding = enUTF8 then begin
ToDo;
end;
Move(ASource[ASourceIndex], VDest[ADestIndex], LLength);
{$ENDIF}
end;
end;
僵哥 2008-10-13
  • 打赏
  • 举报
回复
现在的Indy只是在原来的基础上面匆忙的升级至对2009的兼容,多多少少难免会有一些BUG存在,毕竟Indy套件相当庞大,而2009刚刚推出来,Indy主要的开发人员,还不可能把工作平台搬上去,更何况这个版本是在Codegear RAD 2009发行之前推出的.
Waiting4you 2008-10-13
  • 打赏
  • 举报
回复
由于白天在公司里没有BCB2009试验,现在终于发现这个Indy把这个编码设计成通过Unicode输入输出真是一大败笔.
下面是通过TBytes来传递,这样编解码时不会涉及Unicode编码转换的问题.
void __fastcall TForm2::Button1Click(TObject *Sender)
{
TBytes as;
AnsiString str = Memo1->Text;
as.Length = str.Length();
memcpy(&as[0],str.c_str(),as.Length);
Memo2->Text = IdEncoderMIME1->EncodeBytes(as);
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button2Click(TObject *Sender)
{
TBytes us = IdDecoderMIME1->DecodeBytes( Memo2->Text );
Memo1->Text = AnsiString((char*)&us[0],us.Length);
}
//---------------------------------------------------------------------------
僵哥 2008-10-13
  • 打赏
  • 举报
回复
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TMemoryStream *strm;
AnsiString str;
str = "hello大家好";
strm = new TMemoryStream();
try{
strm->Write(str.c_str(),str.Length());
strm->Seek(0,soFromBeginning);
ShowMessage(IdEncoderMIME1->Encode(strm));
}__finally{
strm->Free();
}
}
lyghe 2008-10-13
  • 打赏
  • 举报
回复
刚刚试了,还是不行。Encode的第三个参数用来指出允许编码的最大数量,缺省为MaxInt,应该不影响吧。
帮助是这样写的:
ABytes indicates the number of bytes in ASrcStream to be affected by the encoding operation. When omitted, ABytes defaults to the constant value MaxInt, and allows the method to read up to MaxInt byte values or until the end of the Stream is reached.
hemiya 2008-10-13
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 lyghe 的回复:]
答hemiya:我也用这个函数试了,结果是dest的DataString总是为空,在2006也是一样。不知道是不是用法错了。

C/C++ code TStringStream * src = new TStringStream;
src->WriteString( Memo1->Text );
TStringStream * dest = new TStringStream;
IdEncoderMIME1->Encode( src, dest );
Memo2->Text = dest->DataString;
[/Quote]

//IdEncoderMIME1->Encode( src, dest ); 此处缺个参数
IdEncoderMIME1->Encode( src, dest , src->Size);
lyghe 2008-10-13
  • 打赏
  • 举报
回复
用TMemoryStream也不行,dest还是为空。
	AnsiString strSrc = Memo1->Text;
AnsiString strDest;

TMemoryStream * src = new TMemoryStream;
src->Write( strSrc.c_str(), strSrc.Length() );
TMemoryStream * dest = new TMemoryStream;

IdEncoderMIME1->Encode( src, dest );

strDest.SetLength( dest->Size );
dest->Read( strDest.c_str(), strDest.Length() );

Memo2->Text = strDest;
lyghe 2008-10-13
  • 打赏
  • 举报
回复
答hemiya:我也用这个函数试了,结果是dest的DataString总是为空,在2006也是一样。不知道是不是用法错了。
	TStringStream * src = new TStringStream;
src->WriteString( Memo1->Text );
TStringStream * dest = new TStringStream;
IdEncoderMIME1->Encode( src, dest );
Memo2->Text = dest->DataString;
hemiya 2008-10-13
  • 打赏
  • 举报
回复
楼主那你试试,这个是存字节的.

Encode(Classes::TStream* ASrcStream, Classes::TStream* ADestStream, const int ABytes = 0xffffffff)/* overload */;
lyghe 2008-10-13
  • 打赏
  • 举报
回复
字符编码类型的问题一直都是个马蜂窝。想不到竟然被捅了,而且捅得这么深。被蜇是必然的。唉。
lyghe 2008-10-13
  • 打赏
  • 举报
回复
答毛毛:因为Encode函数的输入参数也是UnicodeString。
如果把Memo1->Text从UnicodeString转成AnsiString,传入Encode函数时又被自动转换成了UnicodeString,相当于什么都没做。在这么简单的转换上一定不会出什么问题。
当然了,转换成WideString也不靠谱。


对这个问题,我的猜想是:
Indy的实现中大概是把字符串的内容分拆开来,一个一个字符的访问,所以把1个中文当作1个字符了,而不是当初AnsiString时的2个字符。
看到csdn上还有很多人提出来,UnicodeString的c_str()之类的函数有问题,大概也是这个原因吧。唉。
Waiting4you 2008-10-13
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 lyghe 的回复:]
引用 1 楼 Waiting4you 的回复:
是Unicode的问题,你先把Memo1->Text转换成AnsiString再编码。

改了测试,没有用。也不可能有用。要是说转换成WideString,可能还有些靠谱。
[/Quote]
为什么不可能有用?在BCB2009以前Memo1->Text都是AnsiString类型的,你现在是UnicodeString类型的,转换结果当然不一样。
加载更多回复(5)

604

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder VCL组件使用和开发
社区管理员
  • VCL组件使用和开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧