:"-( I really hoped...

Wim ten Brink
Wim ten Brink used Ask the Experts™
on
...that http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20721630.html would have a nice answer by now, so I could save some time trying to do the research by myself....

Does no one here use CryptProtectData and CryptUnprotectData in some way?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Massimo Maria Ghisalberti  has converted the Crypto API see http://delphi-jedi.org/APILIBRARY
Try to get in contact with him. His email is in the conversion files.
Wim ten BrinkSelf-employed developer

Author

Commented:
I already know this convertion and am already using it. But it is incomplete and untested so I need more practical experience about these two functions. Basically, I hoped someone here knew more about these function. Thing is, I wonder about the usability of these methods if no other Delphi expert here has ever tried to use them. But I realise that Delphi developers often tend to use custom tools instead of the tools offered by the operating system itself.
Get in contact with Hagen Reddmann. He is THE crypto expert i know, but no EE member.
OWASP: Avoiding Hacker Tricks

Learn to build secure applications from the mindset of the hacker and avoid being exploited.

don't know if this is what your after????

const
  cPasswordKey  = 'TheKey';

uses
  WinCrypt;

function EncryptPassword(Password: String): String;
var
  hProv    : HCRYPTPROV;
  hKey     : HCRYPTKEY;
  hHash    : HCRYPTHASH;
  dwLength : DWORD;
  pbKeyBlob: PByte;
  dwBlobLen: Integer;
  Key      : String;
begin
  Result := Password;
  Key    := cPasswordKey;
  // if not CryptSetProvider(MS_DEF_PROV, PROV_RSA_FULL) then Exit;
  // Get handle to user default provider.
  if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then
     begin
       if GetLastError <> NTE_KEYSET_NOT_DEF then Exit;
       if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_NEWKEYSET) then Exit;
     end;
  try
    // Create hash object.
    if not CryptCreateHash(hProv, CALG_MD5, 0, 0, @hHash) then Exit;
    try
      // Hash password string.
      dwLength := Length(Key);
      if not CryptHashData(hHash, PByte(Key), dwLength, 0) then Exit;
      try
        // Create block cipher session key based on hash of the password.
        if not CryptDeriveKey(hProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, @hKey) then Exit;

        dwBlobLen := Length(Password);
        CryptEncrypt(hKey, 0, True, 0, nil, @dwBlobLen, 0);

        dwLength  := Length(Password);
        GetMem(pbKeyBlob, dwBlobLen);
        try
          Move(Password[1], pbKeyBlob^, dwLength);
          if not CryptEncrypt(hKey, 0, True, 0, pbKeyBlob, @dwLength, dwBlobLen) then Exit;
          SetLength(Result, dwBlobLen);
          Move(pbKeyBlob^, Result[1], dwBlobLen);
        finally
          FreeMem(pbKeyBlob);
        end;
      finally
        // Destroy hash object.
        CryptDestroyHash(hHash);
      end;
    finally
      // Destroy session key.
      CryptDestroyKey(hKey);
    end;
  finally
    // Release provider handle.
    CryptReleaseContext(hProv, 0);
  end;
end;

function DecryptPassword(Password: String): String;
var
  hProv    : HCRYPTPROV;
  hKey     : HCRYPTKEY;
  hHash    : HCRYPTHASH;
  dwLength : DWORD;
  Key      : String;
begin
  Result := Password;
  Key    := cPasswordKey;
  // if not CryptSetProvider(MS_DEF_PROV, PROV_RSA_FULL) then Exit;
  // Get handle to user default provider.
  if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then
     begin
       if GetLastError <> NTE_KEYSET_NOT_DEF then Exit;
       if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_NEWKEYSET) then Exit;
     end;
  try
    // Create hash object.
    if not CryptCreateHash(hProv, CALG_MD5, 0, 0, @hHash) then Exit;
    try
      // Hash password string.
      dwLength := Length(Key);
      if not CryptHashData(hHash, PByte(Key), dwLength, 0) then Exit;
      try
        // Create block cipher session key based on hash of the password.
        if not CryptDeriveKey(hProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, @hKey) then Exit;

        dwLength := Length(Password);
        CryptDecrypt(hKey, 0, True, 0, PByte(Result), @dwLength);
        SetLength(Result, dwLength);
      finally
        // Destroy hash object.
        CryptDestroyHash(hHash);
      end;
    finally
      // Destroy session key.
      CryptDestroyKey(hKey);
    end;
  finally
    // Release provider handle.
    CryptReleaseContext(hProv, 0);
  end;
end;
Massimo Maria Ghisalberti's converted Crypto API should work fine with this
Wim ten BrinkSelf-employed developer

Author

Commented:
Close...
But not close enough. The problem seems to be that I have no access to the key container. If I'm not mistaken, CryptProtectData and CryptUnprotectData don't need this container. Or, at least I don't have to specify it.

But in the meanwhile I discovered a working solution already...

function EncryptPassword(Password: string): string;
var
  DataIn: DATA_BLOB;
  dwFlags: DWORD;
  DataOut: DATA_BLOB;
  I: Integer;
  P: PByte;
begin
  Result := '';
  DataIn.cbData := Length(Password);
  DataIn.pbData := Pointer(PChar(Password));
  dwFlags := CRYPTPROTECT_LOCAL_MACHINE;
  if CryptProtectData(@DataIn, 'Password', nil, nil, nil, dwFlags, DataOut) then begin
    P := DataOut.pbData;
    I := DataOut.cbData;
    Result := IntToHex(I, 8);
    while (I > 0) do begin
      Dec(I);
      Result := Result + IntToHex(P^, 2);
      Inc(P);
    end;
    LocalFree(Cardinal(DataOut.pbData));
  end;
end;

function DecryptPassword(Password: string): string;
var
  DataIn: DATA_BLOB;
  dwFlags: DWORD;
  DataOut: DATA_BLOB;
  I, J: Integer;
  P: PByte;
  DataDescr: LPWSTR;
begin
  Result := '';
  if (Length(Password) > 0) then begin
    DataIn.cbData := StrToIntDef('$' + Copy(Password, 1, 8), 0);
    if (DataIn.cbData > 0) then begin
      GetMem(DataIn.pbData, DataIn.cbData);
      I := DataIn.cbData;
      J := 9;
      P := DataIn.pbData;
      while (I > 0) and (J < Length(Password)) do begin
        Dec(I);
        P^ := StrToInt('$' + Copy(Password, J, 2));
        Inc(P);
        Inc(J, 2);
      end;
      dwFlags := CRYPTPROTECT_LOCAL_MACHINE;
      if CryptUnprotectData(@DataIn, DataDescr, nil, nil, nil, dwFlags, DataOut) then begin
        Result := Copy(string(DataOut.pbData), 0, DataOut.cbData);
        LocalFree(Cardinal(DataOut.pbData));
      end;
    end;
  end;
end;

It seems to work but I do have to test it now. If anyone has any useful suggestions, please do.
(And yes, it only has to decrypt on one single computer.)
Wim ten BrinkSelf-employed developer

Author

Commented:
sybernite, your answer wasn't exactly what I was looking for but it did help me to get to my own solution. :-) These 20 points and A-grade are therefore well-deserved.
thanks. Glad to be of some (small) help :)
what unit did you use WorkingAlex, which contains the declarations of the functions CryptProtectData and Cryptunprotectdata

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial