EldoS | Feel safer!

Software components for data protection, secure storage and transfer

Looking for recipe: symmetric text encryption

Also by EldoS: CallbackFilter
A component to monitor and control disk activity, track file and directory operations (create, read, write, rename etc.), alter file data, encrypt files, create virtual files.
#27406
Posted: 11/26/2013 16:21:45
by Leonardo Herrera (Standard support level)
Joined: 02/14/2011
Posts: 66

I'm in the need of storing passwords in a database. Normally I would store just a hash but I need access to the plain password (the application will read email on behalf of the user).

Is there a really simple example, using strings, of such form of encryption? My only security restriction is that other users using the same client program in a *different* computer shouldn't be able to get the plain text back.

I'm trying to adapt a function like this one:

https://www.eldos.com/forum/read.php?FID=7&TID=1886

Sadly, I've been at it for several hours and I'm about to give up. Is there any ready-made function for this? Here's one of my failed attempts:

Code
class function TMyPasswordEncoder.Decode(Crypto, Password: string): String;
var
  LEncoder: TElPGPReader;
  LInputStream, LOutputStream: TMemoryStream;
begin
  Result := '';
  LEncoder := TElPGPReader.Create(nil);
  LInputStream := TMemoryStream.Create();
  LOutputStream := TMemoryStream.Create();
  try
    LEncoder.Passphrase := Password;
    LEncoder.OutputStream := LOutputStream;

    LInputStream.WriteBuffer(Pointer(Crypto)^, Length(Crypto));
    LInputStream.Position := 0;

    LOutputStream.Position := 0;
    try
      LEncoder.DecryptAndVerify(LInputStream);
    except
      Exit;
    end;
    LOutputStream.Position := 0;
    SetLength(Result, LOutputStream.Size);
    LOutputStream.ReadBuffer(Pointer(Result)^, LOutputStream.Size);
  finally
    LInputStream.Free;
    LOutputStream.Free;
    LEncoder.Free;
  end;
end;

class function TMyPasswordEncoder.Encode(Plain, APassword: string): string;
var
  Stream: TMemoryStream;
  Writer: TBinaryWriter;
  LEncoder: TElPGPWriter;
  LInputStream, LOutputStream: TMemoryStream;
begin
  Result := '';
  LEncoder := TElPGPWriter.Create(nil);
  LInputStream := TMemoryStream.Create();
  LOutputStream := TMemoryStream.Create();
  try
    LEncoder.EncryptionType := etPassphrase;
    LEncoder.SymmetricKeyAlgorithm := SB_PGP_ALGORITHM_SK_BLOWFISH;
    LEncoder.Passphrases.Add(APassword);
    LEncoder.InputIsText := true;
    LEncoder.TextCompatibilityMode := true;

    LInputStream.WriteBuffer(Pointer(Plain)^, Length(Plain));
    LInputStream.Position := 0;
    LOutputStream.Position := 0;
    try
      LEncoder.Encrypt(LInputStream, LOutputStream);
    except
      Exit;
    end;
    LOutputStream.Position := 0;
    SetLength(Result, LOutputStream.Size);
    LOutputStream.ReadBuffer(Pointer(Result)^, LOutputStream.Size);
  finally
    LInputStream.Free;
    LOutputStream.Free;
    LEncoder.Free;
  end;
end;

procedure TestEncryption.TestEncode;
var
  a, b: string;
begin
  a := TMyPasswordEncoder.Encode('bahbah', 'somerandomgeneratedpassword');
  b := TMyPasswordEncoder.Decode(a, 'somerandomgeneratedpassword');
  CheckEquals('bahbah', b);
end;
#27411
Posted: 11/26/2013 17:03:33
by Ken Ivanov (EldoS Corp.)

Hello Leonardo,

Typically, the task is addressed in the following way:

1. On preparation stage, a master key is generated and written down to a safe place (see below).

2. For each password to be protected, a pseudo random value ('salt') is generated and used to produce a session key:

Salt := GenerateRandomBytes(...);
SessionKey := Hash(Salt, MasterKey);

3. The password is encrypted with SessionKey; the encryption result is stored in the database together with the Salt.

4. Whenever there is a need to decrypt the password, the salt and the encrypted password are retrieved from the database, and the decryption key is recovered:

SessionKey := Hash(Salt, MasterKey);

To encrypt passwords with session keys, you can use different technologies/components (PKI / TElMessageEncryptor, OpenPGP / TElPGPWriter).

Regarding protected password storage, Windows provides a good mechanism for storing sensitive data, which is called Data Protection API (DPAPI). Protected values are stored in local protected area, which makes it impossible to read them on a different computer.

Hope the above makes sense. Please feel free to ask should you have any further questions.
#27455
Posted: 11/27/2013 15:39:57
by Leonardo Herrera (Standard support level)
Joined: 02/14/2011
Posts: 66

Thank for your detailed description.

The method I want to implement is: I already have loaded a certificate for other purposes. I want to use that to encrypt this password.

Here's my current attempt:

Code
  TMyEncoder = class
  private
    FCert: TElX509Certificate;
  public
    constructor Create(Cert: TElX509Certificate);
    function Decode(Encrypted: TMemoryStream): string;
    function Encode(Plain: string): TMemoryStream;
  end;

...

function TMyEncoder.Decode(Encrypted: TMemoryStream): String;
var
  Crypto: TElRSAPublicKeyCrypto;
  StreamOutput: TMemoryStream;
  KeyMaterial: TElRSAKeyMaterial;
begin
  KeyMaterial := TElRSAKeyMaterial.Create;
  KeyMaterial.Assign(FCert.KeyMaterial);
  Crypto := TElRSAPublicKeyCrypto.Create();
  Crypto.KeyMaterial := KeyMaterial;
  Crypto.InputEncoding := pkeBase64;
  Crypto.OutputEncoding := pkeBinary;

  StreamOutput := TMemoryStream.Create;
  Crypto.Decrypt(Encrypted, StreamOutput);

  FreeAndNil(KeyMaterial);
  FreeAndNil(Crypto);

  Result := MemoryStreamToString(StreamOutput);
end;

function TMyEncoder.Encode(Plain: string): TMemoryStream;
var
  Crypto: TElRSAPublicKeyCrypto;
  StreamInput: TMemoryStream;
  KeyMaterial: TElRSAKeyMaterial;
begin
  KeyMaterial := TElRSAKeyMaterial.Create;
  KeyMaterial.Assign(FCert.KeyMaterial);
  Crypto := TElRSAPublicKeyCrypto.Create();
  Crypto.KeyMaterial := KeyMaterial;
  Crypto.InputEncoding := pkeBinary;
  Crypto.OutputEncoding := pkeBase64;
  StreamInput := StringToMemoryStream(Plain);
  Result := TMemoryStream.Create;
  Crypto.Encrypt(StreamInput, Result);
  FreeAndNil(KeyMaterial);
  FreeAndNil(Crypto);
end;



This code, however, doesn't seem to work. The result text in the memory stream looks very different from what I'm passing initially.

Any hints on what to change?
#27457
Posted: 11/27/2013 17:32:35
by Ken Ivanov (EldoS Corp.)

Hello Leonardo,

Nice idea, certificates is another method to encrypt data with. However, please note that the maximal length of the data you'll be able to encrypt is limited with the length of the certificate's public key (and is ~117 bytes for 1024 keys).

Quote
This code, however, doesn't seem to work. The result text in the memory stream looks very different from what I'm passing initially.

As your code looks correct at first glance, the problem is likely to be a conversion or stream to string mapping issue. RSA components verify the integrity of recovered data during decryption and throw an exception if the verification fails. Please re-check the exact bytes that you pass to the Encrypt() method and get from the Decrypt() method. The aspects that require the prioritized attention are positions of stream pointers and accuracy of string to bytes conversion.
#27479
Posted: 11/28/2013 09:43:05
by Leonardo Herrera (Standard support level)
Joined: 02/14/2011
Posts: 66

Thanks. I corrected my string to stream / stream to string functions and it works now.

Code
function TMyEncoder.Decode(Encrypted: TMemoryStream): String;
var
  Crypto: TElRSAPublicKeyCrypto;
  StreamOutput: TMemoryStream;
  KeyMaterial: TElRSAKeyMaterial;
begin
  Result := '';
  KeyMaterial := TElRSAKeyMaterial.Create;
  KeyMaterial.Assign(FCert.KeyMaterial);
  Crypto := TElRSAPublicKeyCrypto.Create();
  Crypto.KeyMaterial := KeyMaterial;

  StreamOutput := TMemoryStream.Create;
  Crypto.Decrypt(Encrypted, StreamOutput);

  FreeAndNil(KeyMaterial);
  FreeAndNil(Crypto);

  Result := StreamToString(StreamOutput);
end;

function TMyEncoder.Encode(Plain: string): TMemoryStream;
var
  Crypto: TElRSAPublicKeyCrypto;
  StreamInput: TStream;
  KeyMaterial: TElRSAKeyMaterial;
begin
  KeyMaterial := TElRSAKeyMaterial.Create;
  KeyMaterial.Assign(FCert.KeyMaterial);
  Crypto := TElRSAPublicKeyCrypto.Create();
  Crypto.KeyMaterial := KeyMaterial;

  StreamInput := StringToStream(Plain);
  Result := TMemoryStream.Create;
  Crypto.Encrypt(StreamInput, Result);
  FreeAndNil(KeyMaterial);
  FreeAndNil(Crypto);
end;
Also by EldoS: Callback File System
Create virtual file systems and disks, expose and manage remote data as if they were files on the local disk.

Reply

Statistics

Topic viewed 1779 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!