I have the following code written in delphi.
with TIdHashMessageDigest5.Create do begin
st2.Position := 0;
Digest := HashValue( st2 );
SetLength( Hash, 16 );
Move( Digest, Hash[1], 16);
Free;
end;
I need to convert that to use SHA1 hash. I couldn't find SHA1 type in the library. Can anyone help? I have looked for help on the internet but couldn't find any.
See here:
https://sergworks.wordpress.com/2014/10/25/high-performance-hash-library/
SHA1 hashing in Delphi XE
https://sourceforge.net/projects/sha1implementat/
http://www.colorfultyping.com/generating-a-sha-1-checksum-for-a-given-class-type/
BTW, you didn't mention your Delphi version. If you are using a modern version (XE onwards), I suppose that its standard libraries should support SHA-1, MD5, etc.
You could do it like this:
uses IdHashSHA;
function SHA1FromString(const AString: string): string;
var
SHA1: TIdHashSHA1;
begin
SHA1 := TIdHashSHA1.Create;
try
Result := SHA1.HashStringAsHex(AString);
finally
SHA1.Free;
end;
end;
Just in case, in latest Delphi versions You can try:
uses
System.Hash;
...
function getHashCode(const s : string) : string;
begin
Result := THashSHA1.GetHashString(s);
end;
You appear to be using Indy 9, which does not support SHA1. SHA1 (and a few other hashes, including several other SHAs) was added in Indy 10. The interface for TIdHash was also re-written in Indy 10. Amongst other changes, the HashValue() method was replaced with new Hash...() and Hash...AsHex() methods (HashString(AsHex), HashStream(AsHex), HashBytes(AsHex)), eg:
uses
..., IdHash, IdHashMessageDigest;
var
Hash: TIdBytes;
begin
with TIdHashMessageDigest5.Create do
try
st2.Position := 0;
Hash := HashStream( st2 );
finally
Free;
end;
// use Hash as needed...
end;
uses
..., IdHash, IdHashSHA;
var
Hash: TIdBytes;
begin
with TIdHashSHA1.Create do
try
st2.Position := 0;
Hash := HashStream( st2 );
finally
Free;
end;
// use Hash as needed...
end;
Two more options:
http://www.spring4d.org
unit Spring.Cryptography.SHA;
TSHA1 = class(THashAlgorithmBase, ISHA1)
http://lockbox.seanbdurkin.id.au/HomePage
unit LbProc;
procedure StreamHashSHA1(var Digest : TSHA1Digest; AStream : TStream);
procedure FileHashSHA1(var Digest : TSHA1Digest; const AFileName : string);
In older versions also (tested in maXbox4):
function SHA1FromFile(const filename: string): string;
var SHA1: TIdHashSHA1;
fs: TFileStream;
begin
SHA1:= TIdHashSHA1.Create;
fs:= TFileStream.Create(fileName, fmOpenRead or fmShareDenyWrite);
try
result:= SHA1.AsHex(SHA1.hashvalue1((fs),0,fs.size));
finally
SHA1.Free;
fs.Free;
end;
end;
Related
In Delphi 10.4, I try to save a valid TPicture base64-encoded to an INI file:
procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
var
LInput: TMemoryStream;
LOutput: TMemoryStream;
MyIni: TIniFile;
ThisFile: string;
begin
if FileSaveDialog1.Execute then
ThisFile := FileSaveDialog1.FileName
else EXIT;
LInput := TMemoryStream.Create;
LOutput := TMemoryStream.Create;
try
APicture.SaveToStream(LInput);
LInput.Position := 0;
TNetEncoding.Base64.Encode(LInput, LOutput);
LOutput.Position := 0;
MyIni := TIniFile.Create(ThisFile);
try
MyIni.WriteBinaryStream('Custom', 'IMG', LOutput); // Exception# 234
finally
MyIni.Free;
end;
finally
LInput.Free;
LOutput.Free;
end;
end;
WriteBinaryStream creates an exception:
ERROR_MORE_DATA 234 (0xEA) More data is available.
Why? What does this mean? How can this problem be solved?
EDIT: Taking into consideration what #Uwe Raabe and #Andreas Rejbrand said, this code (which does not use base64-encoding) now works:
procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
var
LInput: TMemoryStream;
MyIni: System.IniFiles.TMemIniFile;
ThisFile: string;
begin
if FileSaveDialog1.Execute then
ThisFile := FileSaveDialog1.FileName
else EXIT;
LInput := TMemoryStream.Create;
try
APicture.SaveToStream(LInput);
LInput.Position := 0;
MyIni := TMemIniFile.Create(ThisFile);
try
MyIni.WriteBinaryStream('Custom', 'IMG', LInput);
MyIni.UpdateFile;
finally
MyIni.Free;
end;
finally
LInput.Free;
end;
end;
I believe this is a limitation in the operating system's functions for handling INI files; the string is too long for it.
If you instead use the Delphi INI file implementation, TMemIniFile, it works just fine. Just don't forget to call MyIni.UpdateFile at the end.
Yes, this is indeed a limitation in the Windows API, as demonstrated by the following minimal example:
var
wini: TIniFile;
dini: TMemIniFile;
begin
wini := TIniFile.Create('C:\Users\Andreas Rejbrand\Desktop\winini.ini');
try
wini.WriteString('General', 'Text', StringOfChar('W', 10*1024*1024));
finally
wini.Free;
end;
dini := TMemIniFile.Create('C:\Users\Andreas Rejbrand\Desktop\pasini.ini');
try
dini.WriteString('General', 'Text', StringOfChar('D', 10*1024*1024));
dini.UpdateFile;
finally
dini.Free;
end;
(Recall that INI files were initially used to store small amounts of configuration data in the 16-bit Windows era.)
Also, Uwe Raabe is right: you should save the Base64 string as text.
I want to get info_hash of *.torrent file using Delphi.
Tried this BEncode decorder.
But it gives crazy characters when decode.
Any other working BEncode decoder in Delphi? Or anything I'm doing wrong?
This is my code:
procedure TForm.Button1Click(Sender: TObject);
var
be: TBEncoded;
fs: tfilestream;
op: string;
begin
fs := tfilestream.Create('xx.torrent', fmOpenReadWrite);
be := TBEncoded.Create(fs);
be.Encode(be.ListData.Items[0].Data, op);
showmessage(op);
be.Encode(be.ListData.FindElement('info'), op);
showmessage(op);
end;
I've just tried this decoder, it's working normally. You didn't need to use Encode procedure, its purpose (as seen from name) is to encode elements back to BEncode. That's test program that shows torrent information in TMemo:
procedure ShowDecoded(be: TBEncoded; indent: string='');
var i: Integer;
begin
with form1.Memo1.Lines do
case be.Format of
befstring: Add(indent+be.StringData);
befInteger: Add(indent+IntToStr(be.IntegerData));
befList: begin
Add(indent+'list');
for i:=0 to be.ListData.Count-1 do
ShowDecoded(be.ListData.Items[i].Data as TBEncoded,indent+' ');
Add(indent+'end of list');
end;
befDictionary: begin
Add(indent+'dict');
for i:=0 to be.ListData.Count-1 do begin
Add(indent+' '+be.ListData.Items[i].Header+'=');
ShowDecoded(be.listData.Items[i].Data as TBEncoded,indent+' ');
end;
Add(indent+'end of dict');
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var fs: TFileStream;
be: TBEncoded;
i: Integer;
begin
if OpenDialog1.Execute then begin
fs:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
try
be:=TBEncoded.Create(fs);
ShowDecoded(be);
be.Free;
finally
fs.Free;
end;
end;
end;
That's test result:
dict
created by=
uTorrent/3.4.3
creation date=
1439626950
encoding=
UTF-8
info=
dict
length=
1345178
name=
Алябьев А., Лист Ф. - Соловей - 1987.pdf
piece length=
16384
pieces=
)Lo.Î ’üXí»IÙçsáôt£ˆb›hŒˆ*Ð誺š¤/N7’`0âÓ†nË5&T€:V•Ìפ¯9¤Ý:¦J©Ï|Œ•A¥,¼R¯þ:H:X&…¢<¸º"2îV-vÀÖˆD†¨¬ß‰ƒ,ümjà?éÛoe¬r£{¨¾]•4òØžhô†›¼AØBeJÕÌ4³·Œ‹¶ËAG— f„\pa
end of dict
end of dict
I'd make some changes to BEncode unit, there is mess in there: raising empty exceptions, unsafe cast: TBEncoded(object) instead of "object as TBEncoded",
checking for nil object before object.free, which is tautology, but in general it works.
Update 1
Simple code to take one of the fields, 'pieces' and show in hex.
procedure FindAndShowHash(be: TBEncoded);
var i: Integer;
s: string;
infoChunk, piecesChunk: TBencoded;
begin
s:='';
infoChunk:=be.ListData.FindElement('info') as TBencoded;
piecesChunk:=infoChunk.ListData.FindElement('pieces') as TBencoded;
for i:=1 to Length(piecesChunk.StringData) do
s:=s+IntToHex(Byte(piecesChunk.StringData[i]),2);
form1.Memo1.Lines.Add('Hash function:');
form1.Memo1.Lines.Add(s);
end;
As you see, we access StringData char by char and cast it as Byte. I just showed it in hex, of course you can use these bytes for further processing.
Beware: you'll get LOADS of hex values, this is not MD5 hash or any other hash of WHOLE torrent, it's sequence of hash functions for each piece of data, usually blocks of 1 or 2 MB.
UPDATE 2
This unit can be used in newer versions of Delphi, all you need to do is to replace ALL string variables in it from 'string' to 'ANSIstring', just with Ctrl+R - ':string' replace to ':ANSIstring'.
UPDATE 3
OK, finally I get it. Here is procedure which computes info_hash and shows it in hex, this requires newer version of Delphi. Also, add IdGlobal and IdHashSHA to 'uses' section.
procedure makeInfoHash(be: TBEncoded);
var SHA1: TIdHashSHA1;
s: string;
infoChunk: TBencoded;
infoEncoded: ANSIString;
bytes: TIdBytes;
begin
infoChunk:=be.ListData.FindElement('info') as TBencoded;
TBencoded.Encode(infoChunk,infoEncoded);
bytes:=RawToBytes(infoEncoded[1],Length(infoEncoded));
SHA1:=TIdHashSHA1.Create;
try
s:=SHA1.HashBytesAsHex(bytes);
finally
SHA1.Free;
end;
Form1.Memo1.Lines.Add(s);
end;
It gives correct info_hash, the same which is displayed in uTorrent, like this:
7D0487D3D99D9C27A7C09CDCBB2F2A8034D4F9BF
You must replace all string to ANSIstring in BENcode.pas, as said in update 2. Enjoy!
I have scowered the net trying to find an example of a function, how to hash text with Sha1 and DCPcrypt.
I have the below example. Seems to pop up the whole time.
But it returns chinese characters every time. Please assist in corecting the function.
function TForm1.EncryptThis(aString : string) : string;
var
Cipher: TDCP_cast256;
KeyStr: string;
begin
KeyStr:= '';
Cipher:= TDCP_cast256.Create(Self);
Cipher.InitStr(KeyStr,TDCP_sha1);
result := Cipher.EncryptString(aString);
Cipher.Burn;
Cipher.Free;
end;
UPDATE:
Using the links and info belowe, I built these functions. But as I said, This does not make alot of sense to me. So please excuse the ignorance.
THe code however does not work. Its output is: 3F3F3F3F3F3F3F3F3F3F00000000000000000000 whereas it should be 40bd001563085fc35165329ea1ff5c5ecbdbbeef since i told the program to has 123.
Please help.
function CalcDigest(text: string): string;
var
x: TDCP_hash;
begin
x := TDCP_sha1.Create(nil);
try
x.Init;
x.UpdateStr(text);
SetLength(Result, x.GetHashSize div 8);
x.Final(Result[1]);
finally
x.Free;
end;
end;
function String2Hex(const Buffer: Ansistring): string;
begin
SetLength(result, 2*Length(Buffer));
BinToHex(#Buffer[1], PWideChar(#result[1]), Length(Buffer));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
memo2.Lines.Add(String2Hex(CalcDigest(memo1.Lines.Strings[0])));
end;
Judging by this, you can do it this way:
function CalcDigest(text: string): string;
var
x: TDCP_hash;
begin
x := TDCP_sha1.Create(nil);
try
x.Init;
x.UpdateStr(text);
SetLength(Result, x.GetHashSize div 8);
x.Final(Result[1]);
finally
x.Free;
end;
end;
You may want to encode the hash before printing, because the output is binary. See for example this question.
I am not very familiar with DCPCrypt. You can also use other libraries.
1) Indy - usually included in Delphi
function SHA1Text(const s: string): string;
begin
with TIdHashSHA1.Create do
try
Result:=LowerCase(HashStringAsHex(s));
finally
Free;
end;
end;
2) Wolfgang Ehrhardt's libraries (fastest as far as I know) from
http://www.wolfgang-ehrhardt.de/crchash_en.html
function SHA1Text(const s: string): string;
var
Context: THashContext;
SHA1Digest: TSHA1Digest;
begin
SHA1Init(Context);
SHA1Update(Context, pChar(s), length(s));
SHA1Final(Context, SHA1Digest);
Result:=HexStr(#SHA1Digest, SizeOf(SHA1Digest));
end;
NOTE: it is from Delphi 7. You will need to update it if you use unicode Delphi.
Using DEC (Delphi Encryption Compendium Version 5.2) in Delphi XE2, I encrypt a string with the following code below.
However, AFTER EACH PROGRAM START the same sequence of encryption results are generated. I have tried to use Randomize in FormCreate, but it does not help.
uses
DECUtil, DECCipher, DECHash, DECFmt;
const
MyPW = 'MyPassword';
var
AKDFIndex: LongWord = 1;
function MyEncryptEx(const AText: string;
const APassword: string;
ATextFormat: TDECFormatClass;
AHashClass: TDECHashClass;
ACipherMode: TCipherMode;
ACipherClass: TDECCipherClass): string;
var
ASalt: Binary;
AData: Binary;
APass: Binary;
begin
with ValidCipher(ACipherClass).Create, Context do
try
ASalt := RandomBinary(16);
APass := ValidHash(AHashClass).KDFx(APassword[1], length(APassword) * SizeOf(APassword[1]),
ASalt[1], length(ASalt), KeySize, TFormat_Copy, AKDFIndex);
Mode := ACipherMode;
Init(APass);
SetLength(AData, length(AText) * SizeOf(AText[1]));
Encode(AText[1], AData[1], length(AData));
Result := ValidFormat(ATextFormat).Encode(ASalt + AData + CalcMAC);
finally
Free;
ProtectBinary(ASalt);
ProtectBinary(AData);
ProtectBinary(APass);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize;
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
EncrText: string;
begin
EncrText := MyEncryptEx(edtHWID.Text, MyPW, TFormat_ESCAPE, THash_Whirlpool, cmCBCx, TCipher_Rijndael);
end;
It seems that some Randomizer is not initialized after program start.
So how can I get different encryption results after each program start?
I found some source code. Looks like DECUtil's RandomBinary uses its own seed. You could try calling RandomSeed(RandomSystemTime, 4) instead of Randomize.
I used Binary to Base64 function that you answered :
Binary to Base64 (Delphi)
I successfully encode a file to base64 string and write it to MsSQL2008 database, but i want to ask a question:
How can i write this file to disk again with using EncdDecd.pas?
As always, David answered sufficiently. Although I can't resist to give a slightly different solution using some of the goodies from the recent Delphi versions.
procedure DecodeFile(const base64: AnsiString; const FileName: string);
var
stream: TBytesStream;
begin
stream := TBytesStream.Create(DecodeBase64(base64));
try
stream.SaveToFile(Filename);
finally
stream.Free;
end;
end;
This function will take a base64 encoded string, decode it, and write the resulting byte array to a file.
procedure DecodeToFile(const base64: AnsiString; const FileName: string);
var
stream: TFileStream;
bytes: TBytes;
begin
bytes := DecodeBase64(base64);
stream := TFileStream.Create(FileName, fmCreate);
try
if bytes<>nil then
stream.Write(bytes[0], Length(Bytes));
finally
stream.Free;
end;
end;
To explain what is happening here, the first line
bytes := DecodeBase64(base64);
performs the decode and returns the decoded binary contents of the file in a TBytes variable. TBytes is simply an array of bytes.
The next step is to create the file. The idiomatic way to write files in Delphi is to use streams. In this case we want a TFileStream.
stream := TFileStream.Create(FileName, fmCreate);
The fmCreate option means that if the file already exists, it will be replaced and overwritten by what we write.
The final step is to write the contents of the byte array to the file
if bytes<>nil then
stream.Write(bytes[0], Length(Bytes));
The if bytes<>nil check is to handle the case where the base64 string decodes to an empty array. If we were to remove that check then the following line would result in a runtime error if you were running with range checking enabled (which you should be doing). The call to stream.Write should be self-explanatory.
After looking into Soap.EncdDecd the one can find more platform independent way, as it's DecodeBase64 uses universal (no AnsiString) methods from System.NetEncoding.
Based on Uwe's sample:
uses
...
System.Classes,
System.NetEncoding;
...
procedure DecodeFile(const base64: String; const FileName: string);
var
stream: TBytesStream;
begin
stream := TBytesStream.Create(TNetEncoding.Base64.DecodeStringToBytes(base64));
try
stream.SaveToFile(Filename);
finally
stream.Free;
end;
end;
uses
Soap.EncdDecd;
function TForm1.EncodeFile(const FileName: string): AnsiString;
var
MemStream: TMemoryStream;
begin
MemStream := TMemoryStream.Create;
try
MemStream.LoadFromFile(Filename);
Result := EncodeBase64(MemStream.Memory, MemStream.Size);
finally
MemStream.Free;
end;
end;
function TForm1.DecodeFile(const base64: AnsiString): TBytesStream;
begin
Result := TBytesStream.Create(DecodeBase64(base64));
end;
I have a very old Delphi2006(v10.0.2558.35231 Update 2) and had to decode base64 UTF8 encoded input strings. I finally figured it out and heres an example for anyone interested.
Uses
IdCoderMIME; // Indy9
var
decoder: TIdDecoderMIME;
str: WideString;
- - -
decoder := TIdDecoderMIME.Create(nil);
str := base64DecodeUTF8(decoder, b64sourcestr);
decoder.Free;
- - -
function base64DecodeUTF8(decoder:TIdDecoderMIME; str:String): WideString;
var
stream:TMemoryStream;
utf8: UTF8String;
//idx:Integer;
begin
stream := TMemoryStream.Create;
try
decoder.DecodeToStream(str, stream);
setString(utf8, PChar(stream.Memory), stream.Size);
Result := UTF8Decode(utf8);
//for idx := 0 to stream.Size-1 do begin
// Writeln(PChar(stream.Memory)[idx] + ' ' + IntToStr(ORD(PChar(stream.Memory) [idx])) );
//end;
finally
stream.Free;
end;
end;