0

I found a simple unit for generating a RSA KeyPair and encrypting / decrypting. It works fine on small strings but on larger text I get error : Bad Length.

What could be the cause , here is the unit, the unit wcrypt2 you can download here

Tested on Delphi 2010

unit Crypt_RSA;

interface

uses
  Windows, Classes, SysUtils, WCrypt2;

function RSAGenerateKeys(var PrivateKey, PublicKey: String): Boolean;
function RSAEncrypt(Source, Key: String): String;
function RSADecrypt(Source, Key: String): String;

implementation

function RSAGenerateKeys(var PrivateKey, PublicKey: String): Boolean;
const
  RSA1024BIT_KEY = $04000000;

var
  RSA: HCRYPTPROV;
  HKeyPair: HCRYPTKEY;
  Pair: TStringStream;
  buflen: DWORD;

  function SetKey(BlobDef: Cardinal; var Key: String): Boolean;
  begin
    Result := Bool(CryptExportKey(HKeyPair, 0, BlobDef, 0, nil, @buflen));
    if Result then
    begin
      Pair.SetSize(buflen);
      Result := Bool(CryptExportKey(HKeyPair, 0, BlobDef, 0, PByte(Pair.Memory), @buflen));
    end;

    Key := Pair.ReadString(buflen);
    Pair.Seek(0, soBeginning);
  end;

begin
  Pair := TStringStream.Create;

  Result := Bool(CryptAcquireContext(@RSA, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT));
  if Result then
    Result := Bool(CryptGenKey(RSA, AT_KEYEXCHANGE, RSA1024BIT_KEY or CRYPT_EXPORTABLE, @HKeyPair));

  if Result then
    Result := SetKey(PRIVATEKEYBLOB, PrivateKey);
  if Result then
    Result := SetKey(PUBLICKEYBLOB, PublicKey);

  CryptDestroyKey(HKeyPair);
  CryptReleaseContext(RSA, 0);
  FreeAndNil(Pair);
end;

function RSAEncrypt(Source, Key: String): String;
var
  KeyPair: TStringStream;
  RSA: HCRYPTPROV;
  HPair: HCRYPTKEY;
  DDataSize, EDataSize: DWORD;

begin
  Result := '';
  KeyPair := TStringStream.Create(Key);
  if CryptAcquireContext(@RSA, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
  try
    if CryptImportKey(RSA, PByte(KeyPair.Memory), KeyPair.Size, 0, 0, @HPair) then
    try
      EDataSize := SizeOf(Source);
      if CryptEncrypt(HPair, 0, true, 0, nil, @EDataSize, 0) then
      begin
        Result := Source;
        SetLength(Result, EDataSize);
        DDataSize := Length(Source) * SizeOf(Char);
        if not(CryptEncrypt(HPair, 0, True, 0, PByte(PChar(Result)), @DDataSize, EDataSize)) then
          Result := '';
      end;
    finally
      CryptDestroyKey(HPair);
    end;
  finally
  CryptReleaseContext(RSA, 0);
  end;
  FreeAndNil(KeyPair);
end;

function RSADecrypt(Source, Key: String): String;
var
  KeyPair: TStringStream;
  RSA: HCRYPTPROV;
  HPair: HCRYPTKEY;
  EDataSize: DWORD;

begin
  KeyPair := TStringStream.Create(Key);
  Result := '';
  if CryptAcquireContext(@RSA, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
  try
    if CryptImportKey(RSA, PByte(KeyPair.Memory), KeyPair.Size, 0, 0, @HPair) then
    try
      Result := Source;
      EDataSize := Length(Result);
      if not Bool(CryptDecrypt(HPair, 0, True, 0, PByte(PChar(Result)), @EDataSize)) then
        EDataSize := 0;
      SetLength(Result, EDataSize div SizeOf(Char));
    finally
      CryptDestroyKey(HPair);
    end;
  finally
    CryptReleaseContext(RSA, 0);
  end;
  FreeAndNil(KeyPair);
end;

end.
opc0de
  • 11,557
  • 14
  • 94
  • 187

2 Answers2

0

C# in a Nutshell says that plaintext much longer than 1/2 the key size will cause an error. It's likely the same for most languages for security purposes. For longer messages, use RSA to pass a key for symmetrical encryption such as AES.

WDS
  • 966
  • 1
  • 9
  • 17
  • I do not want symetrical encryption what should I do – opc0de Feb 04 '13 at 18:39
  • 1
    You can increase your key size to 2048 bits. But this is still equal to 256 8-bit characters, and remember that something much larger than 1/2 that size will throw an error. So with a key size of 2048 bits, you can send messages about 128 characters long. And if you reuse the key for more than one message, you may compromise your security. My understanding of the common practice is to use asymmetrical to establish a connection and to share a symmetrical key, then handle the rest of the session with symmetrical encryption, which is faster and not burdened by such plain text size limits. – WDS Feb 04 '13 at 22:22
  • There is no need for a session just to use hybrid encryption. While the basics of the answer are correct there are several portions that are wrong, it would be a better answer with corrections. – zaph Dec 27 '17 at 12:57
0

Asymmetric encryption limits the data to less than the key size, there is padding such as OAEP which is 42 bytes.

If you must have asymmetric encryption, that is need a key pair, use hybrid encryption which is essentially what https does. See Hybrid cryptosystem.

For Hybrid encryption basically you create a random symmetric encryption key, encrypt the symmetric key with an asymmetric encryption, encrypt the data with the symmetric key and package the two encryptions together.

Why don't you want symmetric encryption? It is much faster and handles essentially any data size? AES is the encryption workhorse.

zaph
  • 111,848
  • 21
  • 189
  • 228