0

I'm new in Delphi.I'm trying to make a one-way hashing of an ASCII string. Is there any way to encrypt ASCII string without adding Indy or any other Cryptography library to the project And just by standard functions ?

if there is any way to use windows API , How i can do that ?

if it is possible , please give me an example.

Ken White
  • 123,280
  • 14
  • 225
  • 444
Zeid
  • 59
  • 1
  • 8
  • What's the problem with using third party library? Besides, Indy comes with Delphi. Hint: third party libraries are just code that you didn't have to write yourself. – Marcus Adams Feb 07 '14 at 19:51
  • Hashing is pretty easy. Plenty of libraries for that. Have you searched? – David Heffernan Feb 07 '14 at 19:51
  • What did you try? What are requirements for the hash? Questions asking for code should demonstrate a minimum understanding of a problem to be solved. – Free Consulting Feb 07 '14 at 20:02
  • @MarcusAdams The problems with 3rd party libraries are several: 1) Delphi has no central repository for them, so one has to hunt for them; 2) if you don't have source code for them you have a potential future breaking point for your code; 3) They add to the requirements to build the project (someone else working on your code needs to install and possibly buy all the 3rd party components, 4) There will likely be no consistency between how 3rd parties implement their libraries (see Delphi & database drivers). There are probably more. It's a shame a $1000 product doesn't include a hash library. – alcalde Feb 07 '14 at 23:50
  • 1
    @alcalde The thing is there is lots of well tried and tested, and simple, C code to implement hashes. It would not take much to compile that to object, and link it. Or even translate to Delphi. But I'd always compile code that is known to work rather then translate. – David Heffernan Feb 08 '14 at 00:02
  • @alcalde, you're speaking for the OP? – Marcus Adams Feb 08 '14 at 00:15
  • 1
    See [`MD5 Hashing in Delphi 2009`](http://stackoverflow.com/q/392657/576719) for an example of hashing an ansistring with Delphi MD5 library `MessageDigest_5.pas` which is shipped with Delphi since D2009. I noticed that the source is missing by accident in XE3. – LU RD Feb 08 '14 at 10:16

2 Answers2

11

You can use the Windows Cryptography library for this. For instance, here's a minimal unit to calculate MD5 hashes:

unit MD5;

interface

uses
  SysUtils, Math, Windows, Classes;

type
  TMD5Hash = array [0..15] of Byte;

function MD5Hash(Stream: TStream): TMD5Hash; overload;
function MD5Hash(const Bytes: TBytes): TMD5Hash; overload;
function MD5Hash(const Str: RawByteString): TMD5Hash; overload;
function MD5ToString(const Hash: TMD5Hash): string;

implementation

type
  HCRYPTPROV = type NativeUInt;
  HCRYPTKEY = type NativeUInt;
  HCRYPTHASH = type NativeUInt;
  ALG_ID = Cardinal;

const
  PROV_RSA_FULL = 1;
  CRYPT_VERIFYCONTEXT = $F0000000;
  ALG_CLASS_HASH = 4 shl 13;
  ALG_TYPE_ANY = 0;
  ALG_SID_MD5 = 3;
  CALG_MD5 = ALG_CLASS_HASH or ALG_TYPE_ANY or ALG_SID_MD5;
  HP_HASHVAL = $0002;

function CryptAcquireContextW(
  out phProv: HCRYPTPROV;
  pszContainer: PWideChar;
  pszProvider: PWideChar;
  dwProvType: DWORD;
  dwFlags: DWORD
): BOOL; stdcall; external 'Advapi32.dll';

function CryptReleaseContext(
  hProv: HCRYPTPROV;
  dwFlags: DWORD
): BOOL; stdcall; external 'Advapi32.dll';

function CryptCreateHash(
  hProv: HCRYPTPROV;
  Algid: ALG_ID;
  hKey: HCRYPTKEY;
  dwFlags: DWORD;
  out phHash: HCRYPTHASH
): BOOL; stdcall; external 'Advapi32.dll';

function CryptDestroyHash(
  hHash: HCRYPTHASH
): BOOL; stdcall; external 'Advapi32.dll';

function CryptHashData(
  hHash: HCRYPTHASH;
  pbData: Pointer;
  dwDataLen: DWORD;
  dwFlags: DWORD
): BOOL; stdcall; external 'Advapi32.dll';

function CryptGetHashParam(
  hHash: HCRYPTHASH;
  dwParam: DWORD;
  pbData: Pointer;
  var pdwDataLen: DWORD;
  dwFlags: DWORD
): BOOL; stdcall; external 'Advapi32.dll';

function MD5Hash(Stream: TStream): TMD5Hash;
const
  BuffSize = 1024;
var
  hProv: HCRYPTPROV;
  hHash: HCRYPTHASH;
  Buff: array [0..BuffSize] of Byte;
  BytesLeft: Int64;
  BytesToRead: Integer;
  HashSize: DWORD;
begin
  Win32Check(CryptAcquireContextW(hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT));
  try
    Win32Check(CryptCreateHash(hProv, CALG_MD5, 0, 0, hHash));
    try
      BytesLeft := Stream.Size-Stream.Position;
      while BytesLeft>0 do begin
        BytesToRead := Min(BytesLeft, BuffSize);
        Stream.ReadBuffer(Buff, BytesToRead);
        Win32Check(CryptHashData(hHash, @Buff, BytesToRead, 0));
        dec(BytesLeft, BytesToRead);
      end;
      HashSize := SizeOf(Result);
      Win32Check(CryptGetHashParam(hHash, HP_HASHVAL, @Result, HashSize, 0));
    finally
      Win32Check(CryptDestroyHash(hHash));
    end;
  finally
    Win32Check(CryptReleaseContext(hProv, 0));
  end;
end;

function MD5Hash(const Str: RawByteString): TMD5Hash;
var
  Stream: TStringStream;
begin
  Stream := TStringStream.Create(Str);
  try
    Result := MD5Hash(Stream);
  finally
    Stream.Free;
  end;
end;

function MD5Hash(const Bytes: TBytes): TMD5Hash;
var
  Stream: TBytesStream;
begin
  Stream := TBytesStream.Create(Bytes);
  try
    Result := MD5Hash(Stream);
  finally
    Stream.Free;
  end;
end;

function MD5ToString(const Hash: TMD5Hash): string;
begin
  SetLength(Result, SizeOf(Hash)*2);
  BinToHex(Pointer(@Hash)^, PChar(Result), SizeOf(Hash));
end;

end.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I must say I am very surprised by the downvote here. I think this is a useful demonstration of how to get a very flexible MD5 with minimal effort. I cannot see how this could be readily improved. I guess the downvote means I got something wrong, and I'd really like to put it right. I can see this answer being referred to in the future. It should be fixed. – David Heffernan Feb 07 '14 at 22:37
  • Drive-by downvoters. The question itself is +2/-2. I gave you an upvote. – David Feb 07 '14 at 23:46
  • @DavidHeffernan "Minimal effort" to me would be having it built in. :-) Honestly it looks like you put quite a lot of effort into it, including translating function headers. I don't know why the downvote though. – alcalde Feb 07 '14 at 23:53
  • @alcalde Well, I said minimal rather than not very much! ;-) Yes, it took me 30 minutes or so. But it's a straight translation of the MSDN example. And it's a very clean and well-thought our library, so the header translations are simple. I'm not keen on using the JEDI unit because in my experience they come with so much extra baggage. – David Heffernan Feb 07 '14 at 23:55
  • There is a Delphi MD5 library (without external dependency), `MessageDigest_5.pas`. I linked an example of usage in a comment to the question. – LU RD Feb 08 '14 at 11:28
  • @LURD That's a bit weird isn't it. Source, but no .dcu. And a bit of a weird interface. I like the interface in my unit better. I think that my unit could usefully be expanded to add the other major hashes and be a standalone unit for that purpose. Perhaps I should do that and publish it. – David Heffernan Feb 08 '14 at 11:45
  • I like that it has no external dependencies (had to use that in an application for D2007). I guess that your interface is easier to work with (using the RawByteString will be lossless for all types of strings) and a stream input as well. – LU RD Feb 08 '14 at 12:15
  • 2
    @LURD these standard hashes are easy enough to implement. I think I will try to write something simple and portable for Delphi – David Heffernan Feb 08 '14 at 12:21
  • Tank you again David ... – Zeid Feb 09 '14 at 23:25
4

Zeid, I don't believe it's cryptographically secure, but Delphi does have a hash function called BobJenkinsHash(!!!) in the System.Generics.Defaults unit. It's not user-friendly either (requires you to supply the length (size?) of the memory block you're passing to it and returns an integer value. However, if your requirements are 1) Delphi and 2) no third party libraries and 3) you don't want to implement another hash yourself, I believe it's your only choice.

function BobJenkinsHash(const Data; Len, InitData: Integer): Integer;

You'd need to pass your string as the first parameter, the length of the string * the size of char as the second, and any other number (usually zero) as the last parameter.

alcalde
  • 1,288
  • 13
  • 10
  • 1
    Tank you for your time. This is really good answer but David's answer is more useful for me. tank you again for your time and for your sharing. – Zeid Feb 09 '14 at 23:23